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