square-activerecord 3.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. data/CHANGELOG +6140 -0
  2. data/README.rdoc +222 -0
  3. data/examples/associations.png +0 -0
  4. data/examples/performance.rb +179 -0
  5. data/examples/simple.rb +14 -0
  6. data/lib/active_record.rb +124 -0
  7. data/lib/active_record/aggregations.rb +277 -0
  8. data/lib/active_record/association_preload.rb +430 -0
  9. data/lib/active_record/associations.rb +2307 -0
  10. data/lib/active_record/associations/association_collection.rb +572 -0
  11. data/lib/active_record/associations/association_proxy.rb +299 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +91 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +82 -0
  14. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +143 -0
  15. data/lib/active_record/associations/has_many_association.rb +128 -0
  16. data/lib/active_record/associations/has_many_through_association.rb +115 -0
  17. data/lib/active_record/associations/has_one_association.rb +143 -0
  18. data/lib/active_record/associations/has_one_through_association.rb +40 -0
  19. data/lib/active_record/associations/through_association_scope.rb +154 -0
  20. data/lib/active_record/attribute_methods.rb +60 -0
  21. data/lib/active_record/attribute_methods/before_type_cast.rb +30 -0
  22. data/lib/active_record/attribute_methods/dirty.rb +95 -0
  23. data/lib/active_record/attribute_methods/primary_key.rb +56 -0
  24. data/lib/active_record/attribute_methods/query.rb +39 -0
  25. data/lib/active_record/attribute_methods/read.rb +145 -0
  26. data/lib/active_record/attribute_methods/time_zone_conversion.rb +64 -0
  27. data/lib/active_record/attribute_methods/write.rb +43 -0
  28. data/lib/active_record/autosave_association.rb +369 -0
  29. data/lib/active_record/base.rb +1904 -0
  30. data/lib/active_record/callbacks.rb +284 -0
  31. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +364 -0
  32. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +113 -0
  33. data/lib/active_record/connection_adapters/abstract/database_limits.rb +57 -0
  34. data/lib/active_record/connection_adapters/abstract/database_statements.rb +333 -0
  35. data/lib/active_record/connection_adapters/abstract/query_cache.rb +81 -0
  36. data/lib/active_record/connection_adapters/abstract/quoting.rb +73 -0
  37. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +739 -0
  38. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +539 -0
  39. data/lib/active_record/connection_adapters/abstract_adapter.rb +217 -0
  40. data/lib/active_record/connection_adapters/mysql_adapter.rb +657 -0
  41. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1031 -0
  42. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -0
  43. data/lib/active_record/connection_adapters/sqlite_adapter.rb +401 -0
  44. data/lib/active_record/counter_cache.rb +115 -0
  45. data/lib/active_record/dynamic_finder_match.rb +56 -0
  46. data/lib/active_record/dynamic_scope_match.rb +23 -0
  47. data/lib/active_record/errors.rb +172 -0
  48. data/lib/active_record/fixtures.rb +1006 -0
  49. data/lib/active_record/locale/en.yml +40 -0
  50. data/lib/active_record/locking/optimistic.rb +172 -0
  51. data/lib/active_record/locking/pessimistic.rb +55 -0
  52. data/lib/active_record/log_subscriber.rb +48 -0
  53. data/lib/active_record/migration.rb +617 -0
  54. data/lib/active_record/named_scope.rb +138 -0
  55. data/lib/active_record/nested_attributes.rb +419 -0
  56. data/lib/active_record/observer.rb +125 -0
  57. data/lib/active_record/persistence.rb +290 -0
  58. data/lib/active_record/query_cache.rb +36 -0
  59. data/lib/active_record/railtie.rb +91 -0
  60. data/lib/active_record/railties/controller_runtime.rb +38 -0
  61. data/lib/active_record/railties/databases.rake +512 -0
  62. data/lib/active_record/reflection.rb +411 -0
  63. data/lib/active_record/relation.rb +394 -0
  64. data/lib/active_record/relation/batches.rb +89 -0
  65. data/lib/active_record/relation/calculations.rb +295 -0
  66. data/lib/active_record/relation/finder_methods.rb +363 -0
  67. data/lib/active_record/relation/predicate_builder.rb +48 -0
  68. data/lib/active_record/relation/query_methods.rb +303 -0
  69. data/lib/active_record/relation/spawn_methods.rb +132 -0
  70. data/lib/active_record/schema.rb +59 -0
  71. data/lib/active_record/schema_dumper.rb +195 -0
  72. data/lib/active_record/serialization.rb +60 -0
  73. data/lib/active_record/serializers/xml_serializer.rb +244 -0
  74. data/lib/active_record/session_store.rb +340 -0
  75. data/lib/active_record/test_case.rb +67 -0
  76. data/lib/active_record/timestamp.rb +88 -0
  77. data/lib/active_record/transactions.rb +359 -0
  78. data/lib/active_record/validations.rb +84 -0
  79. data/lib/active_record/validations/associated.rb +48 -0
  80. data/lib/active_record/validations/uniqueness.rb +190 -0
  81. data/lib/active_record/version.rb +10 -0
  82. data/lib/rails/generators/active_record.rb +19 -0
  83. data/lib/rails/generators/active_record/migration.rb +15 -0
  84. data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
  85. data/lib/rails/generators/active_record/migration/templates/migration.rb +17 -0
  86. data/lib/rails/generators/active_record/model/model_generator.rb +38 -0
  87. data/lib/rails/generators/active_record/model/templates/migration.rb +16 -0
  88. data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
  89. data/lib/rails/generators/active_record/model/templates/module.rb +5 -0
  90. data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
  91. data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
  92. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +24 -0
  93. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +16 -0
  94. metadata +223 -0
