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: