I am very much a rails newbie, so this could be user error, but...
I am trying to implement Single Table Inheritance, and what I find is
that for the base class, and only for that class, the "type" field is
never written out into the database.
To check this out, I created a new project, and created three classes
with scaffold (Rails 2.0.2): Manager < Employee < Person, following
the example in Agile Web Development with Rails. I then edited the
files to set up Single Table Inheritance, which I think I did
correctly. In any event, the controllers appear to work correctly and
it passes the scaffold-generated tests (after necessary fixes).
But I find that when I save an Employee or Manager, the "type" field
is written correctly, but when I save a Person, the type field is
empty.
Is this normal, or is this a bug? I can supply a sample project if
that helps.
TIA
PS--if this is a bug, where would be the best place to fix it?
Overriding Person.new, or adding a call-back?
I am very much a rails newbie, so this could be user error, but...
I am trying to implement Single Table Inheritance, and what I find is
that for the base class, and only for that class, the "type" field is
never written out into the database.
To check this out, I created a new project, and created three classes
with scaffold (Rails 2.0.2): Manager < Employee < Person, following
the example in Agile Web Development with Rails. I then edited the
files to set up Single Table Inheritance, which I think I did
correctly. In any event, the controllers appear to work correctly and
it passes the scaffold-generated tests (after necessary fixes).
But I find that when I save an Employee or Manager, the "type" field
is written correctly, but when I save a Person, the type field is
empty.
Is this normal, or is this a bug? I can supply a sample project if
that helps.
That's as expected -- types aren't written for classes that are
direct subclasses of ActiveRecord::Base, because it isn't needed
for disambiguation. Why would you like the Person type written?
That's as expected -- types aren't written for classes that are
direct subclasses of ActiveRecord::Base, because it isn't needed
for disambiguation.
Thanks for the response.
There are a couple of reasons one would want it. Often, eg the code at
http://wiki.rubyonrails.org/rails/pages/SingleTableInheritance, one
needs to case on the [:type] column. It makes much more sense to case
on the known types, and then report an error if no match (like a
subclass was added). It seems unintuitive and unsafe to have ".nil?
.empty?" be the test used for the base class.
Second, and again, it might be user error, I set up a test case using
Rails 2.0.2, and scaffolding, and then edited to introduced STI. What
I found was that the call People.find(:all) returned an array of
objects, of which the ".type" field (which I believe is the real type
internal to Ruby?) was NOT set for elements of the array which should
have been type "Person", EXCEPT THE FIRST ONE. I.e., in the array
returned, all Employees and Managers had .type set correctly, and the
first Person also, but other People had .type either nil or empty
(didn't check which). Setting [:type] to Person before writing solved
this problem.
I don't know enough Ruby to muck around in the internals to figure out
why Rails might be acting this way.
I don’t know enough Ruby to muck around in the internals to figure out
why Rails might be acting this way.
Alan
Wasn’t this already answered?
That’s as expected – types aren’t written for classes that are
direct subclasses of ActiveRecord::Base, because it isn’t needed
for disambiguation. Why would you like the Person type written?
Perhaps make a subclass of the original Person class, or change the main class to something called “Party” and have another subclass called “Person”. Rails will not put the type into the table if you’re creating an object of the base model.
Why not just switch on the class? This would be the "Ruby way," I
think, since Class's === operator is designed for it.
Stop me if you've heard this before, but it's best to avoid doing this
kind of think (switching on Class) because every time you add a new
kind of Person, you have to remember to search your code for all the
other places that care about all Person descendents. If possible, it's
to let classes take care of themselves.
Why not just switch on the class? This would be the "Ruby way," I
think, since Class's === operator is designed for it.
That would be fine, of course. Thx.
On a related subject, can anyone explain to me the difference between
the .type and the .class methods? My Ruby book says that they are
synonyms, but I am seeing different values for them in rails. For some
People, I get .type == NIL, and .class == Person. A quick perusal of
the documentation doesn't show me that .type was overridden, but I
suspect it is somewhere.
Stop me if you've heard this before, but it's best to avoid doing this
kind of thing (switching on Class)...
I have heard that, and I do know it. I was quoting somebody else's
code, and s/he thought it was necessary. I am not smart enough to
really grok how object oriented design and rails really work
together,and whether you can avoid bad code like casing on the class,
but I'm working on it.
Much of my confusion came from not understanding what the .type method
does.
It turns out, if you care, that if you call Person.find(:all),
the .type method of the elements of the returned array can return the
following values
* Person (the Class) if the first object in the array is a Person
* NIL if the object is a Person and not the first Person in the array
* "Employee", "Manager", ... (strings) if the object is a subclass of
Person
I will just avoid using .type from now on, and use .class and [:type]
only.