Struggling a little with getting the syntax right for a STI model. Prob
just being a bit dense.
Couple of questions I’m hoping you guys can answer:
Assume here I’ve got:
class Person
class Manager < Person
class Slave < Person
How does person[:type] differ from person.type when type is the
column used to specify the subclass. They seem to output the same
(“Manager”) but the first works with an if statement (if person[:type] =
“Manager”), the second doesn’t.
How do I force the creation method that follows a form submission to
put something into a particular subclass. At the moment I have one form
that can handle all the subtypes and there’s a hidden field <%=
hidden_field(‘person’, ‘type’, “value” => @person[:type]) %> that varies
depending on how it’s called, e.g. @person = Manager.new or @person =
Slave.new. However, although that seems to pass the correct variables
along, the type is left as null.
There’s not much on the wiki (or anywhere else) on STI, so any help
would be greatly appreciated, and I’ll update the wiki.
you definitely want to use person[:type] instead of person.type as
‘type’
is a built-in ruby method (although deprecated) and you could have some
unexpected behavior if you try to use person.type.
to make sure I understand correctly you have something like:
simplified
def new
case @params[:person_type]
when “Manager” @person = Manager.new
when “Slave” @person = Slave.new
end
end
#again, simplified
def create
case @params[:person_type]
when “Manager” @person = Manager.new(@params[:person])
when “Slave” @person = Slave.new(@params[:person])
end
if @person.save
redirect_to :action => :list
else
render_action :new
end
end
you could even go so far as to create a factory class method in your
Person
class
class Person < ActiveRecord::Base
def self.factory(type, params = nil)
case type
when “Manager”
return Manager.new(params)
when “Slave”
return Slave.new(params)
else
return nil
end
end
end
then in your new and create methods, you can do
def new @person = Person.factory(@params[:person_type])
end
def create @person = Person.factory(@params[:person_type], @params[:person])
if @person.save
…
end
end
How does person[:type] differ from person.type when type is the
column used to specify the subclass. They seem to output the same
(“Manager”) but the first works with an if statement (if person[:type] =
“Manager”), the second doesn’t.
From my understanding
person.type returns the string object with the property name ‘type’
person[:type] points to the string value of the property with the key
‘type’ similar to if it were a hash table
So comparing an object to a string value returns false.
Ruby casts the string object to a string when you use it in the context
of
If you want to create a ‘manager’ or ‘slave’ but don’t know which, you
can do something like this…
def create @person = Person.new @person[:type] = “Manager” # or get it from a param[:person][:type]
…
In other words, if you manually set the ‘type’ column to the name of the
class you want it to have, when you pull that record from the database,
AR will re-create it as a ‘Manager’ instead of a ‘Person’.
That (or something very similar) sorted it. Thanks. Still not 100%
understanding why type can’t be passed as a parameter with the rest of
the :person ones, but at least it’s working now.
Thanks for this. I ended up doing something very similar. Why does it
need to be set explicitly, though, i.e. separate from the other
parameters for person? [If you have the form return a hash something
like “person”=>{“name”=>“Fred”, “type”=>“Manager”, “age”=>“28”} I’m
missing something here, as it doesn’t seem to work.]