In a frenzy of Bluetooth-enabled coding from the local diner, I have implemented double polymorphic associations in the has_many_polymorphs plugin. This lets you create a multi-class directed graph in ActiveRecord with great ease.
show, don’t tell
For example, say we wanted to model glue. We want to be able to glue
Leaves to themselves, but also to
Paper. We will make a join model called
Adhesion for this:
class Adhesion < ActiveRecord::Base belongs_to :glue_receiver, :polymorphic => true belongs_to :glue_applier, :polymorphic => true acts_as_double_polymorphic_join :glue_receivers => [:popsicle_sticks, :leaves, :papers], :glue_appliers => [:popsicle_sticks, :leaves] end
This means that a
PopsicleStick will have a
glue_receivers polymorphic collection that can accept anything in the
:glue_receivers list, above. A
Paper, though, will not have a
.glue_receivers method. It will have a
.glue_appliers method, but it is not itself a
:glue_applier. This is tricky. Just remember that the lists in the
acts_as call are the types of children for that relationship, not the types of parents.
Also, using the individual typed collections (e.g.
PopsicleStick#leaves) will always get you the children that belong to the record, instead of the parents that own the record. In this case it will fetch the
Leaves from a
warning! class-reloading laser in use!
The API is a little different because there is no parent class that can act as a controller. Things are instead centralized in the join model.
This means that you have to reference the join class (either with
load(), by evaluating its bare class name, or another way) before the associations will be injected into the “real” classes. Rails’ selective reloading in the development environment can lead to trouble—a modified class will get reloaded and forget its relationship to the join. Keep this in mind.
crazy clown collections
Combining this with the other plugin features leads to controlled chaos. Self-referential directed double-polymorphic namespaced STI associations are (at least according to my tests) fully supported.
Please try it out and let me know if you have trouble. I really appreciate the filing of bugs.