Why are double sided polymorphic relationships lacking in Rails?

http://stackoverflow.com/questions/2224994/why-are-double-sided-polymorphic-relationships-lacking-in-rails

Inverse associations will be standard in Rails 2.3.6. For the impatient, here's a backport:

http://github.com/oggy/inverse_of

Mat

Very cool! Thanks for the answer. But inverse associations are only a component of those double sided polymorphic associations. At the present you can only make one sided associations. Table talking it means the following:

Single sided polymorphic:

ActiveRecord supports one-to-many polymorphic associations but not many-to-many polymorphic associations. Thats what I wanted. I emulate that behavior like this: We have three models: - Article, Asset and Relationship

class Article < PolyRecord   habtm_polymorphs [:assets] end

class Asset < PolyRecord   habtm_polymorphs [:articles] end

class Relationship < ActiveRecord::Base   belongs_to :origin, :polymorphic => true   belongs_to :destination, :polymorphic => true

  after_create :create_reverse_relationship

private   def create_reverse_relationship     rev = Relationship.new :origin => self.destination, :destination => self.origin     rev.save(false)     true   end

end

class PolyRecord < ActiveRecord::Base   self.abstract_class = true

  def self.habtm_polymorphs(associations, options={})     associations = [associations].flatten     options[:polymorphic_join_table] ||= 'relationships'     options[:polymorphic_from] ||= 'origin'     options[:polymorphic_to] ||= 'destination'

    pjoin = options[:polymorphic_join_table]     pto = options[:polymorphic_to]     pfrom = options[:polymorphic_from]

    has_many pjoin, :as => pto     has_many pjoin, :as => pfrom

    associations.each do |assoc|       has_many assoc, :through => pjoin, :source => pto, :source_type => assoc.to_s.singularize.camelize     end

    after_destroy do |obj|       Relationship.delete_all({         :destination_id => obj.id,         :destination_type => obj.class.to_s       })

      Relationship.delete_all({         :origin_id => obj.id,         :origin_type => obj.class.to_s       })     end

    associations.each do |assoc|       define_method "#{assoc}=".to_sym do |args|         eval "self.#{assoc}.clear"         args = [args].flatten

        Relationship.delete_all({           :origin_type => assoc.to_s.singularize.capitalize,           :destination_id => self.id,           :destination_type => self.class.to_s })

        Relationship.destroy_all({           :origin_id => self.id,           :origin_type => self.class.to_s,           :destination_type => assoc.to_s.singularize.camelize })

        eval "self.#{assoc} << args" unless args.empty?       end     end

  end end

Then you can use it like this: @asset = Asset.new @asset.articles = Article.first @asset.articles = Article.all @asset.articles << Article.new @article.assets = @asset.articles.first.assets.last

Ah, I see what you're getting at.

When I've had to do that, I use full-blown model for the join table, and then a has_many :through to define the relationship between the endpoints. It's not a perfect solution but probably the best that's possible in AR.

Mat Brown wrote: