Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

root/tags/rel_2-0-2/activerecord/lib/active_record/associations.rb

Revision 8305, 94.1 kB (checked in by marcel, 1 year ago)

Fix typo in documentation for polymorphic associations w/STI. Closes #7461 [johnjosephbachir]

  • Property svn:executable set to *
Line 
1 require 'active_record/associations/association_proxy'
2 require 'active_record/associations/association_collection'
3 require 'active_record/associations/belongs_to_association'
4 require 'active_record/associations/belongs_to_polymorphic_association'
5 require 'active_record/associations/has_one_association'
6 require 'active_record/associations/has_many_association'
7 require 'active_record/associations/has_many_through_association'
8 require 'active_record/associations/has_and_belongs_to_many_association'
9
10 module ActiveRecord
11   class HasManyThroughAssociationNotFoundError < ActiveRecordError #:nodoc:
12     def initialize(owner_class_name, reflection)
13       super("Could not find the association #{reflection.options[:through].inspect} in model #{owner_class_name}")
14     end
15   end
16
17   class HasManyThroughAssociationPolymorphicError < ActiveRecordError #:nodoc:
18     def initialize(owner_class_name, reflection, source_reflection)
19       super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' on the polymorphic object '#{source_reflection.class_name}##{source_reflection.name}'.")
20     end
21   end
22  
23   class HasManyThroughAssociationPointlessSourceTypeError < ActiveRecordError #:nodoc:
24     def initialize(owner_class_name, reflection, source_reflection)
25       super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' with a :source_type option if the '#{reflection.through_reflection.class_name}##{source_reflection.name}' is not polymorphic.  Try removing :source_type on your association.")
26     end
27   end
28  
29   class HasManyThroughSourceAssociationNotFoundError < ActiveRecordError #:nodoc:
30     def initialize(reflection)
31       through_reflection      = reflection.through_reflection
32       source_reflection_names = reflection.source_reflection_names
33       source_associations     = reflection.through_reflection.klass.reflect_on_all_associations.collect { |a| a.name.inspect }
34       super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence :connector => 'or'} in model #{through_reflection.klass}.  Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'.  Is it one of #{source_associations.to_sentence :connector => 'or'}?")
35     end
36   end
37
38   class HasManyThroughSourceAssociationMacroError < ActiveRecordError #:nodoc:
39     def initialize(reflection)
40       through_reflection = reflection.through_reflection
41       source_reflection  = reflection.source_reflection
42       super("Invalid source reflection macro :#{source_reflection.macro}#{" :through" if source_reflection.options[:through]} for has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}.  Use :source to specify the source reflection.")
43     end
44   end
45
46   class HasManyThroughCantAssociateNewRecords < ActiveRecordError #:nodoc:
47     def initialize(owner, reflection)
48       super("Cannot associate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to create the has_many :through record associating them.")
49     end
50   end
51
52   class HasManyThroughCantDissociateNewRecords < ActiveRecordError #:nodoc:
53     def initialize(owner, reflection)
54       super("Cannot dissociate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to delete the has_many :through record associating them.")
55     end
56   end
57
58   class EagerLoadPolymorphicError < ActiveRecordError #:nodoc:
59     def initialize(reflection)
60       super("Can not eagerly load the polymorphic association #{reflection.name.inspect}")
61     end
62   end
63
64   class ReadOnlyAssociation < ActiveRecordError #:nodoc:
65     def initialize(reflection)
66       super("Can not add to a has_many :through association.  Try adding to #{reflection.through_reflection.name.inspect}.")
67     end
68   end
69
70   module Associations # :nodoc:
71     def self.included(base)
72       base.extend(ClassMethods)
73     end
74
75     # Clears out the association cache
76     def clear_association_cache #:nodoc:
77       self.class.reflect_on_all_associations.to_a.each do |assoc|
78         instance_variable_set "@#{assoc.name}", nil
79       end unless self.new_record?
80     end
81    
82     # Associations are a set of macro-like class methods for tying objects together through foreign keys. They express relationships like
83     # "Project has one Project Manager" or "Project belongs to a Portfolio". Each macro adds a number of methods to the class which are
84     # specialized according to the collection or association symbol and the options hash. It works much the same way as Ruby's own <tt>attr*</tt>
85     # methods. Example:
86     #
87     #   class Project < ActiveRecord::Base
88     #     belongs_to              :portfolio
89     #     has_one                 :project_manager
90     #     has_many                :milestones
91     #     has_and_belongs_to_many :categories
92     #   end
93     #
94     # The project class now has the following methods (and more) to ease the traversal and manipulation of its relationships:
95     # * <tt>Project#portfolio, Project#portfolio=(portfolio), Project#portfolio.nil?</tt>
96     # * <tt>Project#project_manager, Project#project_manager=(project_manager), Project#project_manager.nil?,</tt>
97     # * <tt>Project#milestones.empty?, Project#milestones.size, Project#milestones, Project#milestones<<(milestone),</tt>
98     #   <tt>Project#milestones.delete(milestone), Project#milestones.find(milestone_id), Project#milestones.find(:all, options),</tt>
99     #   <tt>Project#milestones.build, Project#milestones.create</tt>
100     # * <tt>Project#categories.empty?, Project#categories.size, Project#categories, Project#categories<<(category1),</tt>
101     #   <tt>Project#categories.delete(category1)</tt>
102     #
103     # === A word of warning
104     #
105     # Don't create associations that have the same name as instance methods of ActiveRecord::Base. Since the association
106     # adds a method with that name to its model, it will override the inherited method and break things.
107     # For instance, #attributes and #connection would be bad choices for association names.
108     #
109     # == Auto-generated methods
110     #
111     # ===Singular associations (one-to-one)
112     #                                     |            |  belongs_to  |
113     #   generated methods                 | belongs_to | :polymorphic | has_one
114     #   ----------------------------------+------------+--------------+---------
115     #   #other                            |     X      |      X       |    X
116     #   #other=(other)                    |     X      |      X       |    X
117     #   #build_other(attributes={})       |     X      |              |    X
118     #   #create_other(attributes={})      |     X      |              |    X
119     #   #other.create!(attributes={})     |            |              |    X
120     #   #other.nil?                       |     X      |      X       |   
121     #
122     # ===Collection associations (one-to-many / many-to-many)
123     #                                     |       |          | has_many
124     #   generated methods                 | habtm | has_many | :through 
125     #   ----------------------------------+-------+----------+----------
126     #   #others                           |   X   |    X     |    X
127     #   #others=(other,other,...)         |   X   |    X     |   
128     #   #other_ids                        |   X   |    X     |    X
129     #   #other_ids=(id,id,...)            |   X   |    X     |   
130     #   #others<<                         |   X   |    X     |    X
131     #   #others.push                      |   X   |    X     |    X
132     #   #others.concat                    |   X   |    X     |    X
133     #   #others.build(attributes={})      |   X   |    X     |    X
134     #   #others.create(attributes={})     |   X   |    X     |   
135     #   #others.create!(attributes={})    |   X   |    X     |    X
136     #   #others.size                      |   X   |    X     |    X
137     #   #others.length                    |   X   |    X     |    X
138     #   #others.count                     |       |    X     |    X
139     #   #others.sum(args*,&block)         |   X   |    X     |    X
140     #   #others.empty?                    |   X   |    X     |    X
141     #   #others.clear                     |   X   |    X     |   
142     #   #others.delete(other,other,...)   |   X   |    X     |    X
143     #   #others.delete_all                |   X   |    X     |   
144     #   #others.destroy_all               |   X   |    X     |    X
145     #   #others.find(*args)               |   X   |    X     |    X
146     #   #others.find_first                |   X   |          |   
147     #   #others.uniq                      |   X   |    X     |   
148     #   #others.reset                     |   X   |    X     |    X
149     #
150     # == Cardinality and associations
151     #
152     # ActiveRecord associations can be used to describe relations with one-to-one, one-to-many
153     # and many-to-many cardinality. Each model uses an association to describe its role in
154     # the relation. In each case, the +belongs_to+ association is used in the model that has
155     # the foreign key.
156     #
157     # === One-to-one
158     #
159     # Use +has_one+ in the base, and +belongs_to+ in the associated model.
160     #
161     #   class Employee < ActiveRecord::Base
162     #     has_one :office
163     #   end
164     #   class Office < ActiveRecord::Base
165     #     belongs_to :employee    # foreign key - employee_id
166     #   end
167     #
168     # === One-to-many
169     #
170     # Use +has_many+ in the base, and +belongs_to+ in the associated model.
171     #
172     #   class Manager < ActiveRecord::Base
173     #     has_many :employees
174     #   end
175     #   class Employee < ActiveRecord::Base
176     #     belongs_to :manager     # foreign key - manager_id
177     #   end
178     #
179     # === Many-to-many
180     #
181     # There are two ways to build a many-to-many relationship.
182     #
183     # The first way uses a +has_many+ association with the <tt>:through</tt> option and a join model, so
184     # there are two stages of associations.
185     #
186     #   class Assignment < ActiveRecord::Base
187     #     belongs_to :programmer  # foreign key - programmer_id
188     #     belongs_to :project     # foreign key - project_id
189     #   end
190     #   class Programmer < ActiveRecord::Base
191     #     has_many :assignments
192     #     has_many :projects, :through => :assignments
193     #   end
194     #   class Project < ActiveRecord::Base
195     #     has_many :assignments
196     #     has_many :programmers, :through => :assignments
197     #   end
198     #
199     # For the second way, use +has_and_belongs_to_many+ in both models. This requires a join table
200     # that has no corresponding model or primary key.
201     #
202     #   class Programmer < ActiveRecord::Base
203     #     has_and_belongs_to_many :projects       # foreign keys in the join table
204     #   end
205     #   class Project < ActiveRecord::Base
206     #     has_and_belongs_to_many :programmers    # foreign keys in the join table
207     #   end
208     #
209     # Choosing which way to build a many-to-many relationship is not always simple.
210     # If you need to work with the relationship model as its own entity,
211     # use <tt>has_many :through</tt>. Use +has_and_belongs_to_many+ when working with legacy schemas or when
212     # you never work directly with the relationship itself.
213     #
214     # == Is it a +belongs_to+ or +has_one+ association?
215     #
216     # Both express a 1-1 relationship. The difference is mostly where to place the foreign key, which goes on the table for the class
217     # declaring the +belongs_to+ relationship. Example:
218     #
219     #   class User < ActiveRecord::Base
220     #     # I reference an account.
221     #     belongs_to :account
222     #   end
223     #
224     #   class Account < ActiveRecord::Base
225     #     # One user references me.
226     #     has_one :user
227     #   end
228     #
229     # The tables for these classes could look something like:
230     #
231     #   CREATE TABLE users (
232     #     id int(11) NOT NULL auto_increment,
233     #     account_id int(11) default NULL,
234     #     name varchar default NULL,
235     #     PRIMARY KEY  (id)
236     #   )
237     #
238     #   CREATE TABLE accounts (
239     #     id int(11) NOT NULL auto_increment,
240     #     name varchar default NULL,
241     #     PRIMARY KEY  (id)
242     #   )
243     #
244     # == Unsaved objects and associations
245     #
246     # You can manipulate objects and associations before they are saved to the database, but there is some special behavior you should be
247     # aware of, mostly involving the saving of associated objects.
248     #
249     # === One-to-one associations
250     #
251     # * Assigning an object to a +has_one+ association automatically saves that object and the object being replaced (if there is one), in
252     #   order to update their primary keys - except if the parent object is unsaved (<tt>new_record? == true</tt>).
253     # * If either of these saves fail (due to one of the objects being invalid) the assignment statement returns +false+ and the assignment
254     #   is cancelled.
255     # * If you wish to assign an object to a +has_one+ association without saving it, use the <tt>#association.build</tt> method (documented below).
256     # * Assigning an object to a +belongs_to+ association does not save the object, since the foreign key field belongs on the parent. It
257     #   does not save the parent either.
258     #
259     # === Collections
260     #
261     # * Adding an object to a collection (+has_many+ or +has_and_belongs_to_many+) automatically saves that object, except if the parent object
262     #   (the owner of the collection) is not yet stored in the database.
263     # * If saving any of the objects being added to a collection (via <tt>#push</tt> or similar) fails, then <tt>#push</tt> returns +false+.
264     # * You can add an object to a collection without automatically saving it by using the <tt>#collection.build</tt> method (documented below).
265     # * All unsaved (<tt>new_record? == true</tt>) members of the collection are automatically saved when the parent is saved.
266     #
267     # === Association callbacks
268     #
269     # Similar to the normal callbacks that hook into the lifecycle of an Active Record object, you can also define callbacks that get
270     # triggered when you add an object to or remove an object from an association collection. Example:
271     #
272     #   class Project
273     #     has_and_belongs_to_many :developers, :after_add => :evaluate_velocity
274     #
275     #     def evaluate_velocity(developer)
276     #       ...
277     #     end
278     #   end
279     #
280     # It's possible to stack callbacks by passing them as an array. Example:
281     #
282     #   class Project
283     #     has_and_belongs_to_many :developers, :after_add => [:evaluate_velocity, Proc.new { |p, d| p.shipping_date = Time.now}]
284     #   end
285     #
286     # Possible callbacks are: +before_add+, +after_add+, +before_remove+ and +after_remove+.
287     #
288     # Should any of the +before_add+ callbacks throw an exception, the object does not get added to the collection. Same with
289     # the +before_remove+ callbacks; if an exception is thrown the object doesn't get removed.
290     #
291     # === Association extensions
292     #
293     # The proxy objects that control the access to associations can be extended through anonymous modules. This is especially
294     # beneficial for adding new finders, creators, and other factory-type methods that are only used as part of this association.
295     # Example:
296     #
297     #   class Account < ActiveRecord::Base
298     #     has_many :people do
299     #       def find_or_create_by_name(name)
300     #         first_name, last_name = name.split(" ", 2)
301     #         find_or_create_by_first_name_and_last_name(first_name, last_name)
302     #       end
303     #     end
304     #   end
305     #
306     #   person = Account.find(:first).people.find_or_create_by_name("David Heinemeier Hansson")
307     #   person.first_name # => "David"
308     #   person.last_name  # => "Heinemeier Hansson"
309     #
310     # If you need to share the same extensions between many associations, you can use a named extension module. Example:
311     #
312     #   module FindOrCreateByNameExtension
313     #     def find_or_create_by_name(name)
314     #       first_name, last_name = name.split(" ", 2)
315     #       find_or_create_by_first_name_and_last_name(first_name, last_name)
316     #     end
317     #   end
318     #
319     #   class Account < ActiveRecord::Base
320     #     has_many :people, :extend => FindOrCreateByNameExtension
321     #   end
322     #
323     #   class Company < ActiveRecord::Base
324     #     has_many :people, :extend => FindOrCreateByNameExtension
325     #   end
326     #
327     # If you need to use multiple named extension modules, you can specify an array of modules with the <tt>:extend</tt> option.
328     # In the case of name conflicts between methods in the modules, methods in modules later in the array supercede
329     # those earlier in the array. Example:
330     #
331     #   class Account < ActiveRecord::Base
332     #     has_many :people, :extend => [FindOrCreateByNameExtension, FindRecentExtension]
333     #   end
334     #
335     # Some extensions can only be made to work with knowledge of the association proxy's internals.
336     # Extensions can access relevant state using accessors on the association proxy:
337     #
338     # * +proxy_owner+ - Returns the object the association is part of.
339     # * +proxy_reflection+ - Returns the reflection object that describes the association.
340     # * +proxy_target+ - Returns the associated object for +belongs_to+ and +has_one+, or the collection of associated objects for +has_many+ and +has_and_belongs_to_many+.
341     #
342     # === Association Join Models
343     #
344     # Has Many associations can be configured with the <tt>:through</tt> option to use an explicit join model to retrieve the data.  This
345     # operates similarly to a +has_and_belongs_to_many+ association.  The advantage is that you're able to add validations,
346     # callbacks, and extra attributes on the join model.  Consider the following schema:
347     #
348     #   class Author < ActiveRecord::Base
349     #     has_many :authorships
350     #     has_many :books, :through => :authorships
351     #   end
352     #
353     #   class Authorship < ActiveRecord::Base
354     #     belongs_to :author
355     #     belongs_to :book
356     #   end
357     #
358     #   @author = Author.find :first
359     #   @author.authorships.collect { |a| a.book } # selects all books that the author's authorships belong to.
360     #   @author.books                              # selects all books by using the Authorship join model
361     #
362     # You can also go through a +has_many+ association on the join model:
363     #
364     #   class Firm < ActiveRecord::Base
365     #     has_many   :clients
366     #     has_many   :invoices, :through => :clients
367     #   end
368     #   
369     #   class Client < ActiveRecord::Base
370     #     belongs_to :firm
371     #     has_many   :invoices
372     #   end
373     #   
374     #   class Invoice < ActiveRecord::Base
375     #     belongs_to :client
376     #   end
377     #
378     #   @firm = Firm.find :first
379     #   @firm.clients.collect { |c| c.invoices }.flatten # select all invoices for all clients of the firm
380     #   @firm.invoices                                   # selects all invoices by going through the Client join model.
381     #
382     # === Polymorphic Associations
383     #
384     # Polymorphic associations on models are not restricted on what types of models they can be associated with.  Rather, they
385     # specify an interface that a +has_many+ association must adhere to.
386     #
387     #   class Asset < ActiveRecord::Base
388     #     belongs_to :attachable, :polymorphic => true
389     #   end
390     #
391     #   class Post < ActiveRecord::Base
392     #     has_many :assets, :as => :attachable         # The :as option specifies the polymorphic interface to use.
393     #   end
394     #
395     #   @asset.attachable = @post
396     #
397     # This works by using a type column in addition to a foreign key to specify the associated record.  In the Asset example, you'd need
398     # an +attachable_id+ integer column and an +attachable_type+ string column.
399     #
400     # Using polymorphic associations in combination with single table inheritance (STI) is a little tricky. In order
401     # for the associations to work as expected, ensure that you store the base model for the STI models in the
402     # type column of the polymorphic association. To continue with the asset example above, suppose there are guest posts
403     # and member posts that use the posts table for STI. In this case, there must be a +type+ column in the posts table.
404     #
405     #   class Asset < ActiveRecord::Base
406     #     belongs_to :attachable, :polymorphic => true
407     #     
408     #     def attachable_type=(sType)
409     #        super(sType.to_s.classify.constantize.base_class.to_s)
410     #     end
411     #   end
412     #
413     #   class Post < ActiveRecord::Base
414     #     # because we store "Post" in attachable_type now :dependent => :destroy will work
415     #     has_many :assets, :as => :attachable, :dependent => :destroy
416     #   end
417     #
418     #   class GuestPost < Post
419     #   end
420     #
421     #   class MemberPost < Post
422     #   end
423     #
424     # == Caching
425     #
426     # All of the methods are built on a simple caching principle that will keep the result of the last query around unless specifically
427     # instructed not to. The cache is even shared across methods to make it even cheaper to use the macro-added methods without
428     # worrying too much about performance at the first go. Example:
429     #
430     #   project.milestones             # fetches milestones from the database
431     #   project.milestones.size        # uses the milestone cache
432     #   project.milestones.empty?      # uses the milestone cache
433     #   project.milestones(true).size  # fetches milestones from the database
434     #   project.milestones             # uses the milestone cache
435     #
436     # == Eager loading of associations
437     #
438     # Eager loading is a way to find objects of a certain class and a number of named associations along with it in a single SQL call. This is
439     # one of the easiest ways of to prevent the dreaded 1+N problem in which fetching 100 posts that each need to display their author
440     # triggers 101 database queries. Through the use of eager loading, the 101 queries can be reduced to 1. Example:
441     #
442     #   class Post < ActiveRecord::Base
443     #     belongs_to :author
444     #     has_many   :comments
445     #   end
446     #
447     # Consider the following loop using the class above:
448     #
449     #   for post in Post.find(:all)
450     #     puts "Post:            " + post.title
451     #     puts "Written by:      " + post.author.name
452     #     puts "Last comment on: " + post.comments.first.created_on
453     #   end
454     #
455     # To iterate over these one hundred posts, we'll generate 201 database queries. Let's first just optimize it for retrieving the author:
456     #
457     #   for post in Post.find(:all, :include => :author)
458     #
459     # This references the name of the +belongs_to+ association that also used the <tt>:author</tt> symbol, so the find will now weave in a join something
460     # like this: <tt>LEFT OUTER JOIN authors ON authors.id = posts.author_id</tt>. Doing so will cut down the number of queries from 201 to 101.
461     #
462     # We can improve upon the situation further by referencing both associations in the finder with:
463     #
464     #   for post in Post.find(:all, :include => [ :author, :comments ])
465     #
466     # That'll add another join along the lines of: <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt>. And we'll be down to 1 query.
467     #
468     # To include a deep hierarchy of associations, use a hash:
469     #
470     #   for post in Post.find(:all, :include => [ :author, { :comments => { :author => :gravatar } } ])
471     #
472     # That'll grab not only all the comments but all their authors and gravatar pictures.  You can mix and match
473     # symbols, arrays and hashes in any combination to describe the associations you want to load.
474     #
475     # All of this power shouldn't fool you into thinking that you can pull out huge amounts of data with no performance penalty just because you've reduced
476     # the number of queries. The database still needs to send all the data to Active Record and it still needs to be processed. So it's no
477     # catch-all for performance problems, but it's a great way to cut down on the number of queries in a situation as the one described above.
478     #
479     # Since the eager loading pulls from multiple tables, you'll have to disambiguate any column references in both conditions and orders. So
480     # <tt>:order => "posts.id DESC"</tt> will work while <tt>:order => "id DESC"</tt> will not. Because eager loading generates the +SELECT+ statement too, the
481     # <tt>:select</tt> option is ignored.
482     #
483     # You can use eager loading on multiple associations from the same table, but you cannot use those associations in orders and conditions
484     # as there is currently not any way to disambiguate them. Eager loading will not pull additional attributes on join tables, so "rich
485     # associations" with +has_and_belongs_to_many+ are not a good fit for eager loading.
486     #
487     # When eager loaded, conditions are interpolated in the context of the model class, not the model instance.  Conditions are lazily interpolated
488     # before the actual model exists.
489     #
490     # == Table Aliasing
491     #
492     # ActiveRecord uses table aliasing in the case that a table is referenced multiple times in a join.  If a table is referenced only once,
493     # the standard table name is used.  The second time, the table is aliased as <tt>#{reflection_name}_#{parent_table_name}</tt>.  Indexes are appended
494     # for any more successive uses of the table name.
495     #
496     #   Post.find :all, :include => :comments
497     #   # => SELECT ... FROM posts LEFT OUTER JOIN comments ON ...
498     #   Post.find :all, :include => :special_comments # STI
499     #   # => SELECT ... FROM posts LEFT OUTER JOIN comments ON ... AND comments.type = 'SpecialComment'
500     #   Post.find :all, :include => [:comments, :special_comments] # special_comments is the reflection name, posts is the parent table name
501     #   # => SELECT ... FROM posts LEFT OUTER JOIN comments ON ... LEFT OUTER JOIN comments special_comments_posts
502     #
503     # Acts as tree example:
504     #
505     #   TreeMixin.find :all, :include => :children
506     #   # => SELECT ... FROM mixins LEFT OUTER JOIN mixins childrens_mixins ...
507     #   TreeMixin.find :all, :include => {:children => :parent} # using cascading eager includes
508     #   # => SELECT ... FROM mixins LEFT OUTER JOIN mixins childrens_mixins ...
509     #                               LEFT OUTER JOIN parents_mixins ...
510     #   TreeMixin.find :all, :include => {:children => {:parent => :children}}
511     #   # => SELECT ... FROM mixins LEFT OUTER JOIN mixins childrens_mixins ...
512     #                               LEFT OUTER JOIN parents_mixins ...
513     #                               LEFT OUTER JOIN mixins childrens_mixins_2
514     #
515     # Has and Belongs to Many join tables use the same idea, but add a <tt>_join</tt> suffix:
516     #
517     #   Post.find :all, :include => :categories
518     #   # => SELECT ... FROM posts LEFT OUTER JOIN categories_posts ... LEFT OUTER JOIN categories ...
519     #   Post.find :all, :include => {:categories => :posts}
520     #   # => SELECT ... FROM posts LEFT OUTER JOIN categories_posts ... LEFT OUTER JOIN categories ...
521     #                              LEFT OUTER JOIN categories_posts posts_categories_join LEFT OUTER JOIN posts posts_categories
522     #   Post.find :all, :include => {:categories => {:posts => :categories}}
523     #   # => SELECT ... FROM posts LEFT OUTER JOIN categories_posts ... LEFT OUTER JOIN categories ...
524     #                              LEFT OUTER JOIN categories_posts posts_categories_join LEFT OUTER JOIN posts posts_categories
525     #                              LEFT OUTER JOIN categories_posts categories_posts_join LEFT OUTER JOIN categories categories_posts
526     #
527     # If you wish to specify your own custom joins using a <tt>:joins</tt> option, those table names will take precedence over the eager associations:
528     #
529     #   Post.find :all, :include => :comments, :joins => "inner join comments ..."
530     #   # => SELECT ... FROM posts LEFT OUTER JOIN comments_posts ON ... INNER JOIN comments ...
531     #   Post.find :all, :include => [:comments, :special_comments], :joins => "inner join comments ..."
532     #   # => SELECT ... FROM posts LEFT OUTER JOIN comments comments_posts ON ...
533     #                              LEFT OUTER JOIN comments special_comments_posts ...
534     #                              INNER JOIN comments ...
535     #
536     # Table aliases are automatically truncated according to the maximum length of table identifiers according to the specific database.
537     #
538     # == Modules
539     #
540     # By default, associations will look for objects within the current module scope. Consider:
541     #
542     #   module MyApplication
543     #     module Business
544     #       class Firm < ActiveRecord::Base
545     #          has_many :clients
546     #        end
547     #
548     #       class Company < ActiveRecord::Base; end
549     #     end
550     #   end
551     #
552     # When <tt>Firm#clients</tt> is called, it will in turn call <tt>MyApplication::Business::Company.find(firm.id)</tt>. If you want to associate
553     # with a class in another module scope, this can be done by specifying the complete class name.  Example:
554     #
555     #   module MyApplication
556     #     module Business
557     #       class Firm < ActiveRecord::Base; end
558     #     end
559     #
560     #     module Billing
561     #       class Account < ActiveRecord::Base
562     #         belongs_to :firm, :class_name => "MyApplication::Business::Firm"
563     #       end
564     #     end
565     #   end
566     #
567     # == Type safety with <tt>ActiveRecord::AssociationTypeMismatch</tt>
568     #
569     # If you attempt to assign an object to an association that doesn't match the inferred or specified <tt>:class_name</tt>, you'll
570     # get an <tt>ActiveRecord::AssociationTypeMismatch</tt>.
571     #
572     # == Options
573     #
574     # All of the association macros can be specialized through options. This makes cases more complex than the simple and guessable ones
575     # possible.
576     module ClassMethods
577       # Adds the following methods for retrieval and query of collections of associated objects:
578       # +collection+ is replaced with the symbol passed as the first argument, so
579       # <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.
580       # * <tt>collection(force_reload = false)</tt> - returns an array of all the associated objects.
581       #   An empty array is returned if none are found.
582       # * <tt>collection<<(object, ...)</tt> - adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
583       # * <tt>collection.delete(object, ...)</tt> - removes one or more objects from the collection by setting their foreign keys to NULL. 
584       #   This will also destroy the objects if they're declared as +belongs_to+ and dependent on this model.
585       # * <tt>collection=objects</tt> - replaces the collections content by deleting and adding objects as appropriate.
586       # * <tt>collection_singular_ids</tt> - returns an array of the associated objects' ids
587       # * <tt>collection_singular_ids=ids</tt> - replace the collection with the objects identified by the primary keys in +ids+
588       # * <tt>collection.clear</tt> - removes every object from the collection. This destroys the associated objects if they
589       #   are associated with <tt>:dependent => :destroy</tt>, deletes them directly from the database if <tt>:dependent => :delete_all</tt>,
590       #   otherwise sets their foreign keys to NULL.
591       # * <tt>collection.empty?</tt> - returns +true+ if there are no associated objects.
592       # * <tt>collection.size</tt> - returns the number of associated objects.
593       # * <tt>collection.find</tt> - finds an associated object according to the same rules as Base.find.
594       # * <tt>collection.build(attributes = {}, ...)</tt> - returns one or more new objects of the collection type that have been instantiated
595       #   with +attributes+ and linked to this object through a foreign key, but have not yet been saved. *Note:* This only works if an
596       #   associated object already exists, not if it's +nil+!
597       # * <tt>collection.create(attributes = {})</tt> - returns a new object of the collection type that has been instantiated
598       #   with +attributes+, linked to this object through a foreign key, and that has already been saved (if it passed the validation).
599       #   *Note:* This only works if an associated object already exists, not if it's +nil+!
600       #
601       # Example: A +Firm+ class declares <tt>has_many :clients</tt>, which will add:
602       # * <tt>Firm#clients</tt> (similar to <tt>Clients.find :all, :conditions => "firm_id = #{id}"</tt>)
603       # * <tt>Firm#clients<<</tt>
604       # * <tt>Firm#clients.delete</tt>
605       # * <tt>Firm#clients=</tt>
606       # * <tt>Firm#client_ids</tt>
607       # * <tt>Firm#client_ids=</tt>
608       # * <tt>Firm#clients.clear</tt>
609       # * <tt>Firm#clients.empty?</tt> (similar to <tt>firm.clients.size == 0</tt>)
610       # * <tt>Firm#clients.size</tt> (similar to <tt>Client.count "firm_id = #{id}"</tt>)
611       # * <tt>Firm#clients.find</tt> (similar to <tt>Client.find(id, :conditions => "firm_id = #{id}")</tt>)
612       # * <tt>Firm#clients.build</tt> (similar to <tt>Client.new("firm_id" => id)</tt>)
613       # * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>)
614       # The declaration can also include an options hash to specialize the behavior of the association.
615       #
616       # Options are:
617       # * <tt>:class_name</tt>  - specify the class name of the association. Use it only if that name can't be inferred
618       #   from the association name. So <tt>has_many :products</tt> will by default be linked to the +Product+ class, but
619       #   if the real class name is +SpecialProduct+, you'll have to specify it with this option.
620       # * <tt>:conditions</tt>  - specify the conditions that the associated objects must meet in order to be included as a +WHERE+
621       #   SQL fragment, such as <tt>price > 5 AND name LIKE 'B%'</tt>.
622       # * <tt>:order</tt>       - specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
623       #   such as <tt>last_name, first_name DESC</tt>
624       # * <tt>:foreign_key</tt> - specify the foreign key used for the association. By default this is guessed to be the name
625       #   of this class in lower-case and +_id+ suffixed. So a +Person+ class that makes a +has_many+ association will use +person_id+
626       #   as the default +foreign_key+.
627       # * <tt>:dependent</tt>   - if set to <tt>:destroy</tt> all the associated objects are destroyed
628       #   alongside this object by calling their destroy method.  If set to <tt>:delete_all</tt> all associated
629       #   objects are deleted *without* calling their destroy method.  If set to <tt>:nullify</tt> all associated
630       #   objects' foreign keys are set to +NULL+ *without* calling their save callbacks.
631       # * <tt>:finder_sql</tt>  - specify a complete SQL statement to fetch the association. This is a good way to go for complex
632       #   associations that depend on multiple tables. Note: When this option is used, +find_in_collection+ is _not_ added.
633       # * <tt>:counter_sql</tt>  - specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is
634       #   specified but not <tt>:counter_sql</tt>, <tt>:counter_sql</tt> will be generated by replacing <tt>SELECT ... FROM</tt> with <tt>SELECT COUNT(*) FROM</tt>.
635       # * <tt>:extend</tt>  - specify a named module for extending the proxy. See "Association extensions".
636       # * <tt>:include</tt>  - specify second-order associations that should be eager loaded when the collection is loaded.
637       # * <tt>:group</tt>: An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
638       # * <tt>:limit</tt>: An integer determining the limit on the number of rows that should be returned.
639       # * <tt>:offset</tt>: An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
640       # * <tt>:select</tt>: By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if you, for example, want to do a join
641       #   but not include the joined columns.
642       # * <tt>:as</tt>: Specifies a polymorphic interface (See <tt>#belongs_to</tt>).
643       # * <tt>:through</tt>: Specifies a Join Model through which to perform the query.  Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
644       #   are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a <tt>belongs_to</tt>
645       #   or <tt>has_many</tt> association on the join model.
646       # * <tt>:source</tt>: Specifies the source association name used by <tt>has_many :through</tt> queries.  Only use it if the name cannot be
647       #   inferred from the association.  <tt>has_many :subscribers, :through => :subscriptions</tt> will look for either <tt>:subscribers</tt> or
648       #   <tt>:subscriber</tt> on +Subscription+, unless a <tt>:source</tt> is given.
649       # * <tt>:source_type</tt>: Specifies type of the source association used by <tt>has_many :through</tt> queries where the source
650       #   association is a polymorphic +belongs_to+.
651       # * <tt>:uniq</tt> - if set to +true+, duplicates will be omitted from the collection. Useful in conjunction with <tt>:through</tt>.
652       #
653       # Option examples:
654       #   has_many :comments, :order => "posted_on"
655       #   has_many :comments, :include => :author
656       #   has_many :people, :class_name => "Person", :conditions => "deleted = 0", :order => "name"
657       #   has_many :tracks, :order => "position", :dependent => :destroy
658       #   has_many :comments, :dependent => :nullify
659       #   has_many :tags, :as => :taggable
660       #   has_many :subscribers, :through => :subscriptions, :source => :user
661       #   has_many :subscribers, :class_name => "Person", :finder_sql =>
662       #       'SELECT DISTINCT people.* ' +
663       #       'FROM people p, post_subscriptions ps ' +
664       #       'WHERE ps.post_id = #{id} AND ps.person_id = p.id ' +
665       #       'ORDER BY p.first_name'
666       def has_many(association_id, options = {}, &extension)
667         reflection = create_has_many_reflection(association_id, options, &extension)
668
669         configure_dependency_for_has_many(reflection)
670
671         if options[:through]
672           collection_re