how'd that get in there
[has_many_polymorphs.git] / README
blobf4c10c7f9d46a4976ddc5d39be69cc743934c451
2 Has_many_polymorphs
4 An ActiveRecord plugin for self-referential and double-sided polymorphic associations.
6 == License
8 Copyright 2007, 2008 Cloudburst, LLC. Licensed under the AFL 3. See the included LICENSE file. 
10 The public certificate for the gem is here[link:http://rubyforge.org/frs/download.php/25331/evan_weaver-original-public_cert.pem]. 
12 If you like this software, please {make a donation to charity}[link:http://blog.evanweaver.com/donate/] or {recommend Evan}[link:http://www.workingwithrails.com/person/7739-evan-weaver] at Working with Rails.
14 == Description
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.
20 == Features
22 * self-references
23 * double-sided polymorphism
24 * efficient database usage
25 * STI support
26 * namespace support
27 * automatic individual and reverse associations
29 The plugin also includes a generator for a tagging system, a common use case (see below).
31 == Requirements
33 * Rails 1.2.3 or greater
35 = Usage
37 == Installation
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
44   
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.
47 == Configuration
49 Setup the parent model as so:
51   class Kennel < ActiveRecord::Base
52     has_many_polymorphs :guests, :from => [:dogs, :cats, :birds]
53   end
55 The join model:
57   class GuestsKennel < ActiveRecord::Base
58     belongs_to :kennel
59     belongs_to :guest, :polymorphic => true
60   end
62 One of the child models:
64   class Dog < ActiveRecord::Base
65     # nothing
66   end
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
71     def self.up
72       create_table :guests_kennels do |t|
73         t.references :guest, :polymorphic => true
74         t.references :kennel
75       end
76     end
77   
78     def self.down
79       drop_table :guests_kennels
80     end
81   end
83 See ActiveRecord::Associations::PolymorphicClassMethods for more configuration options.
85 == Helper methods example
87   >> k = Kennel.find(1)
88   #<Kennel id: 1, name: "Happy Paws">
89   >> k.guests.map(&:class) 
90   [Dog, Cat, Cat, Bird]
91   
92   >> k.guests.push(Cat.create); k.cats.size
93   3
94   >> k.guests << Cat.create; k.cats.size
95   4
96   >> k.guests.size
97   6
99   >> d = k.dogs.first
100   #<Dog id: 3, name: "Rover">
101   >> d.kennels 
102   [#<Kennel id: 1, name: "Happy Paws">]
103   
104   >> k.guests.delete(d); k.dogs.size
105   0
106   >> k.guests.size
107   5  
108   
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.
113 = Extras
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
122   
123     acts_as_double_polymorphic_join(
124       :guests =>[:dogs, :cats], 
125       :eatens => [:cats, :birds]
126     )       
127   end
128   
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
136     def self.up
137       create_table :devourings do |t|
138         t.references :guest, :polymorphic => true
139         t.references :eaten, :polymorphic => true
140       end
141     end
142   
143     def self.down
144       drop_table :devourings
145     end
146   end
148 See ActiveRecord::Associations::PolymorphicClassMethods for more.
150 == Tagging generator
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">
163   >> d.tag_list
164   ""
165   >> d.tag_with "fierce loud"
166   #<Dog id: 3, name: "Rover">
167   >> d.tag_list
168   "fierce loud"
169   >> c = Cat.create(:name => "Chloe")
170   #<Cat id: 1, name: "Chloe">
171   >> c.tag_with "fierce cute"
172   #<Cat id: 1, name: "Chloe">
173   >> c.tag_list
174   "cute fierce"
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.
182 == Troubleshooting
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.
196 == Further resources
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