I understand why ActiveRecord can’t support has_many :through on polymorphic classes. But I would like to emulate some of its functionality. Consider the following, where a join table associates two polymorphic classes:
class HostPest < ActiveRecord::Base
belongs_to :host, :polymorphic => true
belongs_to :pest, :polymorphic => true
end
class Host < ActiveRecord::Base
self.abstract_class = true
has_many :host_pests, :as => :host
end
class Pest < ActiveRecord::Base
self.abstract_class = true
has_one :host_pest, :as => :pest
end
class Dog < Host ; end
class Cat < Host ; end
class Flea < Pest ; end
class Tick < Pest ; end
The goal
Since I can’t do has_many :pests, :through=>:host_pests, :as=>:host (etc), I’d like to emulate these four methods:
dog.pests (returns a list of pests associated with this dog)
flea.host (return the host associated with this flea)
cat.pests << Tick.create (creates a HostPest record)
tick.host = Cat.create (creates a HostPest record)
Question 1
I’ve got a working implementation for the first two methods (pests and host), but want to know if this is the best way (specifically, am I overlooking something in ActiveRecord associations that would help):
class Host < ActiveRecord::Base
def pests
HostPest.where(:host_id => self.id, :host_type => self.class).map {|hp| hp.pest}
end
end
class Pest < ActiveRecord::Base
def host
HostPest.where(:pest_id => self.id, :pest_type => self.class).first.host
end
end
Question 2
I’m stumped on how to implement the << and = methods implied here:
cat.pests << Tick.create # => HostPest(:host=>cat, :pest=>tick).create
tick.host = Cat.create # => HostPest(:host=>cat, :pest=>tick).create
Any suggestions? (And again, can ActiveRecord associations provide any help?)
Implementing the
host=method on thePestclass is straight forward. We need to make sure we clear the old host while setting a new host (as AR doesn’t clear the old value from the intermediary table.).Implementing
pests<<method onHostclass is bit more involved. Add thepestsmethod on theHostclass to return the aggregated list of pests. Add the<<method on the object returned bypestsmethod.Now