Topic: Inheritance vs. Roles:

In my application I have what is a pretty common setup:
There are users, some of whom are providers and some who are consumers.
The providers have many more attributes and methods than consumers, but they are both users.
Providers can use some special features of the site (i.e. creating detailed profiles), while consumers just have a subset of features (basically browsing, and keeping a favorites list).

I could do it with inheritance,
User
Provider < User
Consumer < User

with a table having columns like:
  id
  login
  password
  provider_attr1
  provider_attr2
  consumer_attr1

Or I could use the same table, and use the Authorization plugin and assign roles to my User, relying on duck-typing for the methods/attribs.

I think the roles-based way might be easier, but I feel like I am probably missing some big issue that will bite me later on.

What do you guys think?

Last edited by mvanhorn (2006-12-06 16:57:25)

Re: Inheritance vs. Roles:

Did you ever find a good way to do this? It is a fairly common problem.

What I like about STI is it keeps things simple and you aren't dealing with complex relationships. The only downside is it shares the attributes. I would say a couple custom attributes are okay, but if the custom attribute count is greater than the shared attribute count, then STI doesn't seem like the best solution. The table can get big and bloated and with lots of nil values. Not very normalized.

I see two alternatives. We can split the custom attributes into their own tables/models. Let's call it ProviderProfile and ConsumerProfile. You can still even keep your STI, it is just that the custom attributes are separate:

class User < ActiveRecord::Base
end

class Provider < User
  has_one :provider_profile
end

class Consumer < User
  has_one :consumer_profile
end

class ProviderProfile < ActiveRecord::Base
  belongs_to :user
end

class ConsumerProfile < ActiveRecord::Base
  belongs_to :user
end


One problem with this is that you need to fetch the custom attributes through the separate profile relationship. However, you can set up getter methods in each subclass which basically forwards the request to the profile.

Another solution is completely split up the two subclasses into just two models. Here you are duplicating the id, login, and password columns in the two tables. But, you can move any shared behavior into a module. For example:

module User
  # shared login behaviour
end

class Provider < ActiveRecord::Base
  include User
end

class Consumer < ActiveRecord::Base
  include User
end


This works fairly well, but if you ever need to reference the user (say a User has many comments) you'll need to use Polymorphic Association:

class Comment < ActiveRecord::Base
  belongs_to :user, :polymorphic => true
end

class Provider < ActiveRecord::Base
  has_many :comments, :as => :user
end

class Consumer < ActiveRecord::Base
  has_many :comments, :as => :user
end


I think this is an acceptable solution. If you have a lot of User has_many relationships or a lot of shared attributes, you may not want to do this. I used this approach for dealing with different kinds of products and it worked fairly well.

I'm sure there are other solutions to this problem. I'd be interested in your approach.

Railscasts - Free Ruby on Rails Screencasts

Re: Inheritance vs. Roles:

ryanb wrote:

We can split the custom attributes into their own tables/models. Let's call it ProviderProfile and ConsumerProfile. You can still even keep your STI, it is just that the custom attributes are separate:

class User < ActiveRecord::Base
end

class Provider < User
  has_one :provider_profile
end

class Consumer < User
  has_one :consumer_profile
end

class ProviderProfile < ActiveRecord::Base
  belongs_to :user
end

class ConsumerProfile < ActiveRecord::Base
  belongs_to :user
end


One problem with this is that you need to fetch the custom attributes through the separate profile relationship. However, you can set up getter methods in each subclass which basically forwards the request to the profile.

What if we go directly to "ProviderProfile < user" - and also have an underlying table and controller for it? would that make sense?

Can you also ellaborate what you meant by "fetch the custom attributes through the separate profile relationship" above? code example would be much appreciated.

radical vision fuels disruptive technologies

Re: Inheritance vs. Roles:

dagger007 wrote:

What if we go directly to "ProviderProfile < user" - and also have an underlying table and controller for it? would that make sense?

This would be going back to a simple STI association, which still has the same problem of sharing one table.

dagger007 wrote:

Can you also ellaborate what you meant by "fetch the custom attributes through the separate profile relationship" above? code example would be much appreciated.

