views:

3193

answers:

4

I am using single table inheritance in my rails application, and want to explicitly set the type of an instance.

I have

class Event < ActiveRecord::Base
class SpecialEvent < Event

Implemented through single table inheritance.

SpecialEvent.new works as expected, but I want to be able to do things like

Event.new(:type => 'SpecialEvent')

So I can create different sub_types easily in the application.

However this doesn't work and seems to set :type to nil, not the value I set it to; I suspect this is because by calling Event.new it is overwriting the :type argument.

Has anyone got a good way of doing this?

+12  A: 

If you're trying to dynamically instantiate a subtype, and you have the type as a string, you can do this:

'SpecialEvent'.constantize.new()
Mr. Matt
+3  A: 

No, I want to create instances of sub-types, where I want to programmatically determine which sub_type they are – HermanD

You could use a factory pattern, although I have heard recently that people frown on the overuse of this pattern. Basically, use the factory to create the actual types you want to get

class EventFactory
  def EventFactory.create_event(event_type)
    event_type.constantize.new()
  end
end
Bill
A: 

Apparently, Rails does not allow you to set Type directly. Here's what I do...

klass_name = 'Foo'
...
klass = Class.const_get(klass_name)
klass.new # Foo.new

I believe .constantize is a Rails inflector shortcut. const_get is a Ruby method on Class and Module.

kt103099
This won't work if you're using namespaces for your classes, ie. Class.const_get('Foo::Bar') will result in an error. 'Foo::Bar'.constantize will work though.
Lars Haugseth
+5  A: 

from "Pragmatic - Agile Web Development with rails 3rd edition", page 380

"

There’s also a less obvious constraint (with STI). The attribute type is also the name of a built-in Ruby method, so accessing it directly to set or change the type of a row may result in strange Ruby messages. Instead, access it implicitly by creating objects of the appropriate class, or access it via the model object’s indexing interface, using something such as this:

person[:type] = 'Manager'

"

man, this book really rocks

cisco