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:

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:

| origin_id | destination_id | destination_type |

Double sided polymorphic:

| origin_id | origin_type | destination_id | destination_type |

This way there’s only on table to track all these relations. It allows
every model to be associated with every other model as many times as
needed.
For a better explanation I wrote something on Stackoverflow → Here is
the link again:
http://stackoverflow.com/questions/2224994/why-are-double-sided-polymorphic-relationships-lacking-in-rails&usg=AFQjCNEK8nv15YTOV3IYYh-2od_6Ng4Eug

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

On Tue, Feb 9, 2010 at 05:40, tsenart [email protected] wrote:

habtm_polymorphs [:articles]
rev = Relationship.new :origin => self.destination, :destination
def self.habtm_polymorphs(associations, options={})
has_many pjoin, :as => pfrom
})
args = [args].flatten

@asset.articles = Article.first

This way there’s only on table to track all these relations. It allows

impatient, here’s a backport:

To post to this group, send email to [email protected].
To unsubscribe from this group, send email to [email protected].
For more options, visit this group athttp://groups.google.com/group/rubyonrails-talk?hl=en.


You received this message because you are subscribed to the Google G. “Ruby on Rails: Talk” group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to [email protected].
For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.

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 B. wrote:

On Tue, Feb 9, 2010 at 05:40, tsenart [email protected] wrote:

�habtm_polymorphs [:articles]
� �rev = Relationship.new :origin => self.destination, :destination
�def self.habtm_polymorphs(associations, options={})
� �has_many pjoin, :as => pfrom
� � �})
� � � �args = [args].flatten

@asset.articles = Article.first

This way there’s only on table to track all these relations. It allows

impatient, here’s a backport:

To post to this group, send email to [email protected].
To unsubscribe from this group, send email to [email protected].
For more options, visit this group athttp://groups.google.com/group/rubyonrails-talk?hl=en.


You received this message because you are subscribed to the Google G. “Ruby on Rails: Talk” group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to [email protected].
For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.

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.

Would has_many_polymorphs help?

Best,
–Â
Marnen Laibow-Koser
http://www.marnen.org
[email protected]