DRY in Models

I am building an app right now that needs to grant access to three
levels of members right now - each will have their own table in the DB.
When creating the add_user action I am converting the password into a
hashed password through the model.

The way I am doing this right now, I will inevitably end up with
repeated code in three different models. Is there a way I can define
this code in one single place and simply reference it from the models?

Sorry if this is a real newbie question - this is my first solo Ruby
app…

Mark ~

You can do this with One Table in your database using Single Table
Inheritance (STI). You will have 4 models in the end, the base member
class, and then the 3 types of members which inherit from the member
class.
You can do this in Rails by adding a field named “type” to your member
table. I believe the Wiki has more information on STI.

~ Ben

Ben R. wrote:

Mark ~

You can do this with One Table in your database using Single Table
Inheritance (STI). You will have 4 models in the end, the base member
class, and then the 3 types of members which inherit from the member
class.
You can do this in Rails by adding a field named “type” to your member
table. I believe the Wiki has more information on STI.

~ Ben

Isn’t there a way to do it using three models and three tables? I’d
like to keep the data separate as I am holding different information
about each - I’d rather not have one table that asks for the address of
the admin (eventually me) - kind of pointless…

I’ll look into STI - that seems close to what I am looking for. I guess
ultimately what I am looking for is the equivalent of application.rb,
but for models rather than a controller…

Ben R. wrote:

Mark ~

You can do this with One Table in your database using Single Table
Inheritance (STI). You will have 4 models in the end, the base member
class, and then the 3 types of members which inherit from the member
class. You can do this in Rails by adding a field named “type” to your
member table. I believe the Wiki has more information on STI.

Really? Sounded more like a modules question to me…

Mark, try this (with your own class names, of course):

module MyUser
def hash_password
do_stuff_here
end
end
(you can put that in a file in lib/, and require it in environment.rb)

class User < ActiveRecord::Base
include MyUser

User-specific methods go here

end
(this should be app/user.rb)

class Admin < ActiveRecord::Base
include MyUser

Admin-specific methods go here

end
(this should be app/admin.rb)

class Member < ActiveRecord::Base
include MyUser

Member-specific methods go here

end
(this should be app/member.rb)

That way there’s no restriction on the differences in table schema
between the user classes.


Alex

Alex Y. wrote:

Ben R. wrote:

Mark ~

You can do this with One Table in your database using Single Table
Inheritance (STI). You will have 4 models in the end, the base member
class, and then the 3 types of members which inherit from the member
class. You can do this in Rails by adding a field named “type” to your
member table. I believe the Wiki has more information on STI.

Really? Sounded more like a modules question to me…

Mark, try this (with your own class names, of course):

module MyUser
def hash_password
do_stuff_here
end
end
(you can put that in a file in lib/, and require it in environment.rb)

class User < ActiveRecord::Base
include MyUser

User-specific methods go here

end
(this should be app/user.rb)

class Admin < ActiveRecord::Base
include MyUser

Admin-specific methods go here

end
(this should be app/admin.rb)

class Member < ActiveRecord::Base
include MyUser

Member-specific methods go here

end
(this should be app/member.rb)

That way there’s no restriction on the differences in table schema
between the user classes.


Alex

Alex,

I think that was exactly what I was looking for! I’ll give that a try.
I’m working with the first table right now (trying to get everything
working correctly), but will let you know if this worked.

Thank you everyone for your responses so far.

Take a look at Single Table Inheritance especially if your members
are very similar … STI has some limitations so you should check to
make sure that it won’t adversely affect you.

See

http://www.ruby-forum.com/topic/51386
http://wiki.rubyonrails.com/rails/pages/SingleTableInheritance

If you’re feeling adventurous, you may want to check out Class Table
Inheritance …

http://johnwilger.com/articles/2005/09/29/class-table-inheritance-in-

rails-with-postgresql

but it seems a little too much trouble for me right now. I like
things to be as simple as possible.

– G.

Hi Mark ~

Yep it is a model question, but in the end the models will relate to
some
physical database table. I guess it depends on how different the 3
membership tables are to decide if they need their own tables. I would
say
the DRYest (spelling?) way to do this would be with one table, STI, and
then
have support tables for different member types.

Just my two cents, your method works as well and does focus on the
models.

~ Ben

Mark D. wrote:

The way I am doing this right now, I will inevitably end up with
repeated code in three different models. Is there a way I can define
this code in one single place and simply reference it from the models?

Sounds like that code should be in a helper…

I have something similar setup, and since the data the users contain is
so
unique, I opted for three tables.

Also, I have a login page for each of these users, that need to be
accessed
by different URL’s.
I end up having to use similar code for each of the login pages, views,
controllers…

I know this is a prime candidite for refactoring to adhere to the DRY
principles… however, I’m more of a IRM kind of guy now.
( I Repeat Myself).

Perhaps you want something like this?

In my STI, I have entities, all of which have an address as well as a
contact person.

class Entity < ActiveRecord::Base
composed_of :address,
:class_name => Address,
:mapping =>
[ # database # ruby
[ :primary_address, :primary ],
[ :secondary_address, :secondary ],
[ :city, :city ],
[ :region, :region ],
[ :postal_code, :postcode ],
]
composed_of :contact,
:class_name => Contact,
:mapping =>
[ # database # ruby
[ :phone_number, :phone ],
[ :mobile_number, :mobile ],
[ :fax_number, :fax ],
[ :email_address, :email ]
]
end

