4 An ActiveRecord plugin for self-referential and double-sided polymorphic associations.
8 Copyright 2006-2008 Cloudburst, LLC. Licensed under the AFL 3. See the included LICENSE file.
10 The public certificate for the gem is here[http://rubyforge.org/frs/download.php/25331/evan_weaver-original-public_cert.pem].
12 If you use this software, please {make a donation}[http://blog.evanweaver.com/donate/], or {recommend Evan}[http://www.workingwithrails.com/person/7739-evan-weaver] at Working with Rails.
16 This plugin lets you define self-referential and double-sided polymorphic associations in your models. It is an extension of <tt>has_many :through</tt>.
18 “Polymorphic” means an association can freely point to any of several unrelated model classes, instead of being tied to one particular class.
23 * double-sided polymorphism
24 * efficient database usage
27 * automatic individual and reverse associations
29 The plugin also includes a generator for a tagging system, a common use case (see below).
33 * Rails 1.2.3 or greater
39 To install the Rails plugin, run:
40 script/plugin install svn://rubyforge.org/var/svn/fauna/has_many_polymorphs/trunk
42 There's also a gem version. To install it instead, run:
43 sudo gem install has_many_polymorphs
45 If you are using the gem, make sure to add <tt>require 'has_many_polymorphs'</tt> to <tt>environment.rb</tt>, before Rails::Initializer block.
49 Setup the parent model as so:
51 class Kennel < ActiveRecord::Base
52 has_many_polymorphs :guests, :from => [:dogs, :cats, :birds]
57 class GuestsKennel < ActiveRecord::Base
59 belongs_to :guest, :polymorphic => true
62 One of the child models:
64 class Dog < ActiveRecord::Base
68 For your parent and child models, you don't need any special fields in your migration. For the join model (GuestsKennel), use a migration like so:
70 class CreateGuestsKennels < ActiveRecord::Migration
72 create_table :guests_kennels do |t|
73 t.references :guest, :polymorphic => true
79 drop_table :guests_kennels
83 See ActiveRecord::Associations::PolymorphicClassMethods for more configuration options.
85 == Helper methods example
88 #<Kennel id: 1, name: "Happy Paws">
89 >> k.guests.map(&:class)
92 >> k.guests.push(Cat.create); k.cats.size
94 >> k.guests << Cat.create; k.cats.size
100 #<Dog id: 3, name: "Rover">
102 [#<Kennel id: 1, name: "Happy Paws">]
104 >> k.guests.delete(d); k.dogs.size
109 Note that the parent method is always plural, even if there is only one parent (<tt>Dog#kennels</tt>, not <tt>Dog#kennel</tt>).
111 See ActiveRecord::Associations::PolymorphicAssociation for more helper method details.
115 == Double-sided polymorphism
117 Double-sided relationships are defined on the join model:
119 class Devouring < ActiveRecord::Base
120 belongs_to :guest, :polymorphic => true
121 belongs_to :eaten, :polymorphic => true
123 acts_as_double_polymorphic_join(
124 :guests =>[:dogs, :cats],
125 :eatens => [:cats, :birds]
129 Now, dogs and cats can eat birds and cats. Birds can't eat anything (they aren't <tt>guests</tt>) and dogs can't be eaten by anything (since they aren't <tt>eatens</tt>). The keys stand for what the models are, not what they do.
131 In this case, each guest/eaten relationship is called a Devouring.
133 In your migration, you need to declare both sides as polymorphic:
135 class CreateDevourings < ActiveRecord::Migration
137 create_table :devourings do |t|
138 t.references :guest, :polymorphic => true
139 t.references :eaten, :polymorphic => true
144 drop_table :devourings
148 See ActiveRecord::Associations::PolymorphicClassMethods for more.
152 Has_many_polymorphs includes a tagging system generator. Run:
153 script/generate tagging Dog Cat [...MoreModels...]
155 This adds a migration and new Tag and Tagging models in <tt>app/models</tt>. It configures Tag with an appropriate <tt>has_many_polymorphs</tt> call against the models you list at the command line. It also adds the file <tt>lib/tagging_extensions.rb</tt> and <tt>requires</tt> it in <tt>environment.rb</tt>.
157 Tests will also be generated.
159 Once you've run the generator, you can tag records as follows:
161 >> d = Dog.create(:name => "Rover")
162 #<Dog id: 3, name: "Rover">
165 >> d.tag_with "fierce loud"
166 #<Dog id: 3, name: "Rover">
169 >> c = Cat.create(:name => "Chloe")
170 #<Cat id: 1, name: "Chloe">
171 >> c.tag_with "fierce cute"
172 #<Cat id: 1, name: "Chloe">
175 >> Tag.find_by_name("fierce").taggables
176 [#<Cat id: 1, name: "Chloe">, #<Dog id: 3, name: "Rover">]
178 The generator accepts the optional flag <tt>--skip-migration</tt> to skip generating a migration (for example, if you are converting from <tt>acts_as_taggable</tt>). It also accepts the flag <tt>--self-referential</tt> if you want to be able to tag tags.
180 See ActiveRecord::Base::TaggingExtensions, Tag, and Tagging for more.
184 Some debugging tools are available in <tt>lib/has_many_polymorphs/debugging_tools.rb</tt>.
186 If you are having trouble, think very carefully about how your model classes, key columns, and table names relate. You may have to explicitly specify options on your join model such as <tt>:class_name</tt>, <tt>:foreign_key</tt>, or <tt>:as</tt>. The included tests are a good place to look for examples.
188 Note that because of the way Rails reloads model classes, the plugin can sometimes bog down your development server. Set <tt>config.cache_classes = true</tt> in <tt>config/environments/development.rb</tt> to avoid this.
190 == Reporting problems
192 The support forum is here[http://rubyforge.org/forum/forum.php?forum_id=16450].
194 Patches and contributions are very welcome. Please note that contributors are required to assign copyright for their additions to Cloudburst, LLC.
198 * http://blog.evanweaver.com/articles/2007/08/15/polymorphs-tutorial
199 * http://blog.evanweaver.com/articles/2007/02/22/polymorphs-25-total-insanity-branch
200 * http://blog.evanweaver.com/articles/2007/02/09/how-to-find-the-most-popular-tags
201 * http://blog.evanweaver.com/articles/2007/01/13/growing-up-your-acts_as_taggable
202 * http://blog.evanweaver.com/articles/2006/12/02/polymorphs-19
203 * http://blog.evanweaver.com/articles/2006/11/05/directed-double-polymorphic-associations
204 * http://blog.evanweaver.com/articles/2006/11/04/namespaced-model-support-in-has_many_polymorphs
205 * http://blog.evanweaver.com/articles/2006/09/26/sti-support-in-has_many_polymorphs
206 * http://blog.evanweaver.com/articles/2006/09/11/make-polymorphic-children-belong-to-only-one-parent