Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

Collection Associations Build Method Not Supported for STI

#6306

The belongs_to association build method does not currently allow for specifying a :type subclass. This creates a lot of confusion and annoyances around STI support.

# Currently working...
u = User.first
b = u.badges << Badges::Superhero.new
# b.class == Badges::Superhero

# Not working...
u = User.first
u.badges.build(:type => Badges::Superhero)
# b.class == Badge

The quick fix is to change the build_association method in ActiveRecord::Reflection::AssociationReflection to detect if :type is a Class object and create the appropriate subclass model.

class ActiveRecord::Reflection::AssociationReflection
  def build_association(*opts)
    col = klass.inheritance_column.to_sym
    if (h = opts.first).is_a? Hash and (type = h.symbolize_keys[col]) and type.class == Class
      opts.first[col].to_s.constantize.new(*opts)
    elsif klass.abstract_class?
      raise "#{klass.to_s} is an abstract class and can not be directly instantiated"
    else
      klass.new(*opts)
    end
  end
end

Full writeup here: http://www.simple10.com/rails-3-sti/

Reported by simple10 · January 18th, 2011 @ 07:16 AM

State: new
Milestone: none
Assigned to: nobody
Importance: none

Activity

  1. x37v
    x37v

    This works nicely for parent.collection.build(:type => ClassName) but it breaks parent.collection.create(:type => ClassName)
    the parent_id isn't filled in in the created object...

    I'm not sure why because the original source for build_association is very simple and there is also a create_association which i assume would be called for the collection.create approach..

    March 1st, 2011 @ 07:05 PM

  2. x37v
    x37v

    well, besides the fact that the type field is filled in with a class instead of a string.. i figure it should do h.symbolize_keys[col] = type.to_s

    March 1st, 2011 @ 07:07 PM

  3. Marc-André Lafortune
    Marc-André Lafortune

    Got bitten by this. A solution for this problem would be nice.

    May 9th, 2011 @ 09:11 PM

  4. Coutud
    Coutud

    Encoutered the same issue here, and the given patch seems to be working. I'd remove the && type.class == Class, and would use ActiveRecord::Base.compute_type(options.first[col]) instead of .constantize

    May 21st, 2011 @ 10:26 PM