Then I use aggregations to ‘wrap’ some of the columns into something
that makes more sense within the application domain …

class Address
attr_reader :primary, :secondary, :city, :region, :postcode

def initialize(primary, secondary, city, region, postcode)
@primary = primary
@secondary = secondary
@city = city
@region = region
@postcode = postcode
end

def to_s
“#{@primary}, @ #{@city}”
end
end

class Contact
attr_reader :phone, :mobile, :fax, :email

def initialize(phone, mobile, fax, email)
@phone = phone
@mobile = mobile
@fax = fax
@email = email
end

def to_s
“M: #{@mobile} T: #{@phone} F: #{@fax} E: #{@email}”
end
end

And the human class, gets everything defined in Entity for free …

class Human < Entity
composed_of :name,
:class_name => Name,
:mapping =>
[ # database # ruby
[ :first_name, :first ],
[ :middle_name, :middle ],
[ :last_name, :last ],
[ :description, :descr ]
]
end

As does each Organization

class Organization < Entity
def location
“#{self.city}, #{self.region}”
end
end

Hope this helps …

– G.

Thank you for your response…definitely some food for thought.
The idea of extending dis-similar attributes of a user into a new table
is interesting - I’ve never thought of doing it this way before.

Not a problem. Glad I could provide you with some brain food! :slight_smile:

I’m not sure how much I would like that, though. I tend to be a bit of a
simpleton, and having well-defined tables that say what they are and are
self-contained just seems a bit easier to me.

There is definitely something to be said for keeping something simple
when simple will do. My experience is mostly with internal, large
scale, multi-user applications where I am collecting lots of similar
things for my users (like > 30 pieces of data) and then also have
other belongs_to attributes for the multitudes of other data I need to
keep track of (multiple addresses, multiple phone numbers, multiple
email addresses, etc). Personally I’ve found that managing small
packaged tables is easier on my brain (I really hate to hoziontally
scroll my tables ;-).

Unfortunately, with the three types of users, the only
thing that they share is an id, username, and password.
Beyond that, I am collecting different information…

If this is the case, then using the library is probably a much easier
route to take. You’re absolutely making the right decision.


DeLynn B.
[email protected]

Mark,

I’ll go ahead and throw in my two cents on this discussion as well.
:wink: I feel that the two questions that needs to be answered when
deciding whether to use STI or a library when DRYing up your models
are:

  1. How similar are the models that are going to use the STI
    functionality?
  2. How reusable is the functionality that is going into a library in
    terms of your entire application?

So, to relate this back to your current situation, I think you have to
decide if your “members” are similar enough objects to warrant STI,
and you also have to decide if the encryption method that is similar
to your “members” might also be used elsewhere in your application.

Isn’t there a way to do it using three models and three tables? I’d
like to keep the data separate as I am holding different information
about each - I’d rather not have one table that asks for the address of
the admin (eventually me) - kind of pointless…

I would first ask: how different is this information from member to
member? Are we talking lots of similar attributes (like username,
password, email, etc) and then a handful of dis-similar attributes
(like address for some not for others, salary for some and not for
others, etc.)? Or do you have a situation where the different members
might only share a handful of similar items and then have a multitude
of dis-similar ones?

I have little mantra that I try to remind myself with whenever I’m
dealing with users in an application and that is:“a User is a User is
a User”. In my experience I have found that when dealing with users,
you usually have a common set of data that you are collecting for each
user regardless of their type or role in the application. Because of
this I usually create a common users table and then use STI to create
the different “types” of user. Then, for the types that require
dis-similar attributes I create tables to store that information and
then use a belongs_to relationship. This ensures that I don’t add
unnecessary attributes to tables that might not get used with all my
users while giving me the flexibility to “extend” my user whenever I
need to collect additional information about a different type of user
by simply creating a new table.

One thing you have to keep in mind when creating multiple tables for
similar objects is that every time you have to add an attribute to
your many objects you’ll have to add it in multiple tables instead of
only having to add it to one table.

All that said, maybe you might need the ability to “encrpt” attributes
of other objects in a similar fashion somewhere else in your
application. If this is the case, then adding that method to a library
and then including it in whatever models might need it is definitely
the way to go.

You could even do a combination of the two and do STI for the
members/users stuff and still pull the encryption method out into a
library and then include it into your base member/user object and
whatever other objects might need that functionality in your
application.

Sorry for the long winded post, but hopefully this adds a bit of
perspective to the two approaches listed.


DeLynn B.
[email protected]

DeLynn,

Thank you for your response…definitely some food for thought. The
code that I want shared across the models is nothing more than a
password encrypting code. I think using a library for this might be the
way to go ultimately.

The idea of extending dis-similar attributes of a user into a new table
is interesting - I’ve never thought of doing it this way before. I’m
not sure how much I would like that, though. I tend to be a bit of a
simpleton, and having well-defined tables that say what they are and are
self-contained just seems a bit easier to me.

I definitely agree, however, with the idea that if the users are
similar, a single table would be the best way to go. Unfortunately,
with the three types of users, the only thing that they share is an id,
username, and password. Beyond that, I am collecting different
information…

Mark D.