activerecord 3.0.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

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