This project is archived and is in readonly mode.

#6306 new
simple10

Collection Associations Build Method Not Supported for STI

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

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/

Comments and changes to this ticket

  • x37v

    x37v March 1st, 2011 @ 07:05 PM

    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..

  • x37v

    x37v March 1st, 2011 @ 07:07 PM

    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

  • Marc-André Lafortune

    Marc-André Lafortune May 9th, 2011 @ 09:11 PM

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

  • Coutud

    Coutud May 21st, 2011 @ 10:26 PM

    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

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

<h2 style="font-size: 14px">Tickets have moved to Github</h2>

The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>

Pages