@@ -0,0 +1,2307 @@
1
+ require 'active_support/core_ext/array/wrap'
2
+ require 'active_support/core_ext/enumerable'
3
+ require 'active_support/core_ext/module/delegation'
4
+ require 'active_support/core_ext/object/blank'
5
+ require 'active_support/core_ext/string/conversions'
6
+ require 'active_support/core_ext/module/remove_method'
7
+
8
+ module ActiveRecord
9
+ class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc:
10
+ def initialize(reflection, associated_class = nil)
11
+ super("Could not find the inverse association for #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{associated_class.nil? ? reflection.class_name : associated_class.name})")
12
+ end
13
+ end
14
+
15
+ class HasManyThroughAssociationNotFoundError < ActiveRecordError #:nodoc:
16
+ def initialize(owner_class_name, reflection)
17
+ super("Could not find the association #{reflection.options[:through].inspect} in model #{owner_class_name}")
18
+ end
19
+ end
20
+
21
+ class HasManyThroughAssociationPolymorphicError < ActiveRecordError #:nodoc:
22
+ def initialize(owner_class_name, reflection, source_reflection)
23
+ super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' on the polymorphic object '#{source_reflection.class_name}##{source_reflection.name}'.")
24
+ end
25
+ end
26
+
27
+ class HasManyThroughAssociationPointlessSourceTypeError < ActiveRecordError #:nodoc:
28
+ def initialize(owner_class_name, reflection, source_reflection)
29
+ 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.")
30
+ end
31
+ end
32
+
33
+ class HasManyThroughSourceAssociationNotFoundError < ActiveRecordError #:nodoc:
34
+ def initialize(reflection)
35
+ through_reflection = reflection.through_reflection
36
+ source_reflection_names = reflection.source_reflection_names
37
+ source_associations = reflection.through_reflection.klass.reflect_on_all_associations.collect { |a| a.name.inspect }
38
+ super("Could not find the source association(s) #{source_reflection_names.collect{ |a| a.inspect }.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)} 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(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)}?")
39
+ end
40
+ end
41
+
42
+ class HasManyThroughSourceAssociationMacroError < ActiveRecordError #:nodoc:
43
+ def initialize(reflection)
44
+ through_reflection = reflection.through_reflection
45
+ source_reflection = reflection.source_reflection
46
+ 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.")
47
+ end
48
+ end
49
+
50
+ class HasManyThroughCantAssociateThroughHasOneOrManyReflection < ActiveRecordError #:nodoc:
51
+ def initialize(owner, reflection)
52
+ super("Cannot modify association '#{owner.class.name}##{reflection.name}' because the source reflection class '#{reflection.source_reflection.class_name}' is associated to '#{reflection.through_reflection.class_name}' via :#{reflection.source_reflection.macro}.")
53
+ end
54
+ end
55
+
56
+ class HasManyThroughCantAssociateNewRecords < ActiveRecordError #:nodoc:
57
+ def initialize(owner, reflection)
58
+ 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.")
59
+ end
60
+ end
61
+
62
+ class HasManyThroughCantDissociateNewRecords < ActiveRecordError #:nodoc:
63
+ def initialize(owner, reflection)
64
+ 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.")
65
+ end
66
+ end
67
+
68
+ class HasAndBelongsToManyAssociationWithPrimaryKeyError < ActiveRecordError #:nodoc:
69
+ def initialize(reflection)
70
+ super("Primary key is not allowed in a has_and_belongs_to_many join table (#{reflection.options[:join_table]}).")
71
+ end
72
+ end
73
+
74
+ class HasAndBelongsToManyAssociationForeignKeyNeeded < ActiveRecordError #:nodoc:
75
+ def initialize(reflection)
76
+ super("Cannot create self referential has_and_belongs_to_many association on '#{reflection.class_name rescue nil}##{reflection.name rescue nil}'. :association_foreign_key cannot be the same as the :foreign_key.")
77
+ end
78
+ end
79
+
80
+ class EagerLoadPolymorphicError < ActiveRecordError #:nodoc:
81
+ def initialize(reflection)
82
+ super("Can not eagerly load the polymorphic association #{reflection.name.inspect}")
83
+ end
84
+ end
85
+
86
+ class ReadOnlyAssociation < ActiveRecordError #:nodoc:
87
+ def initialize(reflection)
88
+ super("Can not add to a has_many :through association. Try adding to #{reflection.through_reflection.name.inspect}.")
89
+ end
90
+ end
91
+
92
+ # This error is raised when trying to destroy a parent instance in N:1 or 1:1 associations
93
+ # (has_many, has_one) when there is at least 1 child associated instance.
94
+ # ex: if @project.tasks.size > 0, DeleteRestrictionError will be raised when trying to destroy @project
95
+ class DeleteRestrictionError < ActiveRecordError #:nodoc:
96
+ def initialize(reflection)
97
+ super("Cannot delete record because of dependent #{reflection.name}")
98
+ end
99
+ end
100
+
101
+ # See ActiveRecord::Associations::ClassMethods for documentation.
102
+ module Associations # :nodoc:
103
+ extend ActiveSupport::Concern
104
+
105
+ # These classes will be loaded when associations are created.
106
+ # So there is no need to eager load them.
107
+ autoload :AssociationCollection, 'active_record/associations/association_collection'
108
+ autoload :AssociationProxy, 'active_record/associations/association_proxy'
109
+ autoload :BelongsToAssociation, 'active_record/associations/belongs_to_association'
110
+ autoload :BelongsToPolymorphicAssociation, 'active_record/associations/belongs_to_polymorphic_association'
111
+ autoload :HasAndBelongsToManyAssociation, 'active_record/associations/has_and_belongs_to_many_association'
112
+ autoload :HasManyAssociation, 'active_record/associations/has_many_association'
113
+ autoload :HasManyThroughAssociation, 'active_record/associations/has_many_through_association'
114
+ autoload :HasOneAssociation, 'active_record/associations/has_one_association'
115
+ autoload :HasOneThroughAssociation, 'active_record/associations/has_one_through_association'
116
+
117
+ # Clears out the association cache.
118
+ def clear_association_cache #:nodoc:
119
+ self.class.reflect_on_all_associations.to_a.each do |assoc|
120
+ instance_variable_set "@#{assoc.name}", nil
121
+ end unless self.new_record?
122
+ end
123
+
124
+ private
125
+ # Returns the specified association instance if it responds to :loaded?, nil otherwise.
126
+ def association_instance_get(name)
127
+ ivar = "@#{name}"
128
+ if instance_variable_defined?(ivar)
129
+ association = instance_variable_get(ivar)
130
+ association if association.respond_to?(:loaded?)
131
+ end
132
+ end
133
+
134
+ # Set the specified association instance.
135
+ def association_instance_set(name, association)
136
+ instance_variable_set("@#{name}", association)
137
+ end
138
+
139
+ # Associations are a set of macro-like class methods for tying objects together through
140
+ # foreign keys. They express relationships like "Project has one Project Manager"
141
+ # or "Project belongs to a Portfolio". Each macro adds a number of methods to the
142
+ # class which are specialized according to the collection or association symbol and the
143
+ # options hash. It works much the same way as Ruby's own <tt>attr*</tt>
144
+ # methods.
145
+ #
146
+ # class Project < ActiveRecord::Base
147
+ # belongs_to :portfolio
148
+ # has_one :project_manager
149
+ # has_many :milestones
150
+ # has_and_belongs_to_many :categories
151
+ # end
152
+ #
153
+ # The project class now has the following methods (and more) to ease the traversal and
154
+ # manipulation of its relationships:
155
+ # * <tt>Project#portfolio, Project#portfolio=(portfolio), Project#portfolio.nil?</tt>
156
+ # * <tt>Project#project_manager, Project#project_manager=(project_manager), Project#project_manager.nil?,</tt>
157
+ # * <tt>Project#milestones.empty?, Project#milestones.size, Project#milestones, Project#milestones<<(milestone),</tt>
158
+ # <tt>Project#milestones.delete(milestone), Project#milestones.find(milestone_id), Project#milestones.find(:all, options),</tt>
159
+ # <tt>Project#milestones.build, Project#milestones.create</tt>
160
+ # * <tt>Project#categories.empty?, Project#categories.size, Project#categories, Project#categories<<(category1),</tt>
161
+ # <tt>Project#categories.delete(category1)</tt>
162
+ #
163
+ # === A word of warning
164
+ #
165
+ # Don't create associations that have the same name as instance methods of
166
+ # <tt>ActiveRecord::Base</tt>. Since the association adds a method with that name to
167
+ # its model, it will override the inherited method and break things.
168
+ # For instance, +attributes+ and +connection+ would be bad choices for association names.
169
+ #
170
+ # == Auto-generated methods
171
+ #
172
+ # === Singular associations (one-to-one)
173
+ # | | belongs_to |
174
+ # generated methods | belongs_to | :polymorphic | has_one
175
+ # ----------------------------------+------------+--------------+---------
176
+ # other | X | X | X
177
+ # other=(other) | X | X | X
178
+ # build_other(attributes={}) | X | | X
179
+ # create_other(attributes={}) | X | | X
180
+ # other.create!(attributes={}) | | | X
181
+ #
182
+ # ===Collection associations (one-to-many / many-to-many)
183
+ # | | | has_many
184
+ # generated methods | habtm | has_many | :through
185
+ # ----------------------------------+-------+----------+----------
186
+ # others | X | X | X
187
+ # others=(other,other,...) | X | X | X
188
+ # other_ids | X | X | X
189
+ # other_ids=(id,id,...) | X | X | X
190
+ # others<< | X | X | X
191
+ # others.push | X | X | X
192
+ # others.concat | X | X | X
193
+ # others.build(attributes={}) | X | X | X
194
+ # others.create(attributes={}) | X | X | X
195
+ # others.create!(attributes={}) | X | X | X
196
+ # others.size | X | X | X
197
+ # others.length | X | X | X
198
+ # others.count | X | X | X
199
+ # others.sum(args*,&block) | X | X | X
200
+ # others.empty? | X | X | X
201
+ # others.clear | X | X | X
202
+ # others.delete(other,other,...) | X | X | X
203
+ # others.delete_all | X | X |
204
+ # others.destroy_all | X | X | X
205
+ # others.find(*args) | X | X | X
206
+ # others.find_first | X | |
207
+ # others.exists? | X | X | X
208
+ # others.uniq | X | X | X
209
+ # others.reset | X | X | X
210
+ #
211
+ # == Cardinality and associations
212
+ #
213
+ # Active Record associations can be used to describe one-to-one, one-to-many and many-to-many
214
+ # relationships between models. Each model uses an association to describe its role in
215
+ # the relation. The +belongs_to+ association is always used in the model that has
216
+ # the foreign key.
217
+ #
218
+ # === One-to-one
219
+ #
220
+ # Use +has_one+ in the base, and +belongs_to+ in the associated model.
221
+ #
222
+ # class Employee < ActiveRecord::Base
223
+ # has_one :office
224
+ # end
225
+ # class Office < ActiveRecord::Base
226
+ # belongs_to :employee # foreign key - employee_id
227
+ # end
228
+ #
229
+ # === One-to-many
230
+ #
231
+ # Use +has_many+ in the base, and +belongs_to+ in the associated model.
232
+ #
233
+ # class Manager < ActiveRecord::Base
234
+ # has_many :employees
235
+ # end
236
+ # class Employee < ActiveRecord::Base
237
+ # belongs_to :manager # foreign key - manager_id
238
+ # end
239
+ #
240
+ # === Many-to-many
241
+ #
242
+ # There are two ways to build a many-to-many relationship.
243
+ #
244
+ # The first way uses a +has_many+ association with the <tt>:through</tt> option and a join model, so
245
+ # there are two stages of associations.
246
+ #
247
+ # class Assignment < ActiveRecord::Base
248
+ # belongs_to :programmer # foreign key - programmer_id
249
+ # belongs_to :project # foreign key - project_id
250
+ # end
251
+ # class Programmer < ActiveRecord::Base
252
+ # has_many :assignments
253
+ # has_many :projects, :through => :assignments
254
+ # end
255
+ # class Project < ActiveRecord::Base
256
+ # has_many :assignments
257
+ # has_many :programmers, :through => :assignments
258
+ # end
259
+ #
260
+ # For the second way, use +has_and_belongs_to_many+ in both models. This requires a join table
261
+ # that has no corresponding model or primary key.
262
+ #
263
+ # class Programmer < ActiveRecord::Base
264
+ # has_and_belongs_to_many :projects # foreign keys in the join table
265
+ # end
266
+ # class Project < ActiveRecord::Base
267
+ # has_and_belongs_to_many :programmers # foreign keys in the join table
268
+ # end
269
+ #
270
+ # Choosing which way to build a many-to-many relationship is not always simple.
271
+ # If you need to work with the relationship model as its own entity,
272
+ # use <tt>has_many :through</tt>. Use +has_and_belongs_to_many+ when working with legacy schemas or when
273
+ # you never work directly with the relationship itself.
274
+ #
275
+ # == Is it a +belongs_to+ or +has_one+ association?
276
+ #
277
+ # Both express a 1-1 relationship. The difference is mostly where to place the foreign
278
+ # key, which goes on the table for the class declaring the +belongs_to+ relationship.
279
+ #
280
+ # class User < ActiveRecord::Base
281
+ # # I reference an account.
282
+ # belongs_to :account
283
+ # end
284
+ #
285
+ # class Account < ActiveRecord::Base
286
+ # # One user references me.
287
+ # has_one :user
288
+ # end
289
+ #
290
+ # The tables for these classes could look something like:
291
+ #
292
+ # CREATE TABLE users (
293
+ # id int(11) NOT NULL auto_increment,
294
+ # account_id int(11) default NULL,
295
+ # name varchar default NULL,
296
+ # PRIMARY KEY (id)
297
+ # )
298
+ #
299
+ # CREATE TABLE accounts (
300
+ # id int(11) NOT NULL auto_increment,
301
+ # name varchar default NULL,
302
+ # PRIMARY KEY (id)
303
+ # )
304
+ #
305
+ # == Unsaved objects and associations
306
+ #
307
+ # You can manipulate objects and associations before they are saved to the database, but
308
+ # there is some special behavior you should be aware of, mostly involving the saving of
309
+ # associated objects.
310
+ #
311
+ # You can set the :autosave option on a <tt>has_one</tt>, <tt>belongs_to</tt>,
312
+ # <tt>has_many</tt>, or <tt>has_and_belongs_to_many</tt> association. Setting it
313
+ # to +true+ will _always_ save the members, whereas setting it to +false+ will
314
+ # _never_ save the members. More details about :autosave option is available at
315
+ # autosave_association.rb .
316
+ #
317
+ # === One-to-one associations
318
+ #
319
+ # * Assigning an object to a +has_one+ association automatically saves that object and
320
+ # the object being replaced (if there is one), in order to update their primary
321
+ # keys - except if the parent object is unsaved (<tt>new_record? == true</tt>).
322
+ # * If either of these saves fail (due to one of the objects being invalid) the assignment
323
+ # statement returns +false+ and the assignment is cancelled.
324
+ # * If you wish to assign an object to a +has_one+ association without saving it,
325
+ # use the <tt>association.build</tt> method (documented below).
326
+ # * Assigning an object to a +belongs_to+ association does not save the object, since
327
+ # the foreign key field belongs on the parent. It does not save the parent either.
328
+ #
329
+ # === Collections
330
+ #
331
+ # * Adding an object to a collection (+has_many+ or +has_and_belongs_to_many+) automatically
332
+ # saves that object, except if the parent object (the owner of the collection) is not yet
333
+ # stored in the database.
334
+ # * If saving any of the objects being added to a collection (via <tt>push</tt> or similar)
335
+ # fails, then <tt>push</tt> returns +false+.
336
+ # * You can add an object to a collection without automatically saving it by using the
337
+ # <tt>collection.build</tt> method (documented below).
338
+ # * All unsaved (<tt>new_record? == true</tt>) members of the collection are automatically
339
+ # saved when the parent is saved.
340
+ #
341
+ # === Association callbacks
342
+ #
343
+ # Similar to the normal callbacks that hook into the life cycle of an Active Record object,
344
+ # you can also define callbacks that get triggered when you add an object to or remove an
345
+ # object from an association collection.
346
+ #
347
+ # class Project
348
+ # has_and_belongs_to_many :developers, :after_add => :evaluate_velocity
349
+ #
350
+ # def evaluate_velocity(developer)
351
+ # ...
352
+ # end
353
+ # end
354
+ #
355
+ # It's possible to stack callbacks by passing them as an array. Example:
356
+ #
357
+ # class Project
358
+ # has_and_belongs_to_many :developers,
359
+ # :after_add => [:evaluate_velocity, Proc.new { |p, d| p.shipping_date = Time.now}]
360
+ # end
361
+ #
362
+ # Possible callbacks are: +before_add+, +after_add+, +before_remove+ and +after_remove+.
363
+ #
364
+ # Should any of the +before_add+ callbacks throw an exception, the object does not get
365
+ # added to the collection. Same with the +before_remove+ callbacks; if an exception is
366
+ # thrown the object doesn't get removed.
367
+ #
368
+ # === Association extensions
369
+ #
370
+ # The proxy objects that control the access to associations can be extended through anonymous
371
+ # modules. This is especially beneficial for adding new finders, creators, and other
372
+ # factory-type methods that are only used as part of this association.
373
+ #
374
+ # class Account < ActiveRecord::Base
375
+ # has_many :people do
376
+ # def find_or_create_by_name(name)
377
+ # first_name, last_name = name.split(" ", 2)
378
+ # find_or_create_by_first_name_and_last_name(first_name, last_name)
379
+ # end
380
+ # end
381
+ # end
382
+ #
383
+ # person = Account.find(:first).people.find_or_create_by_name("David Heinemeier Hansson")
384
+ # person.first_name # => "David"
385
+ # person.last_name # => "Heinemeier Hansson"
386
+ #
387
+ # If you need to share the same extensions between many associations, you can use a named
388
+ # extension module.
389
+ #
390
+ # module FindOrCreateByNameExtension
391
+ # def find_or_create_by_name(name)
392
+ # first_name, last_name = name.split(" ", 2)
393
+ # find_or_create_by_first_name_and_last_name(first_name, last_name)
394
+ # end
395
+ # end
396
+ #
397
+ # class Account < ActiveRecord::Base
398
+ # has_many :people, :extend => FindOrCreateByNameExtension
399
+ # end
400
+ #
401
+ # class Company < ActiveRecord::Base
402
+ # has_many :people, :extend => FindOrCreateByNameExtension
403
+ # end
404
+ #
405
+ # If you need to use multiple named extension modules, you can specify an array of modules
406
+ # with the <tt>:extend</tt> option.
407
+ # In the case of name conflicts between methods in the modules, methods in modules later
408
+ # in the array supercede those earlier in the array.
409
+ #
410
+ # class Account < ActiveRecord::Base
411
+ # has_many :people, :extend => [FindOrCreateByNameExtension, FindRecentExtension]
412
+ # end
413
+ #
414
+ # Some extensions can only be made to work with knowledge of the association proxy's internals.
415
+ # Extensions can access relevant state using accessors on the association proxy:
416
+ #
417
+ # * +proxy_owner+ - Returns the object the association is part of.
418
+ # * +proxy_reflection+ - Returns the reflection object that describes the association.
419
+ # * +proxy_target+ - Returns the associated object for +belongs_to+ and +has_one+, or
420
+ # the collection of associated objects for +has_many+ and +has_and_belongs_to_many+.
421
+ #
422
+ # === Association Join Models
423
+ #
424
+ # Has Many associations can be configured with the <tt>:through</tt> option to use an
425
+ # explicit join model to retrieve the data. This operates similarly to a
426
+ # +has_and_belongs_to_many+ association. The advantage is that you're able to add validations,
427
+ # callbacks, and extra attributes on the join model. Consider the following schema:
428
+ #
429
+ # class Author < ActiveRecord::Base
430
+ # has_many :authorships
431
+ # has_many :books, :through => :authorships
432
+ # end
433
+ #
434
+ # class Authorship < ActiveRecord::Base
435
+ # belongs_to :author
436
+ # belongs_to :book
437
+ # end
438
+ #
439
+ # @author = Author.find :first
440
+ # @author.authorships.collect { |a| a.book } # selects all books that the author's authorships belong to
441
+ # @author.books # selects all books by using the Authorship join model
442
+ #
443
+ # You can also go through a +has_many+ association on the join model:
444
+ #
445
+ # class Firm < ActiveRecord::Base
446
+ # has_many :clients
447
+ # has_many :invoices, :through => :clients
448
+ # end
449
+ #
450
+ # class Client < ActiveRecord::Base
451
+ # belongs_to :firm
452
+ # has_many :invoices
453
+ # end
454
+ #
455
+ # class Invoice < ActiveRecord::Base
456
+ # belongs_to :client
457
+ # end
458
+ #
459
+ # @firm = Firm.find :first
460
+ # @firm.clients.collect { |c| c.invoices }.flatten # select all invoices for all clients of the firm
461
+ # @firm.invoices # selects all invoices by going through the Client join model
462
+ #
463
+ # Similarly you can go through a +has_one+ association on the join model:
464
+ #
465
+ # class Group < ActiveRecord::Base
466
+ # has_many :users
467
+ # has_many :avatars, :through => :users
468
+ # end
469
+ #
470
+ # class User < ActiveRecord::Base
471
+ # belongs_to :group
472
+ # has_one :avatar
473
+ # end
474
+ #
475
+ # class Avatar < ActiveRecord::Base
476
+ # belongs_to :user
477
+ # end
478
+ #
479
+ # @group = Group.first
480
+ # @group.users.collect { |u| u.avatar }.flatten # select all avatars for all users in the group
481
+ # @group.avatars # selects all avatars by going through the User join model.
482
+ #
483
+ # An important caveat with going through +has_one+ or +has_many+ associations on the
484
+ # join model is that these associations are *read-only*. For example, the following
485
+ # would not work following the previous example:
486
+ #
487
+ # @group.avatars << Avatar.new # this would work if User belonged_to Avatar rather than the other way around
488
+ # @group.avatars.delete(@group.avatars.last) # so would this
489
+ #
490
+ # === Polymorphic Associations
491
+ #
492
+ # Polymorphic associations on models are not restricted on what types of models they
493
+ # can be associated with. Rather, they specify an interface that a +has_many+ association
494
+ # must adhere to.
495
+ #
496
+ # class Asset < ActiveRecord::Base
497
+ # belongs_to :attachable, :polymorphic => true
498
+ # end
499
+ #
500
+ # class Post < ActiveRecord::Base
501
+ # has_many :assets, :as => :attachable # The :as option specifies the polymorphic interface to use.
502
+ # end
503
+ #
504
+ # @asset.attachable = @post
505
+ #
506
+ # This works by using a type column in addition to a foreign key to specify the associated
507
+ # record. In the Asset example, you'd need an +attachable_id+ integer column and an
508
+ # +attachable_type+ string column.
509
+ #
510
+ # Using polymorphic associations in combination with single table inheritance (STI) is
511
+ # a little tricky. In order for the associations to work as expected, ensure that you
512
+ # store the base model for the STI models in the type column of the polymorphic
513
+ # association. To continue with the asset example above, suppose there are guest posts
514
+ # and member posts that use the posts table for STI. In this case, there must be a +type+
515
+ # column in the posts table.
516
+ #
517
+ # class Asset < ActiveRecord::Base
518
+ # belongs_to :attachable, :polymorphic => true
519
+ #
520
+ # def attachable_type=(sType)
521
+ # super(sType.to_s.classify.constantize.base_class.to_s)
522
+ # end
523
+ # end
524
+ #
525
+ # class Post < ActiveRecord::Base
526
+ # # because we store "Post" in attachable_type now :dependent => :destroy will work
527
+ # has_many :assets, :as => :attachable, :dependent => :destroy
528
+ # end
529
+ #
530
+ # class GuestPost < Post
531
+ # end
532
+ #
533
+ # class MemberPost < Post
534
+ # end
535
+ #
536
+ # == Caching
537
+ #
538
+ # All of the methods are built on a simple caching principle that will keep the result
539
+ # of the last query around unless specifically instructed not to. The cache is even
540
+ # shared across methods to make it even cheaper to use the macro-added methods without
541
+ # worrying too much about performance at the first go.
542
+ #
543
+ # project.milestones # fetches milestones from the database
544
+ # project.milestones.size # uses the milestone cache
545
+ # project.milestones.empty? # uses the milestone cache
546
+ # project.milestones(true).size # fetches milestones from the database
547
+ # project.milestones # uses the milestone cache
548
+ #
549
+ # == Eager loading of associations
550
+ #
551
+ # Eager loading is a way to find objects of a certain class and a number of named associations.
552
+ # This is one of the easiest ways of to prevent the dreaded 1+N problem in which fetching 100
553
+ # posts that each need to display their author triggers 101 database queries. Through the
554
+ # use of eager loading, the 101 queries can be reduced to 2.
555
+ #
556
+ # class Post < ActiveRecord::Base
557
+ # belongs_to :author
558
+ # has_many :comments
559
+ # end
560
+ #
561
+ # Consider the following loop using the class above:
562
+ #
563
+ # for post in Post.all
564
+ # puts "Post: " + post.title
565
+ # puts "Written by: " + post.author.name
566
+ # puts "Last comment on: " + post.comments.first.created_on
567
+ # end
568
+ #
569
+ # To iterate over these one hundred posts, we'll generate 201 database queries. Let's
570
+ # first just optimize it for retrieving the author:
571
+ #
572
+ # for post in Post.find(:all, :include => :author)
573
+ #
574
+ # This references the name of the +belongs_to+ association that also used the <tt>:author</tt>
575
+ # symbol. After loading the posts, find will collect the +author_id+ from each one and load
576
+ # all the referenced authors with one query. Doing so will cut down the number of queries
577
+ # from 201 to 102.
578
+ #
579
+ # We can improve upon the situation further by referencing both associations in the finder with:
580
+ #
581
+ # for post in Post.find(:all, :include => [ :author, :comments ])
582
+ #
583
+ # This will load all comments with a single query. This reduces the total number of queries
584
+ # to 3. More generally the number of queries will be 1 plus the number of associations
585
+ # named (except if some of the associations are polymorphic +belongs_to+ - see below).
586
+ #
587
+ # To include a deep hierarchy of associations, use a hash:
588
+ #
589
+ # for post in Post.find(:all, :include => [ :author, { :comments => { :author => :gravatar } } ])
590
+ #
591
+ # That'll grab not only all the comments but all their authors and gravatar pictures.
592
+ # You can mix and match symbols, arrays and hashes in any combination to describe the
593
+ # associations you want to load.
594
+ #
595
+ # All of this power shouldn't fool you into thinking that you can pull out huge amounts
596
+ # of data with no performance penalty just because you've reduced the number of queries.
597
+ # The database still needs to send all the data to Active Record and it still needs to
598
+ # be processed. So it's no catch-all for performance problems, but it's a great way to
599
+ # cut down on the number of queries in a situation as the one described above.
600
+ #
601
+ # Since only one table is loaded at a time, conditions or orders cannot reference tables
602
+ # other than the main one. If this is the case Active Record falls back to the previously
603
+ # used LEFT OUTER JOIN based strategy. For example
604
+ #
605
+ # Post.includes([:author, :comments]).where(['comments.approved = ?', true]).all
606
+ #
607
+ # This will result in a single SQL query with joins along the lines of:
608
+ # <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt> and
609
+ # <tt>LEFT OUTER JOIN authors ON authors.id = posts.author_id</tt>. Note that using conditions
610
+ # like this can have unintended consequences.
611
+ # In the above example posts with no approved comments are not returned at all, because
612
+ # the conditions apply to the SQL statement as a whole and not just to the association.
613
+ # You must disambiguate column references for this fallback to happen, for example
614
+ # <tt>:order => "author.name DESC"</tt> will work but <tt>:order => "name DESC"</tt> will not.
615
+ #
616
+ # If you do want eager load only some members of an association it is usually more natural
617
+ # to <tt>:include</tt> an association which has conditions defined on it:
618
+ #
619
+ # class Post < ActiveRecord::Base
620
+ # has_many :approved_comments, :class_name => 'Comment', :conditions => ['approved = ?', true]
621
+ # end
622
+ #
623
+ # Post.find(:all, :include => :approved_comments)
624
+ #
625
+ # This will load posts and eager load the +approved_comments+ association, which contains
626
+ # only those comments that have been approved.
627
+ #
628
+ # If you eager load an association with a specified <tt>:limit</tt> option, it will be ignored,
629
+ # returning all the associated objects:
630
+ #
631
+ # class Picture < ActiveRecord::Base
632
+ # has_many :most_recent_comments, :class_name => 'Comment', :order => 'id DESC', :limit => 10
633
+ # end
634
+ #
635
+ # Picture.find(:first, :include => :most_recent_comments).most_recent_comments # => returns all associated comments.
636
+ #
637
+ # When eager loaded, conditions are interpolated in the context of the model class, not
638
+ # the model instance. Conditions are lazily interpolated before the actual model exists.
639
+ #
640
+ # Eager loading is supported with polymorphic associations.
641
+ #
642
+ # class Address < ActiveRecord::Base
643
+ # belongs_to :addressable, :polymorphic => true
644
+ # end
645
+ #
646
+ # A call that tries to eager load the addressable model
647
+ #
648
+ # Address.find(:all, :include => :addressable)
649
+ #
650
+ # This will execute one query to load the addresses and load the addressables with one
651
+ # query per addressable type.
652
+ # For example if all the addressables are either of class Person or Company then a total
653
+ # of 3 queries will be executed. The list of addressable types to load is determined on
654
+ # the back of the addresses loaded. This is not supported if Active Record has to fallback
655
+ # to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError.
656
+ # The reason is that the parent model's type is a column value so its corresponding table
657
+ # name cannot be put in the +FROM+/+JOIN+ clauses of that query.
658
+ #
659
+ # == Table Aliasing
660
+ #
661
+ # Active Record uses table aliasing in the case that a table is referenced multiple times
662
+ # in a join. If a table is referenced only once, the standard table name is used. The
663
+ # second time, the table is aliased as <tt>#{reflection_name}_#{parent_table_name}</tt>.
664
+ # Indexes are appended for any more successive uses of the table name.
665
+ #
666
+ # Post.find :all, :joins => :comments
667
+ # # => SELECT ... FROM posts INNER JOIN comments ON ...
668
+ # Post.find :all, :joins => :special_comments # STI
669
+ # # => SELECT ... FROM posts INNER JOIN comments ON ... AND comments.type = 'SpecialComment'
670
+ # Post.find :all, :joins => [:comments, :special_comments] # special_comments is the reflection name, posts is the parent table name
671
+ # # => SELECT ... FROM posts INNER JOIN comments ON ... INNER JOIN comments special_comments_posts
672
+ #
673
+ # Acts as tree example:
674
+ #
675
+ # TreeMixin.find :all, :joins => :children
676
+ # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
677
+ # TreeMixin.find :all, :joins => {:children => :parent}
678
+ # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
679
+ # INNER JOIN parents_mixins ...
680
+ # TreeMixin.find :all, :joins => {:children => {:parent => :children}}
681
+ # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
682
+ # INNER JOIN parents_mixins ...
683
+ # INNER JOIN mixins childrens_mixins_2
684
+ #
685
+ # Has and Belongs to Many join tables use the same idea, but add a <tt>_join</tt> suffix:
686
+ #
687
+ # Post.find :all, :joins => :categories
688
+ # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
689
+ # Post.find :all, :joins => {:categories => :posts}
690
+ # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
691
+ # INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
692
+ # Post.find :all, :joins => {:categories => {:posts => :categories}}
693
+ # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
694
+ # INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
695
+ # INNER JOIN categories_posts categories_posts_join INNER JOIN categories categories_posts_2
696
+ #
697
+ # If you wish to specify your own custom joins using a <tt>:joins</tt> option, those table
698
+ # names will take precedence over the eager associations:
699
+ #
700
+ # Post.find :all, :joins => :comments, :joins => "inner join comments ..."
701
+ # # => SELECT ... FROM posts INNER JOIN comments_posts ON ... INNER JOIN comments ...
702
+ # Post.find :all, :joins => [:comments, :special_comments], :joins => "inner join comments ..."
703
+ # # => SELECT ... FROM posts INNER JOIN comments comments_posts ON ...
704
+ # INNER JOIN comments special_comments_posts ...
705
+ # INNER JOIN comments ...
706
+ #
707
+ # Table aliases are automatically truncated according to the maximum length of table identifiers
708
+ # according to the specific database.
709
+ #
710
+ # == Modules
711
+ #
712
+ # By default, associations will look for objects within the current module scope. Consider:
713
+ #
714
+ # module MyApplication
715
+ # module Business
716
+ # class Firm < ActiveRecord::Base
717
+ # has_many :clients
718
+ # end
719
+ #
720
+ # class Client < ActiveRecord::Base; end
721
+ # end
722
+ # end
723
+ #
724
+ # When <tt>Firm#clients</tt> is called, it will in turn call
725
+ # <tt>MyApplication::Business::Client.find_all_by_firm_id(firm.id)</tt>.
726
+ # If you want to associate with a class in another module scope, this can be done by
727
+ # specifying the complete class name.
728
+ #
729
+ # module MyApplication
730
+ # module Business
731
+ # class Firm < ActiveRecord::Base; end
732
+ # end
733
+ #
734
+ # module Billing
735
+ # class Account < ActiveRecord::Base
736
+ # belongs_to :firm, :class_name => "MyApplication::Business::Firm"
737
+ # end
738
+ # end
739
+ # end
740
+ #
741
+ # == Bi-directional associations
742
+ #
743
+ # When you specify an association there is usually an association on the associated model
744
+ # that specifies the same relationship in reverse. For example, with the following models:
745
+ #
746
+ # class Dungeon < ActiveRecord::Base
747
+ # has_many :traps
748
+ # has_one :evil_wizard
749
+ # end
750
+ #
751
+ # class Trap < ActiveRecord::Base
752
+ # belongs_to :dungeon
753
+ # end
754
+ #
755
+ # class EvilWizard < ActiveRecord::Base
756
+ # belongs_to :dungeon
757
+ # end
758
+ #
759
+ # The +traps+ association on +Dungeon+ and the the +dungeon+ association on +Trap+ are
760
+ # the inverse of each other and the inverse of the +dungeon+ association on +EvilWizard+
761
+ # is the +evil_wizard+ association on +Dungeon+ (and vice-versa). By default,
762
+ # Active Record doesn't know anything about these inverse relationships and so no object
763
+ # loading optimisation is possible. For example:
764
+ #
765
+ # d = Dungeon.first
766
+ # t = d.traps.first
767
+ # d.level == t.dungeon.level # => true
768
+ # d.level = 10
769
+ # d.level == t.dungeon.level # => false
770
+ #
771
+ # The +Dungeon+ instances +d+ and <tt>t.dungeon</tt> in the above example refer to
772
+ # the same object data from the database, but are actually different in-memory copies
773
+ # of that data. Specifying the <tt>:inverse_of</tt> option on associations lets you tell
774
+ # Active Record about inverse relationships and it will optimise object loading. For
775
+ # example, if we changed our model definitions to:
776
+ #
777
+ # class Dungeon < ActiveRecord::Base
778
+ # has_many :traps, :inverse_of => :dungeon
779
+ # has_one :evil_wizard, :inverse_of => :dungeon
780
+ # end
781
+ #
782
+ # class Trap < ActiveRecord::Base
783
+ # belongs_to :dungeon, :inverse_of => :traps
784
+ # end
785
+ #
786
+ # class EvilWizard < ActiveRecord::Base
787
+ # belongs_to :dungeon, :inverse_of => :evil_wizard
788
+ # end
789
+ #
790
+ # Then, from our code snippet above, +d+ and <tt>t.dungeon</tt> are actually the same
791
+ # in-memory instance and our final <tt>d.level == t.dungeon.level</tt> will return +true+.
792
+ #
793
+ # There are limitations to <tt>:inverse_of</tt> support:
794
+ #
795
+ # * does not work with <tt>:through</tt> associations.
796
+ # * does not work with <tt>:polymorphic</tt> associations.
797
+ # * for +belongs_to+ associations +has_many+ inverse associations are ignored.
798
+ #
799
+ # == Type safety with <tt>ActiveRecord::AssociationTypeMismatch</tt>
800
+ #
801
+ # If you attempt to assign an object to an association that doesn't match the inferred
802
+ # or specified <tt>:class_name</tt>, you'll get an <tt>ActiveRecord::AssociationTypeMismatch</tt>.
803
+ #
804
+ # == Options
805
+ #
806
+ # All of the association macros can be specialized through options. This makes cases
807
+ # more complex than the simple and guessable ones possible.
808
+ module ClassMethods
809
+ # Specifies a one-to-many association. The following methods for retrieval and query of
810
+ # collections of associated objects will be added:
811
+ #
812
+ # [collection(force_reload = false)]
813
+ # Returns an array of all the associated objects.
814
+ # An empty array is returned if none are found.
815
+ # [collection<<(object, ...)]
816
+ # Adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
817
+ # Note that this operation instantly fires update sql without waiting for the save or update call on the
818
+ # parent object.
819
+ # [collection.delete(object, ...)]
820
+ # Removes one or more objects from the collection by setting their foreign keys to +NULL+.
821
+ # Objects will be in addition destroyed if they're associated with <tt>:dependent => :destroy</tt>,
822
+ # and deleted if they're associated with <tt>:dependent => :delete_all</tt>.
823
+ # [collection=objects]
824
+ # Replaces the collections content by deleting and adding objects as appropriate. If the <tt>:through</tt>
825
+ # option is true callbacks in the join models are triggered except destroy callbacks, since deletion is
826
+ # direct.
827
+ # [collection_singular_ids]
828
+ # Returns an array of the associated objects' ids
829
+ # [collection_singular_ids=ids]
830
+ # Replace the collection with the objects identified by the primary keys in +ids+. This
831
+ # method loads the models and calls <tt>collection=</tt>. See above.
832
+ # [collection.clear]
833
+ # Removes every object from the collection. This destroys the associated objects if they
834
+ # are associated with <tt>:dependent => :destroy</tt>, deletes them directly from the
835
+ # database if <tt>:dependent => :delete_all</tt>, otherwise sets their foreign keys to +NULL+.
836
+ # If the <tt>:through</tt> option is true no destroy callbacks are invoked on the join models.
837
+ # Join models are directly deleted.
838
+ # [collection.empty?]
839
+ # Returns +true+ if there are no associated objects.
840
+ # [collection.size]
841
+ # Returns the number of associated objects.
842
+ # [collection.find(...)]
843
+ # Finds an associated object according to the same rules as ActiveRecord::Base.find.
844
+ # [collection.exists?(...)]
845
+ # Checks whether an associated object with the given conditions exists.
846
+ # Uses the same rules as ActiveRecord::Base.exists?.
847
+ # [collection.build(attributes = {}, ...)]
848
+ # Returns one or more new objects of the collection type that have been instantiated
849
+ # with +attributes+ and linked to this object through a foreign key, but have not yet
850
+ # been saved.
851
+ # [collection.create(attributes = {})]
852
+ # Returns a new object of the collection type that has been instantiated
853
+ # with +attributes+, linked to this object through a foreign key, and that has already
854
+ # been saved (if it passed the validation). *Note*: This only works if the base model
855
+ # already exists in the DB, not if it is a new (unsaved) record!
856
+ #
857
+ # (*Note*: +collection+ is replaced with the symbol passed as the first argument, so
858
+ # <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.)
859
+ #
860
+ # === Example
861
+ #
862
+ # Example: A Firm class declares <tt>has_many :clients</tt>, which will add:
863
+ # * <tt>Firm#clients</tt> (similar to <tt>Clients.find :all, :conditions => ["firm_id = ?", id]</tt>)
864
+ # * <tt>Firm#clients<<</tt>
865
+ # * <tt>Firm#clients.delete</tt>
866
+ # * <tt>Firm#clients=</tt>
867
+ # * <tt>Firm#client_ids</tt>
868
+ # * <tt>Firm#client_ids=</tt>
869
+ # * <tt>Firm#clients.clear</tt>
870
+ # * <tt>Firm#clients.empty?</tt> (similar to <tt>firm.clients.size == 0</tt>)
871
+ # * <tt>Firm#clients.size</tt> (similar to <tt>Client.count "firm_id = #{id}"</tt>)
872
+ # * <tt>Firm#clients.find</tt> (similar to <tt>Client.find(id, :conditions => "firm_id = #{id}")</tt>)
873
+ # * <tt>Firm#clients.exists?(:name => 'ACME')</tt> (similar to <tt>Client.exists?(:name => 'ACME', :firm_id => firm.id)</tt>)
874
+ # * <tt>Firm#clients.build</tt> (similar to <tt>Client.new("firm_id" => id)</tt>)
875
+ # * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>)
876
+ # The declaration can also include an options hash to specialize the behavior of the association.
877
+ #
878
+ # === Supported options
879
+ # [:class_name]
880
+ # Specify the class name of the association. Use it only if that name can't be inferred
881
+ # from the association name. So <tt>has_many :products</tt> will by default be linked
882
+ # to the Product class, but if the real class name is SpecialProduct, you'll have to
883
+ # specify it with this option.
884
+ # [:conditions]
885
+ # Specify the conditions that the associated objects must meet in order to be included as a +WHERE+
886
+ # SQL fragment, such as <tt>price > 5 AND name LIKE 'B%'</tt>. Record creations from
887
+ # the association are scoped if a hash is used.
888
+ # <tt>has_many :posts, :conditions => {:published => true}</tt> will create published
889
+ # posts with <tt>@blog.posts.create</tt> or <tt>@blog.posts.build</tt>.
890
+ # [:order]
891
+ # Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
892
+ # such as <tt>last_name, first_name DESC</tt>.
893
+ # [:foreign_key]
894
+ # Specify the foreign key used for the association. By default this is guessed to be the name
895
+ # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_many+
896
+ # association will use "person_id" as the default <tt>:foreign_key</tt>.
897
+ # [:primary_key]
898
+ # Specify the method that returns the primary key used for the association. By default this is +id+.
899
+ # [:dependent]
900
+ # If set to <tt>:destroy</tt> all the associated objects are destroyed
901
+ # alongside this object by calling their +destroy+ method. If set to <tt>:delete_all</tt> all associated
902
+ # objects are deleted *without* calling their +destroy+ method. If set to <tt>:nullify</tt> all associated
903
+ # objects' foreign keys are set to +NULL+ *without* calling their +save+ callbacks. If set to
904
+ # <tt>:restrict</tt> this object cannot be deleted if it has any associated object.
905
+ #
906
+ # *Warning:* This option is ignored when used with <tt>:through</tt> option.
907
+ #
908
+ # [:finder_sql]
909
+ # Specify a complete SQL statement to fetch the association. This is a good way to go for complex
910
+ # associations that depend on multiple tables. Note: When this option is used, +find_in_collection+
911
+ # is _not_ added.
912
+ # [:counter_sql]
913
+ # Specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is
914
+ # specified but not <tt>:counter_sql</tt>, <tt>:counter_sql</tt> will be generated by
915
+ # replacing <tt>SELECT ... FROM</tt> with <tt>SELECT COUNT(*) FROM</tt>.
916
+ # [:extend]
917
+ # Specify a named module for extending the proxy. See "Association extensions".
918
+ # [:include]
919
+ # Specify second-order associations that should be eager loaded when the collection is loaded.
920
+ # [:group]
921
+ # An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
922
+ # [:having]
923
+ # Combined with +:group+ this can be used to filter the records that a <tt>GROUP BY</tt>
924
+ # returns. Uses the <tt>HAVING</tt> SQL-clause.
925
+ # [:limit]
926
+ # An integer determining the limit on the number of rows that should be returned.
927
+ # [:offset]
928
+ # An integer determining the offset from where the rows should be fetched. So at 5,
929
+ # it would skip the first 4 rows.
930
+ # [:select]
931
+ # By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if
932
+ # you, for example, want to do a join but not include the joined columns. Do not forget
933
+ # to include the primary and foreign keys, otherwise it will raise an error.
934
+ # [:as]
935
+ # Specifies a polymorphic interface (See <tt>belongs_to</tt>).
936
+ # [:through]
937
+ # Specifies a join model through which to perform the query. Options for <tt>:class_name</tt>
938
+ # and <tt>:foreign_key</tt> are ignored, as the association uses the source reflection. You
939
+ # can only use a <tt>:through</tt> query through a <tt>belongs_to</tt>, <tt>has_one</tt>
940
+ # or <tt>has_many</tt> association on the join model. The collection of join models
941
+ # can be managed via the collection API. For example, new join models are created for
942
+ # newly associated objects, and if some are gone their rows are deleted (directly,
943
+ # no destroy callbacks are triggered).
944
+ # [:source]
945
+ # Specifies the source association name used by <tt>has_many :through</tt> queries.
946
+ # Only use it if the name cannot be inferred from the association.
947
+ # <tt>has_many :subscribers, :through => :subscriptions</tt> will look for either <tt>:subscribers</tt> or
948
+ # <tt>:subscriber</tt> on Subscription, unless a <tt>:source</tt> is given.
949
+ # [:source_type]
950
+ # Specifies type of the source association used by <tt>has_many :through</tt> queries where the source
951
+ # association is a polymorphic +belongs_to+.
952
+ # [:uniq]
953
+ # If true, duplicates will be omitted from the collection. Useful in conjunction with <tt>:through</tt>.
954
+ # [:readonly]
955
+ # If true, all the associated objects are readonly through the association.
956
+ # [:validate]
957
+ # If +false+, don't validate the associated objects when saving the parent object. true by default.
958
+ # [:autosave]
959
+ # If true, always save the associated objects or destroy them if marked for destruction,
960
+ # when saving the parent object. If false, never save or destroy the associated objects.
961
+ # By default, only save associated objects that are new records.
962
+ # [:inverse_of]
963
+ # Specifies the name of the <tt>belongs_to</tt> association on the associated object
964
+ # that is the inverse of this <tt>has_many</tt> association. Does not work in combination
965
+ # with <tt>:through</tt> or <tt>:as</tt> options.
966
+ # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
967
+ #
968
+ # Option examples:
969
+ # has_many :comments, :order => "posted_on"
970
+ # has_many :comments, :include => :author
971
+ # has_many :people, :class_name => "Person", :conditions => "deleted = 0", :order => "name"
972
+ # has_many :tracks, :order => "position", :dependent => :destroy
973
+ # has_many :comments, :dependent => :nullify
974
+ # has_many :tags, :as => :taggable
975
+ # has_many :reports, :readonly => true
976
+ # has_many :subscribers, :through => :subscriptions, :source => :user
977
+ # has_many :subscribers, :class_name => "Person", :finder_sql =>
978
+ # 'SELECT DISTINCT people.* ' +
979
+ # 'FROM people p, post_subscriptions ps ' +
980
+ # 'WHERE ps.post_id = #{id} AND ps.person_id = p.id ' +
981
+ # 'ORDER BY p.first_name'
982
+ def has_many(association_id, options = {}, &extension)
983
+ reflection = create_has_many_reflection(association_id, options, &extension)
984
+ configure_dependency_for_has_many(reflection)
985
+ add_association_callbacks(reflection.name, reflection.options)
986
+
987
+ if options[:through]
988
+ collection_accessor_methods(reflection, HasManyThroughAssociation)
989
+ else
990
+ collection_accessor_methods(reflection, HasManyAssociation)
991
+ end
992
+ end
993
+
994
+ # Specifies a one-to-one association with another class. This method should only be used
995
+ # if the other class contains the foreign key. If the current class contains the foreign key,
996
+ # then you should use +belongs_to+ instead. See also ActiveRecord::Associations::ClassMethods's overview
997
+ # on when to use has_one and when to use belongs_to.
998
+ #
999
+ # The following methods for retrieval and query of a single associated object will be added:
1000
+ #
1001
+ # [association(force_reload = false)]
1002
+ # Returns the associated object. +nil+ is returned if none is found.
1003
+ # [association=(associate)]
1004
+ # Assigns the associate object, extracts the primary key, sets it as the foreign key,
1005
+ # and saves the associate object.
1006
+ # [build_association(attributes = {})]
1007
+ # Returns a new object of the associated type that has been instantiated
1008
+ # with +attributes+ and linked to this object through a foreign key, but has not
1009
+ # yet been saved. <b>Note:</b> This ONLY works if an association already exists.
1010
+ # It will NOT work if the association is +nil+.
1011
+ # [create_association(attributes = {})]
1012
+ # Returns a new object of the associated type that has been instantiated
1013
+ # with +attributes+, linked to this object through a foreign key, and that
1014
+ # has already been saved (if it passed the validation).
1015
+ #
1016
+ # (+association+ is replaced with the symbol passed as the first argument, so
1017
+ # <tt>has_one :manager</tt> would add among others <tt>manager.nil?</tt>.)
1018
+ #
1019
+ # === Example
1020
+ #
1021
+ # An Account class declares <tt>has_one :beneficiary</tt>, which will add:
1022
+ # * <tt>Account#beneficiary</tt> (similar to <tt>Beneficiary.find(:first, :conditions => "account_id = #{id}")</tt>)
1023
+ # * <tt>Account#beneficiary=(beneficiary)</tt> (similar to <tt>beneficiary.account_id = account.id; beneficiary.save</tt>)
1024
+ # * <tt>Account#build_beneficiary</tt> (similar to <tt>Beneficiary.new("account_id" => id)</tt>)
1025
+ # * <tt>Account#create_beneficiary</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save; b</tt>)
1026
+ #
1027
+ # === Options
1028
+ #
1029
+ # The declaration can also include an options hash to specialize the behavior of the association.
1030
+ #
1031
+ # Options are:
1032
+ # [:class_name]
1033
+ # Specify the class name of the association. Use it only if that name can't be inferred
1034
+ # from the association name. So <tt>has_one :manager</tt> will by default be linked to the Manager class, but
1035
+ # if the real class name is Person, you'll have to specify it with this option.
1036
+ # [:conditions]
1037
+ # Specify the conditions that the associated object must meet in order to be included as a +WHERE+
1038
+ # SQL fragment, such as <tt>rank = 5</tt>. Record creation from the association is scoped if a hash
1039
+ # is used. <tt>has_one :account, :conditions => {:enabled => true}</tt> will create
1040
+ # an enabled account with <tt>@company.create_account</tt> or <tt>@company.build_account</tt>.
1041
+ # [:order]
1042
+ # Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
1043
+ # such as <tt>last_name, first_name DESC</tt>.
1044
+ # [:dependent]
1045
+ # If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
1046
+ # <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method.
1047
+ # If set to <tt>:nullify</tt>, the associated object's foreign key is set to +NULL+.
1048
+ # Also, association is assigned.
1049
+ # [:foreign_key]
1050
+ # Specify the foreign key used for the association. By default this is guessed to be the name
1051
+ # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_one+ association
1052
+ # will use "person_id" as the default <tt>:foreign_key</tt>.
1053
+ # [:primary_key]
1054
+ # Specify the method that returns the primary key used for the association. By default this is +id+.
1055
+ # [:include]
1056
+ # Specify second-order associations that should be eager loaded when this object is loaded.
1057
+ # [:as]
1058
+ # Specifies a polymorphic interface (See <tt>belongs_to</tt>).
1059
+ # [:select]
1060
+ # By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example,
1061
+ # you want to do a join but not include the joined columns. Do not forget to include the
1062
+ # primary and foreign keys, otherwise it will raise an error.
1063
+ # [:through]
1064
+ # Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt>
1065
+ # and <tt>:foreign_key</tt> are ignored, as the association uses the source reflection. You
1066
+ # can only use a <tt>:through</tt> query through a <tt>has_one</tt> or <tt>belongs_to</tt>
1067
+ # association on the join model.
1068
+ # [:source]
1069
+ # Specifies the source association name used by <tt>has_one :through</tt> queries.
1070
+ # Only use it if the name cannot be inferred from the association.
1071
+ # <tt>has_one :favorite, :through => :favorites</tt> will look for a
1072
+ # <tt>:favorite</tt> on Favorite, unless a <tt>:source</tt> is given.
1073
+ # [:source_type]
1074
+ # Specifies type of the source association used by <tt>has_one :through</tt> queries where the source
1075
+ # association is a polymorphic +belongs_to+.
1076
+ # [:readonly]
1077
+ # If true, the associated object is readonly through the association.
1078
+ # [:validate]
1079
+ # If +false+, don't validate the associated object when saving the parent object. +false+ by default.
1080
+ # [:autosave]
1081
+ # If true, always save the associated object or destroy it if marked for destruction,
1082
+ # when saving the parent object. If false, never save or destroy the associated object.
1083
+ # By default, only save the associated object if it's a new record.
1084
+ # [:inverse_of]
1085
+ # Specifies the name of the <tt>belongs_to</tt> association on the associated object
1086
+ # that is the inverse of this <tt>has_one</tt> association. Does not work in combination
1087
+ # with <tt>:through</tt> or <tt>:as</tt> options.
1088
+ # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
1089
+ #
1090
+ # Option examples:
1091
+ # has_one :credit_card, :dependent => :destroy # destroys the associated credit card
1092
+ # has_one :credit_card, :dependent => :nullify # updates the associated records foreign
1093
+ # # key value to NULL rather than destroying it
1094
+ # has_one :last_comment, :class_name => "Comment", :order => "posted_on"
1095
+ # has_one :project_manager, :class_name => "Person", :conditions => "role = 'project_manager'"
1096
+ # has_one :attachment, :as => :attachable
1097
+ # has_one :boss, :readonly => :true
1098
+ # has_one :club, :through => :membership
1099
+ # has_one :primary_address, :through => :addressables, :conditions => ["addressable.primary = ?", true], :source => :addressable
1100
+ def has_one(association_id, options = {})
1101
+ if options[:through]
1102
+ reflection = create_has_one_through_reflection(association_id, options)
1103
+ association_accessor_methods(reflection, ActiveRecord::Associations::HasOneThroughAssociation)
1104
+ else
1105
+ reflection = create_has_one_reflection(association_id, options)
1106
+ association_accessor_methods(reflection, HasOneAssociation)
1107
+ association_constructor_method(:build, reflection, HasOneAssociation)
1108
+ association_constructor_method(:create, reflection, HasOneAssociation)
1109
+ configure_dependency_for_has_one(reflection)
1110
+ end
1111
+ end
1112
+
1113
+ # Specifies a one-to-one association with another class. This method should only be used
1114
+ # if this class contains the foreign key. If the other class contains the foreign key,
1115
+ # then you should use +has_one+ instead. See also ActiveRecord::Associations::ClassMethods's overview
1116
+ # on when to use +has_one+ and when to use +belongs_to+.
1117
+ #
1118
+ # Methods will be added for retrieval and query for a single associated object, for which
1119
+ # this object holds an id:
1120
+ #
1121
+ # [association(force_reload = false)]
1122
+ # Returns the associated object. +nil+ is returned if none is found.
1123
+ # [association=(associate)]
1124
+ # Assigns the associate object, extracts the primary key, and sets it as the foreign key.
1125
+ # [build_association(attributes = {})]
1126
+ # Returns a new object of the associated type that has been instantiated
1127
+ # with +attributes+ and linked to this object through a foreign key, but has not yet been saved.
1128
+ # [create_association(attributes = {})]
1129
+ # Returns a new object of the associated type that has been instantiated
1130
+ # with +attributes+, linked to this object through a foreign key, and that
1131
+ # has already been saved (if it passed the validation).
1132
+ #
1133
+ # (+association+ is replaced with the symbol passed as the first argument, so
1134
+ # <tt>belongs_to :author</tt> would add among others <tt>author.nil?</tt>.)
1135
+ #
1136
+ # === Example
1137
+ #
1138
+ # A Post class declares <tt>belongs_to :author</tt>, which will add:
1139
+ # * <tt>Post#author</tt> (similar to <tt>Author.find(author_id)</tt>)
1140
+ # * <tt>Post#author=(author)</tt> (similar to <tt>post.author_id = author.id</tt>)
1141
+ # * <tt>Post#build_author</tt> (similar to <tt>post.author = Author.new</tt>)
1142
+ # * <tt>Post#create_author</tt> (similar to <tt>post.author = Author.new; post.author.save; post.author</tt>)
1143
+ # The declaration can also include an options hash to specialize the behavior of the association.
1144
+ #
1145
+ # === Options
1146
+ #
1147
+ # [:class_name]
1148
+ # Specify the class name of the association. Use it only if that name can't be inferred
1149
+ # from the association name. So <tt>has_one :author</tt> will by default be linked to the Author class, but
1150
+ # if the real class name is Person, you'll have to specify it with this option.
1151
+ # [:conditions]
1152
+ # Specify the conditions that the associated object must meet in order to be included as a +WHERE+
1153
+ # SQL fragment, such as <tt>authorized = 1</tt>.
1154
+ # [:select]
1155
+ # By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed
1156
+ # if, for example, you want to do a join but not include the joined columns. Do not
1157
+ # forget to include the primary and foreign keys, otherwise it will raise an error.
1158
+ # [:foreign_key]
1159
+ # Specify the foreign key used for the association. By default this is guessed to be the name
1160
+ # of the association with an "_id" suffix. So a class that defines a <tt>belongs_to :person</tt>
1161
+ # association will use "person_id" as the default <tt>:foreign_key</tt>. Similarly,
1162
+ # <tt>belongs_to :favorite_person, :class_name => "Person"</tt> will use a foreign key
1163
+ # of "favorite_person_id".
1164
+ # [:primary_key]
1165
+ # Specify the method that returns the primary key of associated object used for the association.
1166
+ # By default this is id.
1167
+ # [:dependent]
1168
+ # If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
1169
+ # <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method.
1170
+ # This option should not be specified when <tt>belongs_to</tt> is used in conjunction with
1171
+ # a <tt>has_many</tt> relationship on another class because of the potential to leave
1172
+ # orphaned records behind.
1173
+ # [:counter_cache]
1174
+ # Caches the number of belonging objects on the associate class through the use of +increment_counter+
1175
+ # and +decrement_counter+. The counter cache is incremented when an object of this
1176
+ # class is created and decremented when it's destroyed. This requires that a column
1177
+ # named <tt>#{table_name}_count</tt> (such as +comments_count+ for a belonging Comment class)
1178
+ # is used on the associate class (such as a Post class). You can also specify a custom counter
1179
+ # cache column by providing a column name instead of a +true+/+false+ value to this
1180
+ # option (e.g., <tt>:counter_cache => :my_custom_counter</tt>.)
1181
+ # Note: Specifying a counter cache will add it to that model's list of readonly attributes
1182
+ # using +attr_readonly+.
1183
+ # [:include]
1184
+ # Specify second-order associations that should be eager loaded when this object is loaded.
1185
+ # [:polymorphic]
1186
+ # Specify this association is a polymorphic association by passing +true+.
1187
+ # Note: If you've enabled the counter cache, then you may want to add the counter cache attribute
1188
+ # to the +attr_readonly+ list in the associated classes (e.g. <tt>class Post; attr_readonly :comments_count; end</tt>).
1189
+ # [:readonly]
1190
+ # If true, the associated object is readonly through the association.
1191
+ # [:validate]
1192
+ # If +false+, don't validate the associated objects when saving the parent object. +false+ by default.
1193
+ # [:autosave]
1194
+ # If true, always save the associated object or destroy it if marked for destruction, when
1195
+ # saving the parent object.
1196
+ # If false, never save or destroy the associated object.
1197
+ # By default, only save the associated object if it's a new record.
1198
+ # [:touch]
1199
+ # If true, the associated object will be touched (the updated_at/on attributes set to now)
1200
+ # when this record is either saved or destroyed. If you specify a symbol, that attribute
1201
+ # will be updated with the current time instead of the updated_at/on attribute.
1202
+ # [:inverse_of]
1203
+ # Specifies the name of the <tt>has_one</tt> or <tt>has_many</tt> association on the associated
1204
+ # object that is the inverse of this <tt>belongs_to</tt> association. Does not work in
1205
+ # combination with the <tt>:polymorphic</tt> options.
1206
+ # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
1207
+ #
1208
+ # Option examples:
1209
+ # belongs_to :firm, :foreign_key => "client_of"
1210
+ # belongs_to :person, :primary_key => "name", :foreign_key => "person_name"
1211
+ # belongs_to :author, :class_name => "Person", :foreign_key => "author_id"
1212
+ # belongs_to :valid_coupon, :class_name => "Coupon", :foreign_key => "coupon_id",
1213
+ # :conditions => 'discounts > #{payments_count}'
1214
+ # belongs_to :attachable, :polymorphic => true
1215
+ # belongs_to :project, :readonly => true
1216
+ # belongs_to :post, :counter_cache => true
1217
+ # belongs_to :company, :touch => true
1218
+ # belongs_to :company, :touch => :employees_last_updated_at
1219
+ def belongs_to(association_id, options = {})
1220
+ reflection = create_belongs_to_reflection(association_id, options)
1221
+
1222
+ if reflection.options[:polymorphic]
1223
+ association_accessor_methods(reflection, BelongsToPolymorphicAssociation)
1224
+ else
1225
+ association_accessor_methods(reflection, BelongsToAssociation)
1226
+ association_constructor_method(:build, reflection, BelongsToAssociation)
1227
+ association_constructor_method(:create, reflection, BelongsToAssociation)
1228
+ end
1229
+
1230
+ add_counter_cache_callbacks(reflection) if options[:counter_cache]
1231
+ add_touch_callbacks(reflection, options[:touch]) if options[:touch]
1232
+
1233
+ configure_dependency_for_belongs_to(reflection)
1234
+ end
1235
+
1236
+ # Specifies a many-to-many relationship with another class. This associates two classes via an
1237
+ # intermediate join table. Unless the join table is explicitly specified as an option, it is
1238
+ # guessed using the lexical order of the class names. So a join between Developer and Project
1239
+ # will give the default join table name of "developers_projects" because "D" outranks "P".
1240
+ # Note that this precedence is calculated using the <tt><</tt> operator for String. This
1241
+ # means that if the strings are of different lengths, and the strings are equal when compared
1242
+ # up to the shortest length, then the longer string is considered of higher
1243
+ # lexical precedence than the shorter one. For example, one would expect the tables "paper_boxes" and "papers"
1244
+ # to generate a join table name of "papers_paper_boxes" because of the length of the name "paper_boxes",
1245
+ # but it in fact generates a join table name of "paper_boxes_papers". Be aware of this caveat, and use the
1246
+ # custom <tt>:join_table</tt> option if you need to.
1247
+ #
1248
+ # The join table should not have a primary key or a model associated with it. You must manually generate the
1249
+ # join table with a migration such as this:
1250
+ #
1251
+ # class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration
1252
+ # def self.up
1253
+ # create_table :developers_projects, :id => false do |t|
1254
+ # t.integer :developer_id
1255
+ # t.integer :project_id
1256
+ # end
1257
+ # end
1258
+ #
1259
+ # def self.down
1260
+ # drop_table :developers_projects
1261
+ # end
1262
+ # end
1263
+ #
1264
+ # Deprecated: Any additional fields added to the join table will be placed as attributes when
1265
+ # pulling records out through +has_and_belongs_to_many+ associations. Records returned from join
1266
+ # tables with additional attributes will be marked as readonly (because we can't save changes
1267
+ # to the additional attributes). It's strongly recommended that you upgrade any
1268
+ # associations with attributes to a real join model (see introduction).
1269
+ #
1270
+ # Adds the following methods for retrieval and query:
1271
+ #
1272
+ # [collection(force_reload = false)]
1273
+ # Returns an array of all the associated objects.
1274
+ # An empty array is returned if none are found.
1275
+ # [collection<<(object, ...)]
1276
+ # Adds one or more objects to the collection by creating associations in the join table
1277
+ # (<tt>collection.push</tt> and <tt>collection.concat</tt> are aliases to this method).
1278
+ # Note that this operation instantly fires update sql without waiting for the save or update call on the
1279
+ # parent object.
1280
+ # [collection.delete(object, ...)]
1281
+ # Removes one or more objects from the collection by removing their associations from the join table.
1282
+ # This does not destroy the objects.
1283
+ # [collection=objects]
1284
+ # Replaces the collection's content by deleting and adding objects as appropriate.
1285
+ # [collection_singular_ids]
1286
+ # Returns an array of the associated objects' ids.
1287
+ # [collection_singular_ids=ids]
1288
+ # Replace the collection by the objects identified by the primary keys in +ids+.
1289
+ # [collection.clear]
1290
+ # Removes every object from the collection. This does not destroy the objects.
1291
+ # [collection.empty?]
1292
+ # Returns +true+ if there are no associated objects.
1293
+ # [collection.size]
1294
+ # Returns the number of associated objects.
1295
+ # [collection.find(id)]
1296
+ # Finds an associated object responding to the +id+ and that
1297
+ # meets the condition that it has to be associated with this object.
1298
+ # Uses the same rules as ActiveRecord::Base.find.
1299
+ # [collection.exists?(...)]
1300
+ # Checks whether an associated object with the given conditions exists.
1301
+ # Uses the same rules as ActiveRecord::Base.exists?.
1302
+ # [collection.build(attributes = {})]
1303
+ # Returns a new object of the collection type that has been instantiated
1304
+ # with +attributes+ and linked to this object through the join table, but has not yet been saved.
1305
+ # [collection.create(attributes = {})]
1306
+ # Returns a new object of the collection type that has been instantiated
1307
+ # with +attributes+, linked to this object through the join table, and that has already been
1308
+ # saved (if it passed the validation).
1309
+ #
1310
+ # (+collection+ is replaced with the symbol passed as the first argument, so
1311
+ # <tt>has_and_belongs_to_many :categories</tt> would add among others <tt>categories.empty?</tt>.)
1312
+ #
1313
+ # === Example
1314
+ #
1315
+ # A Developer class declares <tt>has_and_belongs_to_many :projects</tt>, which will add:
1316
+ # * <tt>Developer#projects</tt>
1317
+ # * <tt>Developer#projects<<</tt>
1318
+ # * <tt>Developer#projects.delete</tt>
1319
+ # * <tt>Developer#projects=</tt>
1320
+ # * <tt>Developer#project_ids</tt>
1321
+ # * <tt>Developer#project_ids=</tt>
1322
+ # * <tt>Developer#projects.clear</tt>
1323
+ # * <tt>Developer#projects.empty?</tt>
1324
+ # * <tt>Developer#projects.size</tt>
1325
+ # * <tt>Developer#projects.find(id)</tt>
1326
+ # * <tt>Developer#projects.exists?(...)</tt>
1327
+ # * <tt>Developer#projects.build</tt> (similar to <tt>Project.new("project_id" => id)</tt>)
1328
+ # * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new("project_id" => id); c.save; c</tt>)
1329
+ # The declaration may include an options hash to specialize the behavior of the association.
1330
+ #
1331
+ # === Options
1332
+ #
1333
+ # [:class_name]
1334
+ # Specify the class name of the association. Use it only if that name can't be inferred
1335
+ # from the association name. So <tt>has_and_belongs_to_many :projects</tt> will by default be linked to the
1336
+ # Project class, but if the real class name is SuperProject, you'll have to specify it with this option.
1337
+ # [:join_table]
1338
+ # Specify the name of the join table if the default based on lexical order isn't what you want.
1339
+ # <b>WARNING:</b> If you're overwriting the table name of either class, the +table_name+ method
1340
+ # MUST be declared underneath any +has_and_belongs_to_many+ declaration in order to work.
1341
+ # [:foreign_key]
1342
+ # Specify the foreign key used for the association. By default this is guessed to be the name
1343
+ # of this class in lower-case and "_id" suffixed. So a Person class that makes
1344
+ # a +has_and_belongs_to_many+ association to Project will use "person_id" as the
1345
+ # default <tt>:foreign_key</tt>.
1346
+ # [:association_foreign_key]
1347
+ # Specify the foreign key used for the association on the receiving side of the association.
1348
+ # By default this is guessed to be the name of the associated class in lower-case and "_id" suffixed.
1349
+ # So if a Person class makes a +has_and_belongs_to_many+ association to Project,
1350
+ # the association will use "project_id" as the default <tt>:association_foreign_key</tt>.
1351
+ # [:conditions]
1352
+ # Specify the conditions that the associated object must meet in order to be included as a +WHERE+
1353
+ # SQL fragment, such as <tt>authorized = 1</tt>. Record creations from the association are
1354
+ # scoped if a hash is used.
1355
+ # <tt>has_many :posts, :conditions => {:published => true}</tt> will create published posts with <tt>@blog.posts.create</tt>
1356
+ # or <tt>@blog.posts.build</tt>.
1357
+ # [:order]
1358
+ # Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
1359
+ # such as <tt>last_name, first_name DESC</tt>
1360
+ # [:uniq]
1361
+ # If true, duplicate associated objects will be ignored by accessors and query methods.
1362
+ # [:finder_sql]
1363
+ # Overwrite the default generated SQL statement used to fetch the association with a manual statement
1364
+ # [:counter_sql]
1365
+ # Specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is
1366
+ # specified but not <tt>:counter_sql</tt>, <tt>:counter_sql</tt> will be generated by
1367
+ # replacing <tt>SELECT ... FROM</tt> with <tt>SELECT COUNT(*) FROM</tt>.
1368
+ # [:delete_sql]
1369
+ # Overwrite the default generated SQL statement used to remove links between the associated
1370
+ # classes with a manual statement.
1371
+ # [:insert_sql]
1372
+ # Overwrite the default generated SQL statement used to add links between the associated classes
1373
+ # with a manual statement.
1374
+ # [:extend]
1375
+ # Anonymous module for extending the proxy, see "Association extensions".
1376
+ # [:include]
1377
+ # Specify second-order associations that should be eager loaded when the collection is loaded.
1378
+ # [:group]
1379
+ # An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
1380
+ # [:having]
1381
+ # Combined with +:group+ this can be used to filter the records that a <tt>GROUP BY</tt> returns.
1382
+ # Uses the <tt>HAVING</tt> SQL-clause.
1383
+ # [:limit]
1384
+ # An integer determining the limit on the number of rows that should be returned.
1385
+ # [:offset]
1386
+ # An integer determining the offset from where the rows should be fetched. So at 5,
1387
+ # it would skip the first 4 rows.
1388
+ # [:select]
1389
+ # By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example,
1390
+ # you want to do a join but not include the joined columns. Do not forget to include the primary
1391
+ # and foreign keys, otherwise it will raise an error.
1392
+ # [:readonly]
1393
+ # If true, all the associated objects are readonly through the association.
1394
+ # [:validate]
1395
+ # If +false+, don't validate the associated objects when saving the parent object. +true+ by default.
1396
+ # [:autosave]
1397
+ # If true, always save the associated objects or destroy them if marked for destruction, when
1398
+ # saving the parent object.
1399
+ # If false, never save or destroy the associated objects.
1400
+ # By default, only save associated objects that are new records.
1401
+ #
1402
+ # Option examples:
1403
+ # has_and_belongs_to_many :projects
1404
+ # has_and_belongs_to_many :projects, :include => [ :milestones, :manager ]
1405
+ # has_and_belongs_to_many :nations, :class_name => "Country"
1406
+ # has_and_belongs_to_many :categories, :join_table => "prods_cats"
1407
+ # has_and_belongs_to_many :categories, :readonly => true
1408
+ # has_and_belongs_to_many :active_projects, :join_table => 'developers_projects', :delete_sql =>
1409
+ # 'DELETE FROM developers_projects WHERE active=1 AND developer_id = #{id} AND project_id = #{record.id}'
1410
+ def has_and_belongs_to_many(association_id, options = {}, &extension)
1411
+ reflection = create_has_and_belongs_to_many_reflection(association_id, options, &extension)
1412
+ collection_accessor_methods(reflection, HasAndBelongsToManyAssociation)
1413
+
1414
+ configure_after_destroy_method_for_has_and_belongs_to_many(reflection)
1415
+
1416
+ add_association_callbacks(reflection.name, options)
1417
+ end
1418
+
1419
+ private
1420
+ # Generates a join table name from two provided table names.
1421
+ # The names in the join table names end up in lexicographic order.
1422
+ #
1423
+ # join_table_name("members", "clubs") # => "clubs_members"
1424
+ # join_table_name("members", "special_clubs") # => "members_special_clubs"
1425
+ def join_table_name(first_table_name, second_table_name)
1426
+ if first_table_name < second_table_name
1427
+ join_table = "#{first_table_name}_#{second_table_name}"
1428
+ else
1429
+ join_table = "#{second_table_name}_#{first_table_name}"
1430
+ end
1431
+
1432
+ table_name_prefix + join_table + table_name_suffix
1433
+ end
1434
+
1435
+ def association_accessor_methods(reflection, association_proxy_class)
1436
+ redefine_method(reflection.name) do |*params|
1437
+ force_reload = params.first unless params.empty?
1438
+ association = association_instance_get(reflection.name)
1439
+
1440
+ if association.nil? || force_reload
1441
+ association = association_proxy_class.new(self, reflection)
1442
+ retval = force_reload ? reflection.klass.uncached { association.reload } : association.reload
1443
+ if retval.nil? and association_proxy_class == BelongsToAssociation
1444
+ association_instance_set(reflection.name, nil)
1445
+ return nil
1446
+ end
1447
+ association_instance_set(reflection.name, association)
1448
+ end
1449
+
1450
+ association.target.nil? ? nil : association
1451
+ end
1452
+
1453
+ redefine_method("loaded_#{reflection.name}?") do
1454
+ association = association_instance_get(reflection.name)
1455
+ association && association.loaded?
1456
+ end
1457
+
1458
+ redefine_method("#{reflection.name}=") do |new_value|
1459
+ association = association_instance_get(reflection.name)
1460
+
1461
+ if association.nil? || association.target != new_value
1462
+ association = association_proxy_class.new(self, reflection)
1463
+ end
1464
+
1465
+ association.replace(new_value)
1466
+ association_instance_set(reflection.name, new_value.nil? ? nil : association)
1467
+ end
1468
+
1469
+ redefine_method("set_#{reflection.name}_target") do |target|
1470
+ return if target.nil? and association_proxy_class == BelongsToAssociation
1471
+ association = association_proxy_class.new(self, reflection)
1472
+ association.target = target
1473
+ association_instance_set(reflection.name, association)
1474
+ end
1475
+ end
1476
+
1477
+ def collection_reader_method(reflection, association_proxy_class)
1478
+ redefine_method(reflection.name) do |*params|
1479
+ force_reload = params.first unless params.empty?
1480
+ association = association_instance_get(reflection.name)
1481
+
1482
+ unless association
1483
+ association = association_proxy_class.new(self, reflection)
1484
+ association_instance_set(reflection.name, association)
1485
+ end
1486
+
1487
+ reflection.klass.uncached { association.reload } if force_reload
1488
+
1489
+ association
1490
+ end
1491
+
1492
+ redefine_method("#{reflection.name.to_s.singularize}_ids") do
1493
+ if send(reflection.name).loaded? || reflection.options[:finder_sql]
1494
+ send(reflection.name).map { |r| r.id }
1495
+ else
1496
+ if reflection.through_reflection && reflection.source_reflection.belongs_to?
1497
+ through = reflection.through_reflection
1498
+ primary_key = reflection.source_reflection.primary_key_name
1499
+ send(through.name).select("DISTINCT #{through.quoted_table_name}.#{primary_key}").map! { |r| r.send(primary_key) }
1500
+ else
1501
+ send(reflection.name).select("#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").except(:includes).map! { |r| r.id }
1502
+ end
1503
+ end
1504
+ end
1505
+
1506
+ end
1507
+
1508
+ def collection_accessor_methods(reflection, association_proxy_class, writer = true)
1509
+ collection_reader_method(reflection, association_proxy_class)
1510
+
1511
+ if writer
1512
+ redefine_method("#{reflection.name}=") do |new_value|
1513
+ # Loads proxy class instance (defined in collection_reader_method) if not already loaded
1514
+ association = send(reflection.name)
1515
+ association.replace(new_value)
1516
+ association
1517
+ end
1518
+
1519
+ redefine_method("#{reflection.name.to_s.singularize}_ids=") do |new_value|
1520
+ pk_column = reflection.primary_key_column
1521
+ ids = (new_value || []).reject { |nid| nid.blank? }
1522
+ ids.map!{ |i| pk_column.type_cast(i) }
1523
+ send("#{reflection.name}=", reflection.klass.find(ids).index_by{ |r| r.id }.values_at(*ids))
1524
+ end
1525
+ end
1526
+ end
1527
+
1528
+ def association_constructor_method(constructor, reflection, association_proxy_class)
1529
+ redefine_method("#{constructor}_#{reflection.name}") do |*params|
1530
+ attributees = params.first unless params.empty?
1531
+ replace_existing = params[1].nil? ? true : params[1]
1532
+ association = association_instance_get(reflection.name)
1533
+
1534
+ unless association
1535
+ association = association_proxy_class.new(self, reflection)
1536
+ association_instance_set(reflection.name, association)
1537
+ end
1538
+
1539
+ if association_proxy_class == HasOneAssociation
1540
+ association.send(constructor, attributees, replace_existing)
1541
+ else
1542
+ association.send(constructor, attributees)
1543
+ end
1544
+ end
1545
+ end
1546
+
1547
+ def add_counter_cache_callbacks(reflection)
1548
+ cache_column = reflection.counter_cache_column
1549
+
1550
+ method_name = "belongs_to_counter_cache_after_create_for_#{reflection.name}".to_sym
1551
+ define_method(method_name) do
1552
+ association = send(reflection.name)
1553
+ association.class.increment_counter(cache_column, association.id) unless association.nil?
1554
+ end
1555
+ after_create(method_name)
1556
+
1557
+ method_name = "belongs_to_counter_cache_before_destroy_for_#{reflection.name}".to_sym
1558
+ define_method(method_name) do
1559
+ association = send(reflection.name)
1560
+ association.class.decrement_counter(cache_column, association.id) unless association.nil?
1561
+ end
1562
+ before_destroy(method_name)
1563
+
1564
+ module_eval(
1565
+ "#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)", __FILE__, __LINE__
1566
+ )
1567
+ end
1568
+
1569
+ def add_touch_callbacks(reflection, touch_attribute)
1570
+ method_name = :"belongs_to_touch_after_save_or_destroy_for_#{reflection.name}"
1571
+ redefine_method(method_name) do
1572
+ association = send(reflection.name)
1573
+
1574
+ if touch_attribute == true
1575
+ association.touch unless association.nil?
1576
+ else
1577
+ association.touch(touch_attribute) unless association.nil?
1578
+ end
1579
+ end
1580
+ after_save(method_name)
1581
+ after_touch(method_name)
1582
+ after_destroy(method_name)
1583
+ end
1584
+
1585
+ # Creates before_destroy callback methods that nullify, delete or destroy
1586
+ # has_many associated objects, according to the defined :dependent rule.
1587
+ # If the association is marked as :dependent => :restrict, create a callback
1588
+ # that prevents deleting entirely.
1589
+ #
1590
+ # See HasManyAssociation#delete_records. Dependent associations
1591
+ # delete children, otherwise foreign key is set to NULL.
1592
+ # See HasManyAssociation#delete_records. Dependent associations
1593
+ # delete children if the option is set to :destroy or :delete_all, set the
1594
+ # foreign key to NULL if the option is set to :nullify, and do not touch the
1595
+ # child records if the option is set to :restrict.
1596
+ #
1597
+ # The +extra_conditions+ parameter, which is not used within the main
1598
+ # Active Record codebase, is meant to allow plugins to define extra
1599
+ # finder conditions.
1600
+ def configure_dependency_for_has_many(reflection, extra_conditions = nil)
1601
+ if reflection.options.include?(:dependent)
1602
+ case reflection.options[:dependent]
1603
+ when :destroy
1604
+ method_name = "has_many_dependent_destroy_for_#{reflection.name}".to_sym
1605
+ define_method(method_name) do
1606
+ send(reflection.name).each do |o|
1607
+ # No point in executing the counter update since we're going to destroy the parent anyway
1608
+ counter_method = ('belongs_to_counter_cache_before_destroy_for_' + self.class.name.downcase).to_sym
1609
+ if(o.respond_to? counter_method) then
1610
+ class << o
1611
+ self
1612
+ end.send(:define_method, counter_method, Proc.new {})
1613
+ end
1614
+ o.destroy
1615
+ end
1616
+ end
1617
+ before_destroy method_name
1618
+ when :delete_all
1619
+ before_destroy do |record|
1620
+ self.class.send(:delete_all_has_many_dependencies,
1621
+ record,
1622
+ reflection.name,
1623
+ reflection.klass,
1624
+ reflection.dependent_conditions(record, self.class, extra_conditions))
1625
+ end
1626
+ when :nullify
1627
+ before_destroy do |record|
1628
+ self.class.send(:nullify_has_many_dependencies,
1629
+ record,
1630
+ reflection.name,
1631
+ reflection.klass,
1632
+ reflection.primary_key_name,
1633
+ reflection.dependent_conditions(record, self.class, extra_conditions))
1634
+ end
1635
+ when :restrict
1636
+ method_name = "has_many_dependent_restrict_for_#{reflection.name}".to_sym
1637
+ define_method(method_name) do
1638
+ unless send(reflection.name).empty?
1639
+ raise DeleteRestrictionError.new(reflection)
1640
+ end
1641
+ end
1642
+ before_destroy method_name
1643
+ else
1644
+ raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, :nullify or :restrict (#{reflection.options[:dependent].inspect})"
1645
+ end
1646
+ end
1647
+ end
1648
+
1649
+ # Creates before_destroy callback methods that nullify, delete or destroy
1650
+ # has_one associated objects, according to the defined :dependent rule.
1651
+ # If the association is marked as :dependent => :restrict, create a callback
1652
+ # that prevents deleting entirely.
1653
+ def configure_dependency_for_has_one(reflection)
1654
+ if reflection.options.include?(:dependent)
1655
+ name = reflection.options[:dependent]
1656
+ method_name = :"has_one_dependent_#{name}_for_#{reflection.name}"
1657
+
1658
+ case name
1659
+ when :destroy, :delete
1660
+ class_eval <<-eoruby, __FILE__, __LINE__ + 1
1661
+ def #{method_name}
1662
+ association = #{reflection.name}
1663
+ association.#{name} if association
1664
+ end
1665
+ eoruby
1666
+ when :nullify
1667
+ class_eval <<-eoruby, __FILE__, __LINE__ + 1
1668
+ def #{method_name}
1669
+ association = #{reflection.name}
1670
+ association.update_attribute(#{reflection.primary_key_name.inspect}, nil) if association
1671
+ end
1672
+ eoruby
1673
+ when :restrict
1674
+ method_name = "has_one_dependent_restrict_for_#{reflection.name}".to_sym
1675
+ define_method(method_name) do
1676
+ unless send(reflection.name).nil?
1677
+ raise DeleteRestrictionError.new(reflection)
1678
+ end
1679
+ end
1680
+ before_destroy method_name
1681
+ else
1682
+ raise ArgumentError, "The :dependent option expects either :destroy, :delete, :nullify or :restrict (#{reflection.options[:dependent].inspect})"
1683
+ end
1684
+
1685
+ before_destroy method_name
1686
+ end
1687
+ end
1688
+
1689
+ def configure_dependency_for_belongs_to(reflection)
1690
+ if reflection.options.include?(:dependent)
1691
+ name = reflection.options[:dependent]
1692
+
1693
+ unless [:destroy, :delete].include?(name)
1694
+ raise ArgumentError, "The :dependent option expects either :destroy or :delete (#{reflection.options[:dependent].inspect})"
1695
+ end
1696
+
1697
+ method_name = :"belongs_to_dependent_#{name}_for_#{reflection.name}"
1698
+ class_eval <<-eoruby, __FILE__, __LINE__ + 1
1699
+ def #{method_name}
1700
+ association = #{reflection.name}
1701
+ association.#{name} if association
1702
+ end
1703
+ eoruby
1704
+ after_destroy method_name
1705
+ end
1706
+ end
1707
+
1708
+ def configure_after_destroy_method_for_has_and_belongs_to_many(reflection)
1709
+ method_name = :"has_and_belongs_to_many_after_destroy_for_#{reflection.name}"
1710
+ class_eval <<-eoruby, __FILE__, __LINE__ + 1
1711
+ def #{method_name}
1712
+ association = #{reflection.name}
1713
+ association.delete_all if association
1714
+ end
1715
+ eoruby
1716
+ after_destroy method_name
1717
+ end
1718
+
1719
+ def delete_all_has_many_dependencies(record, reflection_name, association_class, dependent_conditions)
1720
+ association_class.delete_all(dependent_conditions)
1721
+ end
1722
+
1723
+ def nullify_has_many_dependencies(record, reflection_name, association_class, primary_key_name, dependent_conditions)
1724
+ association_class.update_all("#{primary_key_name} = NULL", dependent_conditions)
1725
+ end
1726
+
1727
+ mattr_accessor :valid_keys_for_has_many_association
1728
+ @@valid_keys_for_has_many_association = [
1729
+ :class_name, :table_name, :foreign_key, :primary_key,
1730
+ :dependent,
1731
+ :select, :conditions, :include, :order, :group, :having, :limit, :offset,
1732
+ :as, :through, :source, :source_type,
1733
+ :uniq,
1734
+ :finder_sql, :counter_sql,
1735
+ :before_add, :after_add, :before_remove, :after_remove,
1736
+ :extend, :readonly,
1737
+ :validate, :inverse_of
1738
+ ]
1739
+
1740
+ def create_has_many_reflection(association_id, options, &extension)
1741
+ options.assert_valid_keys(valid_keys_for_has_many_association)
1742
+ options[:extend] = create_extension_modules(association_id, extension, options[:extend])
1743
+
1744
+ create_reflection(:has_many, association_id, options, self)
1745
+ end
1746
+
1747
+ mattr_accessor :valid_keys_for_has_one_association
1748
+ @@valid_keys_for_has_one_association = [
1749
+ :class_name, :foreign_key, :remote, :select, :conditions, :order,
1750
+ :include, :dependent, :counter_cache, :extend, :as, :readonly,
1751
+ :validate, :primary_key, :inverse_of
1752
+ ]
1753
+
1754
+ def create_has_one_reflection(association_id, options)
1755
+ options.assert_valid_keys(valid_keys_for_has_one_association)
1756
+ create_reflection(:has_one, association_id, options, self)
1757
+ end
1758
+
1759
+ def create_has_one_through_reflection(association_id, options)
1760
+ options.assert_valid_keys(
1761
+ :class_name, :foreign_key, :remote, :select, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as, :through, :source, :source_type, :validate
1762
+ )
1763
+ create_reflection(:has_one, association_id, options, self)
1764
+ end
1765
+
1766
+ mattr_accessor :valid_keys_for_belongs_to_association
1767
+ @@valid_keys_for_belongs_to_association = [
1768
+ :class_name, :primary_key, :foreign_key, :foreign_type, :remote, :select, :conditions,
1769
+ :include, :dependent, :counter_cache, :extend, :polymorphic, :readonly,
1770
+ :validate, :touch, :inverse_of
1771
+ ]
1772
+
1773
+ def create_belongs_to_reflection(association_id, options)
1774
+ options.assert_valid_keys(valid_keys_for_belongs_to_association)
1775
+ reflection = create_reflection(:belongs_to, association_id, options, self)
1776
+
1777
+ if options[:polymorphic]
1778
+ reflection.options[:foreign_type] ||= reflection.class_name.underscore + "_type"
1779
+ end
1780
+
1781
+ reflection
1782
+ end
1783
+
1784
+ mattr_accessor :valid_keys_for_has_and_belongs_to_many_association
1785
+ @@valid_keys_for_has_and_belongs_to_many_association = [
1786
+ :class_name, :table_name, :join_table, :foreign_key, :association_foreign_key,
1787
+ :select, :conditions, :include, :order, :group, :having, :limit, :offset,
1788
+ :uniq,
1789
+ :finder_sql, :counter_sql, :delete_sql, :insert_sql,
1790
+ :before_add, :after_add, :before_remove, :after_remove,
1791
+ :extend, :readonly,
1792
+ :validate
1793
+ ]
1794
+
1795
+ def create_has_and_belongs_to_many_reflection(association_id, options, &extension)
1796
+ options.assert_valid_keys(valid_keys_for_has_and_belongs_to_many_association)
1797
+ options[:extend] = create_extension_modules(association_id, extension, options[:extend])
1798
+
1799
+ reflection = create_reflection(:has_and_belongs_to_many, association_id, options, self)
1800
+
1801
+ if reflection.association_foreign_key == reflection.primary_key_name
1802
+ raise HasAndBelongsToManyAssociationForeignKeyNeeded.new(reflection)
1803
+ end
1804
+
1805
+ reflection.options[:join_table] ||= join_table_name(undecorated_table_name(self.to_s), undecorated_table_name(reflection.class_name))
1806
+ if connection.supports_primary_key? && (connection.primary_key(reflection.options[:join_table]) rescue false)
1807
+ raise HasAndBelongsToManyAssociationWithPrimaryKeyError.new(reflection)
1808
+ end
1809
+
1810
+ reflection
1811
+ end
1812
+
1813
+ def add_association_callbacks(association_name, options)
1814
+ callbacks = %w(before_add after_add before_remove after_remove)
1815
+ callbacks.each do |callback_name|
1816
+ full_callback_name = "#{callback_name}_for_#{association_name}"
1817
+ defined_callbacks = options[callback_name.to_sym]
1818
+ if options.has_key?(callback_name.to_sym)
1819
+ class_inheritable_reader full_callback_name.to_sym
1820
+ write_inheritable_attribute(full_callback_name.to_sym, [defined_callbacks].flatten)
1821
+ else
1822
+ write_inheritable_attribute(full_callback_name.to_sym, [])
1823
+ end
1824
+ end
1825
+ end
1826
+
1827
+ def create_extension_modules(association_id, block_extension, extensions)
1828
+ if block_extension
1829
+ extension_module_name = "#{self.to_s.demodulize}#{association_id.to_s.camelize}AssociationExtension"
1830
+
1831
+ silence_warnings do
1832
+ self.parent.const_set(extension_module_name, Module.new(&block_extension))
1833
+ end
1834
+ Array.wrap(extensions).push("#{self.parent}::#{extension_module_name}".constantize)
1835
+ else
1836
+ Array.wrap(extensions)
1837
+ end
1838
+ end
1839
+
1840
+ class JoinDependency # :nodoc:
1841
+ attr_reader :joins, :reflections, :table_aliases
1842
+
1843
+ def initialize(base, associations, joins)
1844
+ @joins = [JoinBase.new(base, joins)]
1845
+ @associations = {}
1846
+ @reflections = []
1847
+ @base_records_hash = {}
1848
+ @base_records_in_order = []
1849
+ @table_aliases = Hash.new { |aliases, table| aliases[table] = 0 }
1850
+ @table_aliases[base.table_name] = 1
1851
+ build(associations)
1852
+ end
1853
+
1854
+ def graft(*associations)
1855
+ associations.each do |association|
1856
+ join_associations.detect {|a| association == a} ||
1857
+ build(association.reflection.name, association.find_parent_in(self) || join_base, association.join_type)
1858
+ end
1859
+ self
1860
+ end
1861
+
1862
+ def join_associations
1863
+ @joins[1..-1].to_a
1864
+ end
1865
+
1866
+ def join_base
1867
+ @joins[0]
1868
+ end
1869
+
1870
+ def count_aliases_from_table_joins(name)
1871
+ # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase
1872
+ quoted_name = join_base.active_record.connection.quote_table_name(name.downcase).downcase
1873
+ join_sql = join_base.table_joins.to_s.downcase
1874
+ join_sql.blank? ? 0 :
1875
+ # Table names
1876
+ join_sql.scan(/join(?:\s+\w+)?\s+#{quoted_name}\son/).size +
1877
+ # Table aliases
1878
+ join_sql.scan(/join(?:\s+\w+)?\s+\S+\s+#{quoted_name}\son/).size
1879
+ end
1880
+
1881
+ def instantiate(rows)
1882
+ rows.each_with_index do |row, i|
1883
+ primary_id = join_base.record_id(row)
1884
+ unless @base_records_hash[primary_id]
1885
+ @base_records_in_order << (@base_records_hash[primary_id] = join_base.instantiate(row))
1886
+ end
1887
+ construct(@base_records_hash[primary_id], @associations, join_associations.dup, row)
1888
+ end
1889
+ remove_duplicate_results!(join_base.active_record, @base_records_in_order, @associations)
1890
+ return @base_records_in_order
1891
+ end
1892
+
1893
+ def remove_duplicate_results!(base, records, associations)
1894
+ case associations
1895
+ when Symbol, String
1896
+ reflection = base.reflections[associations]
1897
+ remove_uniq_by_reflection(reflection, records)
1898
+ when Array
1899
+ associations.each do |association|
1900
+ remove_duplicate_results!(base, records, association)
1901
+ end
1902
+ when Hash
1903
+ associations.keys.each do |name|
1904
+ reflection = base.reflections[name]
1905
+ remove_uniq_by_reflection(reflection, records)
1906
+
1907
+ parent_records = []
1908
+ records.each do |record|
1909
+ if descendant = record.send(reflection.name)
1910
+ if reflection.collection?
1911
+ parent_records.concat descendant.target.uniq
1912
+ else
1913
+ parent_records << descendant
1914
+ end
1915
+ end
1916
+ end
1917
+
1918
+ remove_duplicate_results!(reflection.klass, parent_records, associations[name]) unless parent_records.empty?
1919
+ end
1920
+ end
1921
+ end
1922
+
1923
+ protected
1924
+
1925
+ def cache_joined_association(association)
1926
+ associations = []
1927
+ parent = association.parent
1928
+ while parent != join_base
1929
+ associations.unshift(parent.reflection.name)
1930
+ parent = parent.parent
1931
+ end
1932
+ ref = @associations
1933
+ associations.each do |key|
1934
+ ref = ref[key]
1935
+ end
1936
+ ref[association.reflection.name] ||= {}
1937
+ end
1938
+
1939
+ def build(associations, parent = nil, join_type = Arel::InnerJoin)
1940
+ parent ||= @joins.last
1941
+ case associations
1942
+ when Symbol, String
1943
+ reflection = parent.reflections[associations.to_s.intern] or
1944
+ raise ConfigurationError, "Association named '#{ associations }' was not found; perhaps you misspelled it?"
1945
+ unless join_association = find_join_association(reflection, parent)
1946
+ @reflections << reflection
1947
+ join_association = build_join_association(reflection, parent)
1948
+ join_association.join_type = join_type
1949
+ @joins << join_association
1950
+ cache_joined_association(join_association)
1951
+ end
1952
+ join_association
1953
+ when Array
1954
+ associations.each do |association|
1955
+ build(association, parent, join_type)
1956
+ end
1957
+ when Hash
1958
+ associations.keys.sort{|a,b|a.to_s<=>b.to_s}.each do |name|
1959
+ join_association = build(name, parent, join_type)
1960
+ build(associations[name], join_association, join_type)
1961
+ end
1962
+ else
1963
+ raise ConfigurationError, associations.inspect
1964
+ end
1965
+ end
1966
+
1967
+ def find_join_association(name_or_reflection, parent)
1968
+ case name_or_reflection
1969
+ when Symbol, String
1970
+ join_associations.detect {|j| (j.reflection.name == name_or_reflection.to_s.intern) && (j.parent == parent)}
1971
+ else
1972
+ join_associations.detect {|j| (j.reflection == name_or_reflection) && (j.parent == parent)}
1973
+ end
1974
+ end
1975
+
1976
+ def remove_uniq_by_reflection(reflection, records)
1977
+ if reflection && reflection.collection?
1978
+ records.each { |record| record.send(reflection.name).target.uniq! }
1979
+ end
1980
+ end
1981
+
1982
+ def build_join_association(reflection, parent)
1983
+ JoinAssociation.new(reflection, self, parent)
1984
+ end
1985
+
1986
+ def construct(parent, associations, joins, row)
1987
+ case associations
1988
+ when Symbol, String
1989
+ join = joins.detect{|j| j.reflection.name.to_s == associations.to_s && j.parent_table_name == parent.class.table_name }
1990
+ raise(ConfigurationError, "No such association") if join.nil?
1991
+
1992
+ joins.delete(join)
1993
+ construct_association(parent, join, row)
1994
+ when Array
1995
+ associations.each do |association|
1996
+ construct(parent, association, joins, row)
1997
+ end
1998
+ when Hash
1999
+ associations.keys.sort{|a,b|a.to_s<=>b.to_s}.each do |name|
2000
+ join = joins.detect{|j| j.reflection.name.to_s == name.to_s && j.parent_table_name == parent.class.table_name }
2001
+ raise(ConfigurationError, "No such association") if join.nil?
2002
+
2003
+ association = construct_association(parent, join, row)
2004
+ joins.delete(join)
2005
+ construct(association, associations[name], joins, row) if association
2006
+ end
2007
+ else
2008
+ raise ConfigurationError, associations.inspect
2009
+ end
2010
+ end
2011
+
2012
+ def construct_association(record, join, row)
2013
+ case join.reflection.macro
2014
+ when :has_many, :has_and_belongs_to_many
2015
+ collection = record.send(join.reflection.name)
2016
+ collection.loaded
2017
+
2018
+ return nil if record.id.to_s != join.parent.record_id(row).to_s or row[join.aliased_primary_key].nil?
2019
+ association = join.instantiate(row)
2020
+ collection.target.push(association)
2021
+ collection.__send__(:set_inverse_instance, association, record)
2022
+ when :has_one
2023
+ return if record.id.to_s != join.parent.record_id(row).to_s
2024
+ return if record.instance_variable_defined?("@#{join.reflection.name}")
2025
+ association = join.instantiate(row) unless row[join.aliased_primary_key].nil?
2026
+ set_target_and_inverse(join, association, record)
2027
+ when :belongs_to
2028
+ return if record.id.to_s != join.parent.record_id(row).to_s or row[join.aliased_primary_key].nil?
2029
+ association = join.instantiate(row)
2030
+ set_target_and_inverse(join, association, record)
2031
+ else
2032
+ raise ConfigurationError, "unknown macro: #{join.reflection.macro}"
2033
+ end
2034
+ return association
2035
+ end
2036
+
2037
+ def set_target_and_inverse(join, association, record)
2038
+ association_proxy = record.send("set_#{join.reflection.name}_target", association)
2039
+ association_proxy.__send__(:set_inverse_instance, association, record)
2040
+ end
2041
+
2042
+ class JoinBase # :nodoc:
2043
+ attr_reader :active_record, :table_joins
2044
+ delegate :table_name, :column_names, :primary_key, :reflections, :sanitize_sql, :arel_engine, :to => :active_record
2045
+
2046
+ def initialize(active_record, joins = nil)
2047
+ @active_record = active_record
2048
+ @cached_record = {}
2049
+ @table_joins = joins
2050
+ end
2051
+
2052
+ def ==(other)
2053
+ other.class == self.class &&
2054
+ other.active_record == active_record
2055
+ end
2056
+
2057
+ def aliased_prefix
2058
+ "t0"
2059
+ end
2060
+
2061
+ def aliased_primary_key
2062
+ "#{aliased_prefix}_r0"
2063
+ end
2064
+
2065
+ def aliased_table_name
2066
+ active_record.table_name
2067
+ end
2068
+
2069
+ def column_names_with_alias
2070
+ unless defined?(@column_names_with_alias)
2071
+ @column_names_with_alias = []
2072
+
2073
+ ([primary_key] + (column_names - [primary_key])).each_with_index do |column_name, i|
2074
+ @column_names_with_alias << [column_name, "#{aliased_prefix}_r#{i}"]
2075
+ end
2076
+ end
2077
+
2078
+ @column_names_with_alias
2079
+ end
2080
+
2081
+ def extract_record(row)
2082
+ Hash[column_names_with_alias.map{|cn, an| [cn, row[an]]}]
2083
+ end
2084
+
2085
+ def record_id(row)
2086
+ row[aliased_primary_key]
2087
+ end
2088
+
2089
+ def instantiate(row)
2090
+ @cached_record[record_id(row)] ||= active_record.send(:instantiate, extract_record(row))
2091
+ end
2092
+ end
2093
+
2094
+ class JoinAssociation < JoinBase # :nodoc:
2095
+ attr_reader :reflection, :parent, :aliased_table_name, :aliased_prefix, :aliased_join_table_name, :parent_table_name
2096
+ # What type of join will be generated, either Arel::InnerJoin (default) or Arel::OuterJoin
2097
+ attr_accessor :join_type
2098
+ delegate :options, :klass, :through_reflection, :source_reflection, :to => :reflection
2099
+
2100
+ def initialize(reflection, join_dependency, parent = nil)
2101
+ reflection.check_validity!
2102
+ if reflection.options[:polymorphic]
2103
+ raise EagerLoadPolymorphicError.new(reflection)
2104
+ end
2105
+
2106
+ super(reflection.klass)
2107
+ @join_dependency = join_dependency
2108
+ @parent = parent
2109
+ @reflection = reflection
2110
+ @aliased_prefix = "t#{ join_dependency.joins.size }"
2111
+ @parent_table_name = parent.active_record.table_name
2112
+ @aliased_table_name = aliased_table_name_for(table_name)
2113
+ @join = nil
2114
+ @join_type = Arel::InnerJoin
2115
+
2116
+ if reflection.macro == :has_and_belongs_to_many
2117
+ @aliased_join_table_name = aliased_table_name_for(reflection.options[:join_table], "_join")
2118
+ end
2119
+
2120
+ if [:has_many, :has_one].include?(reflection.macro) && reflection.options[:through]
2121
+ @aliased_join_table_name = aliased_table_name_for(reflection.through_reflection.klass.table_name, "_join")
2122
+ end
2123
+ end
2124
+
2125
+ def ==(other)
2126
+ other.class == self.class &&
2127
+ other.reflection == reflection &&
2128
+ other.parent == parent
2129
+ end
2130
+
2131
+ def find_parent_in(other_join_dependency)
2132
+ other_join_dependency.joins.detect do |join|
2133
+ self.parent == join
2134
+ end
2135
+ end
2136
+
2137
+ def association_join
2138
+ return @join if @join
2139
+
2140
+ aliased_table = Arel::Table.new(table_name, :as => @aliased_table_name,
2141
+ :engine => arel_engine,
2142
+ :columns => klass.columns)
2143
+
2144
+ parent_table = Arel::Table.new(parent.table_name, :as => parent.aliased_table_name,
2145
+ :engine => arel_engine,
2146
+ :columns => parent.active_record.columns)
2147
+
2148
+ @join = case reflection.macro
2149
+ when :has_and_belongs_to_many
2150
+ join_table = Arel::Table.new(options[:join_table], :as => aliased_join_table_name, :engine => arel_engine)
2151
+ fk = options[:foreign_key] || reflection.active_record.to_s.foreign_key
2152
+ klass_fk = options[:association_foreign_key] || klass.to_s.foreign_key
2153
+
2154
+ [
2155
+ join_table[fk].eq(parent_table[reflection.active_record.primary_key]),
2156
+ aliased_table[klass.primary_key].eq(join_table[klass_fk])
2157
+ ]
2158
+ when :has_many, :has_one
2159
+ if reflection.options[:through]
2160
+ join_table = Arel::Table.new(through_reflection.klass.table_name, :as => aliased_join_table_name, :engine => arel_engine)
2161
+ jt_as_extra = jt_source_extra = jt_sti_extra = nil
2162
+ first_key = second_key = as_extra = nil
2163
+
2164
+ if through_reflection.macro == :belongs_to
2165
+ jt_primary_key = through_reflection.primary_key_name
2166
+ jt_foreign_key = through_reflection.association_primary_key
2167
+ else
2168
+ jt_primary_key = through_reflection.active_record_primary_key
2169
+ jt_foreign_key = through_reflection.primary_key_name
2170
+
2171
+ if through_reflection.options[:as] # has_many :through against a polymorphic join
2172
+ jt_as_extra = join_table[through_reflection.options[:as].to_s + '_type'].eq(parent.active_record.base_class.name)
2173
+ end
2174
+ end
2175
+
2176
+ case source_reflection.macro
2177
+ when :has_many
2178
+ if source_reflection.options[:as]
2179
+ first_key = "#{source_reflection.options[:as]}_id"
2180
+ second_key = options[:foreign_key] || primary_key
2181
+ as_extra = aliased_table["#{source_reflection.options[:as]}_type"].eq(source_reflection.active_record.base_class.name)
2182
+ else
2183
+ first_key = through_reflection.klass.base_class.to_s.foreign_key
2184
+ second_key = options[:foreign_key] || primary_key
2185
+ end
2186
+
2187
+ unless through_reflection.klass.descends_from_active_record?
2188
+ jt_sti_extra = join_table[through_reflection.active_record.inheritance_column].eq(through_reflection.klass.sti_name)
2189
+ end
2190
+ when :belongs_to
2191
+ first_key = primary_key
2192
+ if reflection.options[:source_type]
2193
+ second_key = source_reflection.association_foreign_key
2194
+ jt_source_extra = join_table[reflection.source_reflection.options[:foreign_type]].eq(reflection.options[:source_type])
2195
+ else
2196
+ second_key = source_reflection.primary_key_name
2197
+ end
2198
+ end
2199
+
2200
+ [
2201
+ [parent_table[jt_primary_key].eq(join_table[jt_foreign_key]), jt_as_extra, jt_source_extra, jt_sti_extra].reject{|x| x.blank? },
2202
+ aliased_table[first_key].eq(join_table[second_key])
2203
+ ]
2204
+ elsif reflection.options[:as]
2205
+ id_rel = aliased_table["#{reflection.options[:as]}_id"].eq(parent_table[parent.primary_key])
2206
+ type_rel = aliased_table["#{reflection.options[:as]}_type"].eq(parent.active_record.base_class.name)
2207
+ [id_rel, type_rel]
2208
+ else
2209
+ foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key
2210
+ [aliased_table[foreign_key].eq(parent_table[reflection.options[:primary_key] || parent.primary_key])]
2211
+ end
2212
+ when :belongs_to
2213
+ [aliased_table[options[:primary_key] || reflection.klass.primary_key].eq(parent_table[options[:foreign_key] || reflection.primary_key_name])]
2214
+ end
2215
+
2216
+ unless klass.descends_from_active_record?
2217
+ sti_column = aliased_table[klass.inheritance_column]
2218
+ sti_condition = sti_column.eq(klass.sti_name)
2219
+ klass.descendants.each {|subclass| sti_condition = sti_condition.or(sti_column.eq(subclass.sti_name)) }
2220
+
2221
+ @join << sti_condition
2222
+ end
2223
+
2224
+ [through_reflection, reflection].each do |ref|
2225
+ if ref && ref.options[:conditions]
2226
+ @join << process_conditions(ref.options[:conditions], aliased_table_name)
2227
+ end
2228
+ end
2229
+
2230
+ @join
2231
+ end
2232
+
2233
+ def relation
2234
+ aliased = Arel::Table.new(table_name, :as => @aliased_table_name,
2235
+ :engine => arel_engine,
2236
+ :columns => klass.columns)
2237
+
2238
+ if reflection.macro == :has_and_belongs_to_many
2239
+ [Arel::Table.new(options[:join_table], :as => aliased_join_table_name, :engine => arel_engine), aliased]
2240
+ elsif reflection.options[:through]
2241
+ [Arel::Table.new(through_reflection.klass.table_name, :as => aliased_join_table_name, :engine => arel_engine), aliased]
2242
+ else
2243
+ aliased
2244
+ end
2245
+ end
2246
+
2247
+ def join_relation(joining_relation)
2248
+ self.join_type = Arel::OuterJoin
2249
+ joining_relation.joins(self)
2250
+ end
2251
+
2252
+ protected
2253
+
2254
+ def aliased_table_name_for(name, suffix = nil)
2255
+ if @join_dependency.table_aliases[name].zero?
2256
+ @join_dependency.table_aliases[name] = @join_dependency.count_aliases_from_table_joins(name)
2257
+ end
2258
+
2259
+ if !@join_dependency.table_aliases[name].zero? # We need an alias
2260
+ name = active_record.connection.table_alias_for "#{pluralize(reflection.name)}_#{parent_table_name}#{suffix}"
2261
+ @join_dependency.table_aliases[name] += 1
2262
+ if @join_dependency.table_aliases[name] == 1 # First time we've seen this name
2263
+ # Also need to count the aliases from the table_aliases to avoid incorrect count
2264
+ @join_dependency.table_aliases[name] += @join_dependency.count_aliases_from_table_joins(name)
2265
+ end
2266
+ table_index = @join_dependency.table_aliases[name]
2267
+ name = name[0..active_record.connection.table_alias_length-3] + "_#{table_index}" if table_index > 1
2268
+ else
2269
+ @join_dependency.table_aliases[name] += 1
2270
+ end
2271
+
2272
+ name
2273
+ end
2274
+
2275
+ def pluralize(table_name)
2276
+ ActiveRecord::Base.pluralize_table_names ? table_name.to_s.pluralize : table_name
2277
+ end
2278
+
2279
+ def table_alias_for(table_name, table_alias)
2280
+ "#{table_name} #{table_alias if table_name != table_alias}".strip
2281
+ end
2282
+
2283
+ def table_name_and_alias
2284
+ table_alias_for table_name, @aliased_table_name
2285
+ end
2286
+
2287
+ def process_conditions(conditions, table_name)
2288
+ sanitized = sanitize_sql(conditions, table_name)
2289
+
2290
+ if sanitized =~ /\#\{.*\}/
2291
+ ActiveSupport::Deprecation.warn(
2292
+ 'String-based interpolation of association conditions is deprecated. Please use a ' \
2293
+ 'proc instead. So, for example, has_many :older_friends, :conditions => \'age > #{age}\' ' \
2294
+ 'should be changed to has_many :older_friends, :conditions => proc { "age > #{age}" }.'
2295
+ )
2296
+ instance_eval("%@#{sanitized.gsub('@', '\@')}@", __FILE__, __LINE__)
2297
+ elsif conditions.respond_to?(:to_proc)
2298
+ conditions = sanitize_sql(instance_eval(&conditions), table_name)
2299
+ else
2300
+ sanitized
2301
+ end
2302
+ end
2303
+ end
2304
+ end
2305
+ end
2306
+ end
2307
+ end