same
[has_many_polymorphs.git] / README
blobc6c179075fb598136d988737d6e65634909aaa1f
2 Has_many_polymorphs
4 An ActiveRecord plugin for self-referential and double-sided polymorphic associations.
6 == License
8 Copyright 2007 Cloudburst, LLC. Licensed under the AFL 3. See the included LICENSE file. 
10 The public certificate for this gem is at http://rubyforge.org/frs/download.php/25331/evan_weaver-original-public_cert.pem.
12 == Description
14 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>.
16 “Polymorphic” means an association can freely point to any of several unrelated model classes, instead of being tied to one particular class.
18 == Features
20 * self-references
21 * double-sided polymorphism
22 * efficient database usage
23 * STI support
24 * namespace support
25 * automatic individual and reverse associations
27 The plugin also includes a generator for a tagging system, a common use case (see below).
29 == Requirements
31 * Rails 1.2.3 or greater
33 = Usage
35 == Installation
37 To install the Rails plugin, run:
38   script/plugin install svn://rubyforge.org/var/svn/fauna/has_many_polymorphs/trunk
40 There's also a gem version. To install it instead, run: 
41   sudo gem install has_many_polymorphs
42   
43 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.
45 == Configuration
47 Setup the parent model as so:
49   class Kennel < ActiveRecord::Base
50     has_many_polymorphs :guests, :from => [:dogs, :cats, :birds]
51   end
53 The join model:
55   class GuestsKennel < ActiveRecord::Base
56     belongs_to :kennel
57     belongs_to :guest, :polymorphic => true
58   end
60 One of the child models:
62   class Dog < ActiveRecord::Base
63     # nothing
64   end
66 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:
68   class CreateGuestsKennels < ActiveRecord::Migration
69     def self.up
70       create_table :guests_kennels do |t|
71         t.references :guest, :polymorphic => true
72         t.references :kennel
73       end
74     end
75   
76     def self.down
77       drop_table :guests_kennels
78     end
79   end
81 See ActiveRecord::Associations::PolymorphicClassMethods for more configuration options.
83 == Helper methods example
85   >> k = Kennel.find(1)
86   #<Kennel id: 1, name: "Happy Paws">
87   >> k.guests.map(&:class) 
88   [Dog, Cat, Cat, Bird]
89   
90   >> k.guests.push(Cat.create); k.cats.size
91   3
92   >> k.guests << Cat.create; k.cats.size
93   4
94   >> k.guests.size
95   6
97   >> d = k.dogs.first
98   #<Dog id: 3, name: "Rover">
99   >> d.kennels 
100   [#<Kennel id: 1, name: "Happy Paws">]
101   
102   >> k.guests.delete(d); k.dogs.size
103   0
104   >> k.guests.size
105   5  
106   
107 Note that the parent method is always plural, even if there is only one parent (<tt>Dog#kennels</tt>, not <tt>Dog#kennel</tt>).
109 See ActiveRecord::Associations::PolymorphicAssociation for more helper method details.
111 = Extras
113 == Double-sided polymorphism
115 Double-sided relationships are defined on the join model:
117   class Devouring < ActiveRecord::Base
118     belongs_to :guest, :polymorphic => true
119     belongs_to :eaten, :polymorphic => true
120   
121     acts_as_double_polymorphic_join(
122       :guests =>[:dogs, :cats], 
123       :eatens => [:cats, :birds]
124     )       
125   end
126   
127 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. 
129 In this case, each guest/eaten relationship is called a Devouring.
131 In your migration, you need to declare both sides as polymorphic:
133   class CreateDevourings < ActiveRecord::Migration
134     def self.up
135       create_table :devourings do |t|
136         t.references :guest, :polymorphic => true
137         t.references :eaten, :polymorphic => true
138       end
139     end
140   
141     def self.down
142       drop_table :devourings
143     end
144   end
146 See ActiveRecord::Associations::PolymorphicClassMethods for more.
148 == Tagging generator
150 Has_many_polymorphs includes a tagging system generator. Run:
151   script/generate tagging Dog Cat [...MoreModels...]
153 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>. 
155 Tests will also be generated. 
157 Once you've run the generator, you can tag records as follows:
159   >> d = Dog.create(:name => "Rover")
160   #<Dog id: 3, name: "Rover">
161   >> d.tag_list
162   ""
163   >> d.tag_with "fierce loud"
164   #<Dog id: 3, name: "Rover">
165   >> d.tag_list
166   "fierce loud"
167   >> c = Cat.create(:name => "Chloe")
168   #<Cat id: 1, name: "Chloe">
169   >> c.tag_with "fierce cute"
170   #<Cat id: 1, name: "Chloe">
171   >> c.tag_list
172   "cute fierce"
173   >> Tag.find_by_name("fierce").taggables 
174   [#<Cat id: 1, name: "Chloe">, #<Dog id: 3, name: "Rover">]
176 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.
178 See ActiveRecord::Base::TaggingExtensions, Tag, and Tagging for more.
180 == Troubleshooting
182 Some debugging tools are available in <tt>lib/has_many_polymorphs/debugging_tools.rb</tt>.
184 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.
186 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. 
188 == Reporting problems
190 * http://rubyforge.org/forum/forum.php?forum_id=16450
192 Patches and contributions are very welcome. Please note that contributors are required to assign copyright for their additions to Cloudburst, LLC.
194 == Further resources
196 * http://blog.evanweaver.com/articles/2007/08/15/polymorphs-tutorial
197 * http://blog.evanweaver.com/articles/2007/02/22/polymorphs-25-total-insanity-branch
198 * http://blog.evanweaver.com/articles/2007/02/09/how-to-find-the-most-popular-tags
199 * http://blog.evanweaver.com/articles/2007/01/13/growing-up-your-acts_as_taggable
200 * http://blog.evanweaver.com/articles/2006/12/02/polymorphs-19
201 * http://blog.evanweaver.com/articles/2006/11/05/directed-double-polymorphic-associations
202 * http://blog.evanweaver.com/articles/2006/11/04/namespaced-model-support-in-has_many_polymorphs
203 * http://blog.evanweaver.com/articles/2006/09/26/sti-support-in-has_many_polymorphs
204 * http://blog.evanweaver.com/articles/2006/09/11/make-polymorphic-children-belong-to-only-one-parent