features list
[has_many_polymorphs.git] / README
blobc69bc48e5dfd8230dcbf4585772a9dac1681717a
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 == Description
12 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>.
14 “Polymorphic” means an association can freely point to any of several unrelated model classes, instead of being tied to one particular class.
16 == Features
18 * self-references
19 * double-sided polymorphism
20 * efficient database usage
21 * STI support
22 * namespace support
23 * automatic individual and reverse associations
25 The plugin also includes a generator for a tagging system, a common use case (see below).
27 == Requirements
29 * Rails 1.2.3 or grguest
31 = Usage
33 == Installation
35 To install the Rails plugin, run:
36   script/plugin install svn://rubyforge.org/var/svn/fauna/has_many_polymorphs/trunk
38 There's also a gem version. To install it instead, run: 
39   sudo gem install has_many_polymorphs
40   
41 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.
43 == Configuration
45 Setup the parent model as so:
47   class Kennel < ActiveRecord::Base
48     has_many_polymorphs :guests, :from => [:dogs, :cats, :birds]
49   end
51 The join model:
53   class EatersKennel < ActiveRecord::Base
54     belongs_to :kennel
55     belongs_to :guest, :polymorphic => true
56   end
58 One of the child models:
60   class Dog < ActiveRecord::Base
61     # nothing
62   end
64 See ActiveRecord::Associations::PolymorphicClassMethods for more configuration options.
66 == Helper methods example
68   >> k = Kennel.find(1)
69   #<Kennel id: 1, name: "Happy Paws">
70   >> k.guests.map(&:class) 
71   [Dog, Cat, Cat, Bird]
72   
73   >> k.guests.push(Cat.create); k.cats.size
74   3
75   >> k.guests << Cat.create; k.cats.size
76   4
77   >> k.guests.size
78   6
80   >> d = k.dogs.first
81   #<Dog id: 3, name: "Rover">
82   >> d.kennels 
83   [#<Kennel id: 1, name: "Happy Paws">]
84   
85   >> k.guests.delete(d); k.dogs.size
86   0
87   >> k.guests.size
88   5  
89   
90 Note that the parent method is always plural, even if there is only one parent (<tt>Dog#kennels</tt>, not <tt>Dog#kennel</tt>).
92 See ActiveRecord::Associations::PolymorphicAssociation for more helper method details.
94 = Extras
96 == Double-sided polymorphism
98 Double-sided relationships are defined on the join model:
100   class Devouring < ActiveRecord::Base
101     belongs_to :guest, :polymorphic => true
102     belongs_to :eaten, :polymorphic => true
103   
104     acts_as_double_polymorphic_join(
105       :guests =>[:dogs, :cats], 
106       :eatens => [:cats, :birds]
107     )       
108   end
109   
110 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. 
112 In this case, each guest/eaten relationship is called a Devouring.
114 See ActiveRecord::Associations::PolymorphicClassMethods for more.
116 == Tagging generator
118 Has_many_polymorphs includes a tagging system generator. Run:
119   script/generate tagging Dog Cat [...MoreModels...]
121 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>. 
123 Tests will also be generated. 
125 Once you've run the generator, you can tag records as follows:
127   >> d = Dog.create(:name => "Rover")
128   #<Dog id: 3, name: "Rover">
129   >> d.tag_list
130   ""
131   >> d.tag_with "fierce loud"
132   #<Dog id: 3, name: "Rover">
133   >> d.tag_list
134   "fierce loud"
135   >> c = Cat.create(:name => "Chloe")
136   #<Cat id: 1, name: "Chloe">
137   >> c.tag_with "fierce cute"
138   #<Cat id: 1, name: "Chloe">
139   >> c.tag_list
140   "cute fierce"
141   >> Tag.find_by_name("fierce").taggables 
142   [#<Cat id: 1, name: "Chloe">, #<Dog id: 3, name: "Rover">]
144 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.
146 See ActiveRecord::Base::TaggingExtensions, Tag, and Tagging for more.
148 == Troubleshooting
150 Some debugging tools are available in <tt>lib/has_many_polymorphs/debugging_tools.rb</tt>.
152 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.
154 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. 
156 == Further resources
158 * http://blog.evanweaver.com/pages/code#polymorphs
159 * http://rubyforge.org/forum/forum.php?forum_id=16450