I currently have a User model (which “acts_as_authentic” in
Authlogic). However, my app is going to have multiple user types
(“Teacher”, “Student”, and potentially “Teacher’s Assistant”). For the
moment, they’ll all be mutually exclusive; a user can’t be both a
Teacher’s Assistant and a Student, for example.
Currently, I’m planning on creating separate models for each of these
(Teacher, Student, TA). They’ll each “belongs_to” a User, and a user
will “has_one” of each. This way, I’ll be able to put any common
methods/fields in User, and any exclusive methods/fields in the model
it belongs in.
I’m very new to Ruby (and, by association, to Rails). Does this sound
like a reasonable way to do this? Does it follow “the Ruby/Rails way”?
Or is there a common pattern for doing this in Rails that I don’t know
about?
I currently have a User model (which “acts_as_authentic” in
Authlogic). However, my app is going to have multiple user types
(“Teacher”, “Student”, and potentially “Teacher’s Assistant”). For the
moment, they’ll all be mutually exclusive; a user can’t be both a
Teacher’s Assistant and a Student, for example.
Currently, I’m planning on creating separate models for each of these
(Teacher, Student, TA). They’ll each “belongs_to” a User, and a user
will “has_one” of each. This way, I’ll be able to put any common
methods/fields in User, and any exclusive methods/fields in the model
it belongs in.
I’m very new to Ruby (and, by association, to Rails). Does this sound
like a reasonable way to do this?
No. If you want several kinds of user models, make them inherit from
User.
However, you probably only want one User model with a Role field. Then
use something like rails_authorization or acl9 if you want role-based
security.
Does it follow “the Ruby/Rails way”?
No, to the extent that it doesn’t follow proper object-oriented design
owing to misuse of associations as I outlined above.
Or is there a common pattern for doing this in Rails that I don’t know
about?
See above. In general, if X is a kind of Y, then X should be a subclass
of Y.
Jake,
I couldn’t help but feel sorry for you in the path you’re taking.
Simply create a has_one and belongs_to relationship between a
controlling master record such as Student_Type(Teacher, Student, etc).
Now, when you call a record its @student_type.users(find ??).
That inheritence stuff is for the race car drivers and opens up many
potholes that could CRASH your effort.
Hope this makes your life a little easier.
David
I agree with Marnen. The easiest way would be to just add a role
field to the user model.
I wouldn’t mess with inherited classes or associations for what you
are describing.
But if you do, you really should use inheritance, as Marnen
described.
The only other table that I might consider would a “domain-table”
called Roles (or anything you want). It could start with simply
an :id field and a :name field. This could later be expanded to
include more data. Then in your user table you could define a field
for the role_id which could be set via the Role table. The advantage
of this is that you get a central place to define all possible roles
within the database rather that in just your rails application. This
would allow you to guarantee referential integrity in the database, so
that a user could not be made with an unintended role by bypassing
your rails app (at the mysql or psql prompts, for example).
Of course, the importance of an unintended role getting into the
database depends of your application setup. If you blacklist
everything and only open up certain areas of the app by white-listing
roles, then the consequence is less severe. Beware however, if you
whitelist everything and only blacklist based on the role. This type
of setup might allow unrestricted access to an unintended role. For
this reason, I tend to blacklist everything, and then whitelist based
on the role.
In despite of a design problem, or the misconception on inheritance
which Marnen already explained and you must change it if you haven’t
already, have you think about using a plugin for this? I’ve used
acts_as_permissible[1] and it’s really simple to use and configure, it
has resolved the problem you’re presenting here.
Jake,
I couldn’t help but feel sorry for you in the path you’re taking.
Simply create a has_one and belongs_to relationship between a
controlling master record such as Student_Type(Teacher, Student, etc).
This is what the OP was originally planning. It is a bad idea for the
reasons I outlined above.
Now, when you call a record its @student_type.users(find ??).
That inheritence stuff is for the race car drivers and opens up many
potholes that could CRASH your effort.
You are absolutely wrong. It’s the proper way to do it. It’s not for
“race car drivers”, merely for developers who actually care about
modeling their data correctly
Inheritance is not difficult to work with, and every OO developer should
understand it. Incorrect data modeling such as you are advocating,
however, will crash your effort.