For example, let's say Consumer has a "salary" attribute. You don't want to put this in the users table because the attribute is unique to Consumer (Provider doesn't have it). In my post I mentioned you can create separate Profile models to hold the custom attributes which are unique to each User subclass. The problem is, if you place the "salary" attribute into this profile model:

@consumer.salary # this won't work because the attribute is in the profile
@consumer.consumer_profile.salary # you have to do this

One way to solve this is to create getter/setter methods in the subclass to fetch the attribute in the profile. This doesn't feel very efficient though. I think rails does have an undocumented "delegate" method in ActiveRecord, but I still haven't tested it out to see if it solves the problem.

Railscasts - Free Ruby on Rails Screencasts

Re: Inheritance vs. Roles:

Yeah, i also stumbled upon this http://wiki.rubyonrails.org/rails/pages … nceInRails - which i didn't really like - So i am gonna use this way (discussed above) of implementing CTI (sort of).

But lemme explain the issue
My app consists of posts belonging to a category - category has a type, which is then also assigned to the post. Now some of the types comprise of different attribute sets, and others can just live with the common attributes. I refer to these two sets of types as physical and virtual. Physical because the type has a model and tables of its own existing - on the other hand Virtual coz the type just lives on common attributes.

now i am confused in deciding how to implement it - should i make subclasses of post e.g. jobs, services, news and have them associated with another model having the extra attributes or i would be better off with

class Post < ActiveRecord::Base
has_one :job
has_one :service
end

I think i am not getting something right here... help please

thanking in anticipation

Last edited by dagger007 (2006-12-28 15:34:17)

radical vision fuels disruptive technologies

Re: Inheritance vs. Roles:

So, do categories have different types, or do posts have different types, or both? Which model has the custom attributes, Category or Post? Or both?

Perhaps what you want is polymorphic association, where a post can belong to different models.

Railscasts - Free Ruby on Rails Screencasts

Re: Inheritance vs. Roles:

Categories and posts both have types - the type of the post is determined by which category the post is actually being placed in - so the category is say: jobs then there would be 5 extra attributes (besides the common ones) that would be updated for post(intended to be extra table/model) but at the same time if the category is news - then the common set of attributes would be sufficient... so in a way (i wanted) determine the type of post by the type of category it is being placed in. (hope that makes sense)

I cannot use polymorphic association as different models belong to post and post belongs to a category & type...

Last edited by dagger007 (2006-12-29 09:49:13)

radical vision fuels disruptive technologies

Re: Inheritance vs. Roles:

The second approach I mentioned in my first post may work for you. It sounds like the different Posts vary quite a bit in their attributes and functionality. Then for everything that belongs_to :post you will need to use polymorphic association to reference the post since it is multiple models. If you need further explanation just let me know.

Neither of these approaches will work very well if the category of the post may change throughout its existence. Do you ever expect this to happen?

Railscasts - Free Ruby on Rails Screencasts

Re: Inheritance vs. Roles:

the posts could be shuffled around by the admin to clean the categories from the posts not belonging to it - but with the contraint of type in mind - so the type of the post cannot change through out its existance - but the category can - therefore post.category.type == target_category.type.

In the second approach above, 2 things are not clear to me, 1) the use of 'include' and its behaviour 2) why would i need 'has_many :comments, :as => user' when the "super" class user has_many comments already? (unless the 'include' has to say something about that) - or - am i just lost?

radical vision fuels disruptive technologies

Re: Inheritance vs. Roles:

dagger007 wrote:

1) the use of 'include' and its behaviour 2) why would i need 'has_many :comments, :as => user' when the "super" class user has_many comments already? (unless the 'include' has to say something about that) - or - am i just lost?

In the second example there is no User class with inheritance. Notice both Provider and Consumer are completely separate models inheriting from ActiveRecord::Base. Instead of User being a superclass, it is a module which the other two classes include. This way you can define methods in this module which will be included into both classes - no duplication. In the end it almost behaves like inheritance, but it is a little more flexible. There are also some things you can't do in the module that you could in a full class, such as setting the "has_many :comments". At least that's my understanding but I haven't done much testing.

Railscasts - Free Ruby on Rails Screencasts

Re: Inheritance vs. Roles:

thanks ryan - i am going to give it a shot... but still if you have any more information plz do share - coz i don't really feel comfortable with this approach... my main concerns are flexability and performance... thanks again

radical vision fuels disruptive technologies

Re: Inheritance vs. Roles:

Is there any way I can do a mutiple user roles with CTI? I'm planning to have one users table and users_roles. But I'm not sure if I should make only one User model or multiple sub-classes. Please advice.

Thank you,

Evgeniy

Re: Inheritance vs. Roles:

You mean for Single Table Inheritance? See this post for a way to do this.

Railscasts - Free Ruby on Rails Screencasts

Re: Inheritance vs. Roles:

Yes, I mean STI (sorry). I have read mentioned topic, but think it is a bit different.
I have global roles in the system, lets say script writer and video professional and I want to have the ability for users to have multiple roles. That's why I have the ACL and roles_users table. I also have users table with fields for all user types (most of them are common). So I can have 2 roles for a user in roles_users table and can use User to fill that data into users, but the question is if I can use multiple STI (for both script writer and video professional roles of one user) based on users table?

Re: Inheritance vs. Roles:

In this case STI will not work as it only allows one subclass/role per user. You will need to create a HABTM association between users and roles. It sounds like this is what you have done? Is this method not ideal? If not, please specify.

Railscasts - Free Ruby on Rails Screencasts

Re: Inheritance vs. Roles:

Yes, I'm doing this, but I wanted to have some ability to separate the User into User, ScriptWriter and VideoProfessional (maybe?). For having saparate validations or custom functions. Maybe I should use usual inheritance, but connected somehow to the roles, user has? And what model class I should use later, for working with particular user?
sorry for been a bit blurry - I just haven't desided how this should work...

Re: Inheritance vs. Roles:

Ruby doesn't support multiple inheritance, however, you can add STI to the Role model:

class User < ActiveRecord::Base
  has_and_belongs_to_many :roles
end

class Role < ActiveRecord::Base
  has_and_belongs_to_many :users
end

class ScriptWriter < Role
  # custom method
end

class VideoProfessional < Role
  # custom method
end


The user class will then need to delegate its permission handling over to the Role classes/subclasses.

Railscasts - Free Ruby on Rails Screencasts

Re: Inheritance vs. Roles:

What about some input data format validations, specific to ScriptWriter and VideoProfessional, that needs to be in the model?
Do I forsed to make them based on if (User.roles.ScriptWriter)?

Re: Inheritance vs. Roles:

but in this case one user can't be of those 2 types, right? if the formats differ? otherwise I can add them to User model smile

Last edited by evgeniy (2007-02-06 18:55:00)

Re: Inheritance vs. Roles:

Are you asking how to determine the roles of a given user? With my above recommendation you can do this:

# in User
def role?(role_class)
  roles.any? { |r| r.kind_of? role_class }
end

You can then ask if a given user is part of any kind of role.

user = User.find(:first) # fetch some user
if user.role? ScriptWriter
  # user is a script writer
else
  # user is not a script writer
end

Railscasts - Free Ruby on Rails Screencasts