Inheritance Question


#1

Hi,
My application has a table of People that has information about
them, and I want to have a subset of those people as Users - people
that can login to the application.

What is the best way to do this? Add columns to People that only
Users would use? I’ve read the threads on single table inheritance,
but it seemed a little bit wasteful: 10,000 people vs 50 users

For those more experienced, is it more of a pain to create a Users
table that has a foreign key to a Person_id, and then try to bring up
this data? There are instances where I would want to display
information about a User that has data contained in a Person.

Hope this makes sense,

  • Nic

#2

I’m dealing with the same issue. I don’t much like STI, though it may be
my lack of experience with using it. I wish Rails supported Class Table
Inheritance - create a table for each inheritance level and tie rows in
different tables together by giving them the same ‘id’ value. Maybe I’ll
go nuts someday and create a patch. I’m not very database savvy, so
there may be good reasons for avoiding CTI, like causing too many
queries or reads/writes.

In the mean time, I rolled my own CTI using standard Rails features. I
have a User superclass and several subclasses for types of users (eg
Admin). I have a UserMixin class that I include in each subclass that
provides easy access to the user fields as if they were part of that
class. For example

def name
return user.name
end

That keeps all my references to functionality in user.rb abstracted away
from the inheritance model and will make it easy to change the
implementation in the future if I decide to move back to STI or if Rails
suddenly gets support for CTI. I’m finding it very easy to deal with
things using this approach, and haven’t missed using STI at all.

Nic W. wrote:

Hi,
My application has a table of People that has information about
them, and I want to have a subset of those people as Users - people
that can login to the application.


#3

Nuby alert. I’m more steeped in OOP than I am yet in Ruby and Rails.

As with so many things, there are lots of ways of doing this. My
personal preference is to take advantage of the OO nature of Ruby and
just create subclass of People called User, then add whatever
attributes and methods the User class needs to differentiate from the
People class.

Presumably, all Users are People, so this is not, as you seem to
conclude, wasteful at all. On the contrary; adding attributes to the
People class (with potentially thousands of instantiations) to handle
the relatively smaller number of Users would be much more wasteful/
less efficient.

On Jan 5, 2006, at 9:36 AM, Nic W. wrote:

Hi,
My application has a table of People that has information about
them, and I want to have a subset of those people as Users - people
that can login to the application.

What is the best way to do this? Add columns to People that only

-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
-.-.-.-.-.-.-.-.-.-.-.-.-.-.-
Dan S.
Technology Visionary - Technology Assessment - Documentation
“Looking at technology from every angle”
http://www.eclecticity.com


#4

Thanks for all the responses, its a little clearer in my head now. I
think my problem is that I’m trying to think in OOP and DB models at
the same time, wondering how my subclass is actually going to created
in the DB and if it is ‘efficient’.

  • Nic.

On 1/5/06, Dan S. removed_email_address@domain.invalid wrote:

People class (with potentially thousands of instantiations) to handle


Rails mailing list
removed_email_address@domain.invalid
http://lists.rubyonrails.org/mailman/listinfo/rails

  • Nic

#5

Hi Nic,

Regular relational databases are not object-oriented, so yes,
thinking in terms of RDBMS structure and and object-oriented
language like Ruby at the same time can cause dizzyness, and
occasionally nausea. In the OO-world relationships are frequently
established with inheritance; in the RDBMS world they are established
with associations. There are some new-fangled object-oriented
database management systems coming on the market these days (OODBMS),
mostly for java, that make the mapping between objects in your app
and objects in the db straightforward. I still miss the fast, simple
object db in Frontier…

Like Dan said, there are a few ways of cutting the pie. One option
is to have separate tables for users vs. people. This would be a
good choice if there’s little overlap between the two tables. But
fetching from a join between users and people is an expensive process
(relative to fetching from a single table). So, if there’s a fair
bit of overlap in the attributes (name, age, hair_color, ss_number,
favorite_continental_philosopher, etc.) then it makes sense to
structure the db with single table inheritance.

How do you do that? Setting up single table inheritance in a db is
pretty common, the Type-of-Person < Person subclassing is perhaps the
most typical. You can probably get some info on this for your db of
choice pretty easily on-line. Typically, you just set up the db with
all the columns that you’ll need for all classes. This means there
will be many object “closer” to the parent class that won’t be used.
A person might only have a name and id, for example. But an angry
spear-fisherman native whose good with poison darts will use a lot
more attributes (mood, source_of_food, good_with_poison_darts, etc.).
So, you’ve got to make sure that the columns that aren’t used by
every class are nullable (i.e., don’t set the column to ‘not
null’). That’s the first thing to remember. The second thing
involves tracking in the non-object-oriented db table what class each
person-row falls under. So, just add a column called something like,
‘person_type’.

Even though you’ve only got one table in your db you want to set up
your class structure in rails so that you can make use of the
inheritance structure in your app. That just means you define your
model objects with the hierarchy that you want:

class Person < ActiveRecord::Base
end

class SpearFisherman < Person
end

class User < Person
end

class DODEmployee < Person
end

class AngrySpearFisherman <SpearFisherman
end

You can then create objects of any of the above classes and they’ll
have access to any of the attributes in the entire table. It’s up to
you, in your code, to make sure that the angry spear-fisherman
doesn’t get assigned a DOD clearance number … well, wasn’t there
that picture of Rumsfield… O.k., nevermind, you get the idea. You
can use that ‘person_type’ column to help keep track of what your
code should be doing to any otherwise unknown row it pulls from the
db.

A couple of last things: you can’t have two objects that have an
attribute with the same name but are supposed to be different, e.g,.
you can’t have a ‘fish’ column that represents the type of fish
speared for the fisherman (varchar) but the number (int) of fish
eaten at the DOD press dinner since, obviously, you can’t have one
column with different types. You can use that same column to
represent semantically different things (number of fish eaten by that
person vs. number of types of fish that person can identify) but
you’ll eventually confuse yourself if you do this.

Lastly, don’t name that ‘person_type’ column something like ‘type’
(or if you do, change the mapping from the Ruby class to the column
so that it’s something else) because that’s the name of a Ruby method.

Sheesh, I really rambled. Anyway, the Agile book covers this same
material pretty well in ch. 15, I think.

Best,

Russ


#6

On 1/5/06, Nic W. removed_email_address@domain.invalid wrote:

Hi,
My application has a table of People that has information about
them, and I want to have a subset of those people as Users - people
that can login to the application.

What is the best way to do this? Add columns to People that only
Users would use? I’ve read the threads on single table inheritance,
but it seemed a little bit wasteful: 10,000 people vs 50 users

I would just make an association between User and Person.

For those more experienced, is it more of a pain to create a Users
table that has a foreign key to a Person_id, and then try to bring up
this data? There are instances where I would want to display
information about a User that has data contained in a Person.

If you have users with person_id, the code to get the person
associated with the User stored in @my_user is:

@my_user.person.

So to answer your question, no.

-Steven