activerecord 4.2.6 → 5.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 (246) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1307 -1105
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +7 -8
  5. data/examples/performance.rb +2 -3
  6. data/examples/simple.rb +0 -1
  7. data/lib/active_record/aggregations.rb +37 -23
  8. data/lib/active_record/association_relation.rb +3 -3
  9. data/lib/active_record/associations/alias_tracker.rb +19 -16
  10. data/lib/active_record/associations/association.rb +11 -9
  11. data/lib/active_record/associations/association_scope.rb +73 -102
  12. data/lib/active_record/associations/belongs_to_association.rb +21 -32
  13. data/lib/active_record/associations/builder/association.rb +28 -34
  14. data/lib/active_record/associations/builder/belongs_to.rb +43 -18
  15. data/lib/active_record/associations/builder/collection_association.rb +7 -19
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +14 -11
  17. data/lib/active_record/associations/builder/has_many.rb +4 -4
  18. data/lib/active_record/associations/builder/has_one.rb +11 -6
  19. data/lib/active_record/associations/builder/singular_association.rb +3 -10
  20. data/lib/active_record/associations/collection_association.rb +50 -31
  21. data/lib/active_record/associations/collection_proxy.rb +69 -29
  22. data/lib/active_record/associations/foreign_association.rb +1 -1
  23. data/lib/active_record/associations/has_many_association.rb +20 -71
  24. data/lib/active_record/associations/has_many_through_association.rb +8 -47
  25. data/lib/active_record/associations/has_one_association.rb +12 -5
  26. data/lib/active_record/associations/join_dependency/join_association.rb +20 -8
  27. data/lib/active_record/associations/join_dependency.rb +29 -19
  28. data/lib/active_record/associations/preloader/association.rb +46 -52
  29. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  30. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  31. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  32. data/lib/active_record/associations/preloader/through_association.rb +27 -14
  33. data/lib/active_record/associations/preloader.rb +14 -4
  34. data/lib/active_record/associations/singular_association.rb +7 -1
  35. data/lib/active_record/associations/through_association.rb +11 -3
  36. data/lib/active_record/associations.rb +317 -209
  37. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  38. data/lib/active_record/attribute.rb +68 -18
  39. data/lib/active_record/attribute_assignment.rb +20 -141
  40. data/lib/active_record/attribute_decorators.rb +6 -5
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  42. data/lib/active_record/attribute_methods/dirty.rb +46 -86
  43. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  44. data/lib/active_record/attribute_methods/query.rb +2 -2
  45. data/lib/active_record/attribute_methods/read.rb +31 -59
  46. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
  48. data/lib/active_record/attribute_methods/write.rb +14 -38
  49. data/lib/active_record/attribute_methods.rb +70 -45
  50. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  51. data/lib/active_record/attribute_set/builder.rb +6 -4
  52. data/lib/active_record/attribute_set.rb +30 -3
  53. data/lib/active_record/attributes.rb +199 -80
  54. data/lib/active_record/autosave_association.rb +49 -16
  55. data/lib/active_record/base.rb +32 -23
  56. data/lib/active_record/callbacks.rb +39 -43
  57. data/lib/active_record/coders/json.rb +1 -1
  58. data/lib/active_record/coders/yaml_column.rb +20 -8
  59. data/lib/active_record/collection_cache_key.rb +40 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +452 -182
  61. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  62. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -61
  63. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -2
  64. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -9
  65. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  66. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  67. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -185
  68. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
  69. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +378 -140
  70. data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
  71. data/lib/active_record/connection_adapters/abstract_adapter.rb +153 -59
  72. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +405 -362
  73. data/lib/active_record/connection_adapters/column.rb +28 -43
  74. data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
  75. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  76. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  77. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  78. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  79. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  81. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  82. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  83. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  84. data/lib/active_record/connection_adapters/mysql2_adapter.rb +25 -176
  85. data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
  86. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +10 -72
  87. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -56
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  95. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  97. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  98. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  100. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
  101. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  102. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  103. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  105. data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
  106. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  107. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  108. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  109. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +234 -148
  110. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  111. data/lib/active_record/connection_adapters/postgresql_adapter.rb +248 -160
  112. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  113. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  114. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  115. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  116. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  117. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +148 -203
  118. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  119. data/lib/active_record/connection_handling.rb +37 -14
  120. data/lib/active_record/core.rb +89 -107
  121. data/lib/active_record/counter_cache.rb +13 -24
  122. data/lib/active_record/dynamic_matchers.rb +1 -20
  123. data/lib/active_record/enum.rb +113 -76
  124. data/lib/active_record/errors.rb +87 -48
  125. data/lib/active_record/explain_registry.rb +1 -1
  126. data/lib/active_record/explain_subscriber.rb +1 -1
  127. data/lib/active_record/fixture_set/file.rb +26 -5
  128. data/lib/active_record/fixtures.rb +76 -40
  129. data/lib/active_record/gem_version.rb +3 -3
  130. data/lib/active_record/inheritance.rb +32 -40
  131. data/lib/active_record/integration.rb +4 -4
  132. data/lib/active_record/internal_metadata.rb +56 -0
  133. data/lib/active_record/legacy_yaml_adapter.rb +18 -2
  134. data/lib/active_record/locale/en.yml +3 -2
  135. data/lib/active_record/locking/optimistic.rb +15 -15
  136. data/lib/active_record/locking/pessimistic.rb +1 -1
  137. data/lib/active_record/log_subscriber.rb +43 -21
  138. data/lib/active_record/migration/command_recorder.rb +59 -18
  139. data/lib/active_record/migration/compatibility.rb +126 -0
  140. data/lib/active_record/migration.rb +364 -109
  141. data/lib/active_record/model_schema.rb +128 -38
  142. data/lib/active_record/nested_attributes.rb +58 -29
  143. data/lib/active_record/null_relation.rb +16 -8
  144. data/lib/active_record/persistence.rb +121 -80
  145. data/lib/active_record/query_cache.rb +15 -18
  146. data/lib/active_record/querying.rb +10 -9
  147. data/lib/active_record/railtie.rb +27 -18
  148. data/lib/active_record/railties/controller_runtime.rb +1 -1
  149. data/lib/active_record/railties/databases.rake +58 -45
  150. data/lib/active_record/readonly_attributes.rb +1 -1
  151. data/lib/active_record/reflection.rb +282 -115
  152. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  153. data/lib/active_record/relation/batches.rb +139 -34
  154. data/lib/active_record/relation/calculations.rb +80 -102
  155. data/lib/active_record/relation/delegation.rb +7 -20
  156. data/lib/active_record/relation/finder_methods.rb +163 -81
  157. data/lib/active_record/relation/from_clause.rb +32 -0
  158. data/lib/active_record/relation/merger.rb +16 -42
  159. data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -15
  160. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  161. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  162. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  163. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  164. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  165. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  166. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  167. data/lib/active_record/relation/predicate_builder.rb +120 -107
  168. data/lib/active_record/relation/query_attribute.rb +19 -0
  169. data/lib/active_record/relation/query_methods.rb +308 -244
  170. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  171. data/lib/active_record/relation/spawn_methods.rb +4 -7
  172. data/lib/active_record/relation/where_clause.rb +174 -0
  173. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  174. data/lib/active_record/relation.rb +176 -116
  175. data/lib/active_record/result.rb +4 -3
  176. data/lib/active_record/runtime_registry.rb +1 -1
  177. data/lib/active_record/sanitization.rb +95 -66
  178. data/lib/active_record/schema.rb +26 -22
  179. data/lib/active_record/schema_dumper.rb +62 -38
  180. data/lib/active_record/schema_migration.rb +11 -17
  181. data/lib/active_record/scoping/default.rb +23 -9
  182. data/lib/active_record/scoping/named.rb +49 -28
  183. data/lib/active_record/scoping.rb +32 -15
  184. data/lib/active_record/secure_token.rb +38 -0
  185. data/lib/active_record/serialization.rb +2 -4
  186. data/lib/active_record/statement_cache.rb +16 -14
  187. data/lib/active_record/store.rb +8 -3
  188. data/lib/active_record/suppressor.rb +58 -0
  189. data/lib/active_record/table_metadata.rb +68 -0
  190. data/lib/active_record/tasks/database_tasks.rb +58 -41
  191. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -20
  192. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
  193. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  194. data/lib/active_record/timestamp.rb +20 -9
  195. data/lib/active_record/touch_later.rb +58 -0
  196. data/lib/active_record/transactions.rb +138 -56
  197. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  198. data/lib/active_record/type/date.rb +2 -41
  199. data/lib/active_record/type/date_time.rb +2 -49
  200. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  201. data/lib/active_record/type/internal/timezone.rb +15 -0
  202. data/lib/active_record/type/serialized.rb +15 -14
  203. data/lib/active_record/type/time.rb +10 -16
  204. data/lib/active_record/type/type_map.rb +4 -4
  205. data/lib/active_record/type.rb +66 -17
  206. data/lib/active_record/type_caster/connection.rb +29 -0
  207. data/lib/active_record/type_caster/map.rb +19 -0
  208. data/lib/active_record/type_caster.rb +7 -0
  209. data/lib/active_record/validations/absence.rb +23 -0
  210. data/lib/active_record/validations/associated.rb +10 -3
  211. data/lib/active_record/validations/length.rb +24 -0
  212. data/lib/active_record/validations/presence.rb +11 -12
  213. data/lib/active_record/validations/uniqueness.rb +30 -29
  214. data/lib/active_record/validations.rb +33 -32
  215. data/lib/active_record.rb +7 -2
  216. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  217. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
  218. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
  219. data/lib/rails/generators/active_record/migration.rb +7 -0
  220. data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
  221. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  222. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  223. metadata +58 -34
  224. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  225. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  226. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  227. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  228. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  229. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  230. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  231. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  232. data/lib/active_record/type/big_integer.rb +0 -13
  233. data/lib/active_record/type/binary.rb +0 -50
  234. data/lib/active_record/type/boolean.rb +0 -31
  235. data/lib/active_record/type/decimal.rb +0 -50
  236. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  237. data/lib/active_record/type/decorator.rb +0 -14
  238. data/lib/active_record/type/float.rb +0 -19
  239. data/lib/active_record/type/integer.rb +0 -59
  240. data/lib/active_record/type/mutable.rb +0 -16
  241. data/lib/active_record/type/numeric.rb +0 -36
  242. data/lib/active_record/type/string.rb +0 -40
  243. data/lib/active_record/type/text.rb +0 -11
  244. data/lib/active_record/type/time_value.rb +0 -38
  245. data/lib/active_record/type/unsigned_integer.rb +0 -15
  246. data/lib/active_record/type/value.rb +0 -105
@@ -40,9 +40,9 @@ module ActiveRecord
40
40
  ar.aggregate_reflections = ar.aggregate_reflections.merge(name.to_s => reflection)
41
41
  end
42
42
 
43
- # \Reflection enables interrogating of Active Record classes and objects
44
- # about their associations and aggregations. This information can,
45
- # for example, be used in a form builder that takes an Active Record object
43
+ # \Reflection enables the ability to examine the associations and aggregations of
44
+ # Active Record classes and objects. This information, for example,
45
+ # can be used in a form builder that takes an Active Record object
46
46
  # and creates input fields for all of the attributes depending on their type
47
47
  # and displays the associations to other objects.
48
48
  #
@@ -62,20 +62,20 @@ module ActiveRecord
62
62
  aggregate_reflections[aggregation.to_s]
63
63
  end
64
64
 
65
- # Returns a Hash of name of the reflection as the key and a AssociationReflection as the value.
65
+ # Returns a Hash of name of the reflection as the key and an AssociationReflection as the value.
66
66
  #
67
67
  # Account.reflections # => {"balance" => AggregateReflection}
68
68
  #
69
- # @api public
70
69
  def reflections
71
70
  @__reflections ||= begin
72
71
  ref = {}
73
72
 
74
73
  _reflections.each do |name, reflection|
75
- parent_name, parent_reflection = reflection.parent_reflection
74
+ parent_reflection = reflection.parent_reflection
76
75
 
77
- if parent_name
78
- ref[parent_name] = parent_reflection
76
+ if parent_reflection
77
+ parent_name = parent_reflection.name
78
+ ref[parent_name.to_s] = parent_reflection
79
79
  else
80
80
  ref[name] = reflection
81
81
  end
@@ -95,10 +95,10 @@ module ActiveRecord
95
95
  # Account.reflect_on_all_associations # returns an array of all associations
96
96
  # Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
97
97
  #
98
- # @api public
99
98
  def reflect_on_all_associations(macro = nil)
100
99
  association_reflections = reflections.values
101
- macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
100
+ association_reflections.select! { |reflection| reflection.macro == macro } if macro
101
+ association_reflections
102
102
  end
103
103
 
104
104
  # Returns the AssociationReflection object for the +association+ (use the symbol).
@@ -106,31 +106,42 @@ module ActiveRecord
106
106
  # Account.reflect_on_association(:owner) # returns the owner AssociationReflection
107
107
  # Invoice.reflect_on_association(:line_items).macro # returns :has_many
108
108
  #
109
- # @api public
110
109
  def reflect_on_association(association)
111
110
  reflections[association.to_s]
112
111
  end
113
112
 
114
- # @api private
115
113
  def _reflect_on_association(association) #:nodoc:
116
114
  _reflections[association.to_s]
117
115
  end
118
116
 
119
117
  # Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
120
- #
121
- # @api public
122
118
  def reflect_on_all_autosave_associations
123
119
  reflections.values.select { |reflection| reflection.options[:autosave] }
124
120
  end
125
121
 
126
- def clear_reflections_cache #:nodoc:
122
+ def clear_reflections_cache # :nodoc:
127
123
  @__reflections = nil
128
124
  end
129
125
  end
130
126
 
131
- # Holds all the methods that are shared between MacroReflection, AssociationReflection
132
- # and ThroughReflection
127
+ # Holds all the methods that are shared between MacroReflection and ThroughReflection.
128
+ #
129
+ # AbstractReflection
130
+ # MacroReflection
131
+ # AggregateReflection
132
+ # AssociationReflection
133
+ # HasManyReflection
134
+ # HasOneReflection
135
+ # BelongsToReflection
136
+ # HasAndBelongsToManyReflection
137
+ # ThroughReflection
138
+ # PolymorphicReflection
139
+ # RuntimeReflection
133
140
  class AbstractReflection # :nodoc:
141
+ def through_reflection?
142
+ false
143
+ end
144
+
134
145
  def table_name
135
146
  klass.table_name
136
147
  end
@@ -159,17 +170,24 @@ module ActiveRecord
159
170
 
160
171
  JoinKeys = Struct.new(:key, :foreign_key) # :nodoc:
161
172
 
162
- def join_keys(assoc_klass)
173
+ def join_keys(association_klass)
163
174
  JoinKeys.new(foreign_key, active_record_primary_key)
164
175
  end
165
176
 
166
- def source_macro
167
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
168
- ActiveRecord::Base.source_macro is deprecated and will be removed
169
- without replacement.
170
- MSG
177
+ def constraints
178
+ scope_chain.flatten
179
+ end
171
180
 
172
- macro
181
+ def counter_cache_column
182
+ if belongs_to?
183
+ if options[:counter_cache] == true
184
+ "#{active_record.name.demodulize.underscore.pluralize}_count"
185
+ elsif options[:counter_cache]
186
+ options[:counter_cache].to_s
187
+ end
188
+ else
189
+ options[:counter_cache] ? options[:counter_cache].to_s : "#{name}_count"
190
+ end
173
191
  end
174
192
 
175
193
  def inverse_of
@@ -185,17 +203,54 @@ module ActiveRecord
185
203
  end
186
204
  end
187
205
  end
206
+
207
+ # This shit is nasty. We need to avoid the following situation:
208
+ #
209
+ # * An associated record is deleted via record.destroy
210
+ # * Hence the callbacks run, and they find a belongs_to on the record with a
211
+ # :counter_cache options which points back at our owner. So they update the
212
+ # counter cache.
213
+ # * In which case, we must make sure to *not* update the counter cache, or else
214
+ # it will be decremented twice.
215
+ #
216
+ # Hence this method.
217
+ def inverse_which_updates_counter_cache
218
+ return @inverse_which_updates_counter_cache if defined?(@inverse_which_updates_counter_cache)
219
+ @inverse_which_updates_counter_cache = klass.reflect_on_all_associations(:belongs_to).find do |inverse|
220
+ inverse.counter_cache_column == counter_cache_column
221
+ end
222
+ end
223
+ alias inverse_updates_counter_cache? inverse_which_updates_counter_cache
224
+
225
+ def inverse_updates_counter_in_memory?
226
+ inverse_of && inverse_which_updates_counter_cache == inverse_of
227
+ end
228
+
229
+ # Returns whether a counter cache should be used for this association.
230
+ #
231
+ # The counter_cache option must be given on either the owner or inverse
232
+ # association, and the column must be present on the owner.
233
+ def has_cached_counter?
234
+ options[:counter_cache] ||
235
+ inverse_which_updates_counter_cache && inverse_which_updates_counter_cache.options[:counter_cache] &&
236
+ !!active_record.columns_hash[counter_cache_column]
237
+ end
238
+
239
+ def counter_must_be_updated_by_has_many?
240
+ !inverse_updates_counter_in_memory? && has_cached_counter?
241
+ end
242
+
243
+ def alias_candidate(name)
244
+ "#{plural_name}_#{name}"
245
+ end
246
+
247
+ def chain
248
+ collect_join_chain
249
+ end
188
250
  end
251
+
189
252
  # Base class for AggregateReflection and AssociationReflection. Objects of
190
253
  # AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
191
- #
192
- # MacroReflection
193
- # AssociationReflection
194
- # AggregateReflection
195
- # HasManyReflection
196
- # HasOneReflection
197
- # BelongsToReflection
198
- # ThroughReflection
199
254
  class MacroReflection < AbstractReflection
200
255
  # Returns the name of the macro.
201
256
  #
@@ -228,7 +283,7 @@ module ActiveRecord
228
283
  def autosave=(autosave)
229
284
  @automatic_inverse_of = false
230
285
  @options[:autosave] = autosave
231
- _, parent_reflection = self.parent_reflection
286
+ parent_reflection = self.parent_reflection
232
287
  if parent_reflection
233
288
  parent_reflection.autosave = autosave
234
289
  end
@@ -296,7 +351,7 @@ module ActiveRecord
296
351
  end
297
352
 
298
353
  attr_reader :type, :foreign_type
299
- attr_accessor :parent_reflection # [:name, Reflection]
354
+ attr_accessor :parent_reflection # Reflection
300
355
 
301
356
  def initialize(name, scope, options, active_record)
302
357
  super
@@ -343,14 +398,6 @@ module ActiveRecord
343
398
  @active_record_primary_key ||= options[:primary_key] || primary_key(active_record)
344
399
  end
345
400
 
346
- def counter_cache_column
347
- if options[:counter_cache] == true
348
- "#{active_record.name.demodulize.underscore.pluralize}_count"
349
- elsif options[:counter_cache]
350
- options[:counter_cache].to_s
351
- end
352
- end
353
-
354
401
  def check_validity!
355
402
  check_validity_of_inverse!
356
403
  end
@@ -359,13 +406,10 @@ module ActiveRecord
359
406
  return unless scope
360
407
 
361
408
  if scope.arity > 0
362
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
409
+ raise ArgumentError, <<-MSG.squish
363
410
  The association scope '#{name}' is instance dependent (the scope
364
- block takes an argument). Preloading happens before the individual
365
- instances are created. This means that there is no instance being
366
- passed to the association scope. This will most likely result in
367
- broken or incorrect behavior. Joining, Preloading and eager loading
368
- of these associations is deprecated and will be removed in the future.
411
+ block takes an argument). Preloading instance dependent scopes is
412
+ not supported.
369
413
  MSG
370
414
  end
371
415
  end
@@ -385,10 +429,16 @@ module ActiveRecord
385
429
 
386
430
  # A chain of reflections from this one back to the owner. For more see the explanation in
387
431
  # ThroughReflection.
388
- def chain
432
+ def collect_join_chain
389
433
  [self]
390
434
  end
391
435
 
436
+ # This is for clearing cache on the reflection. Useful for tests that need to compare
437
+ # SQL queries on associations.
438
+ def clear_association_scope_cache # :nodoc:
439
+ @association_scope_cache.clear
440
+ end
441
+
392
442
  def nested?
393
443
  false
394
444
  end
@@ -399,6 +449,10 @@ module ActiveRecord
399
449
  scope ? [[scope]] : [[]]
400
450
  end
401
451
 
452
+ def has_scope?
453
+ scope
454
+ end
455
+
402
456
  def has_inverse?
403
457
  inverse_name
404
458
  end
@@ -444,28 +498,7 @@ module ActiveRecord
444
498
  # Returns +true+ if +self+ is a +has_one+ reflection.
445
499
  def has_one?; false; end
446
500
 
447
- def association_class
448
- case macro
449
- when :belongs_to
450
- if polymorphic?
451
- Associations::BelongsToPolymorphicAssociation
452
- else
453
- Associations::BelongsToAssociation
454
- end
455
- when :has_many
456
- if options[:through]
457
- Associations::HasManyThroughAssociation
458
- else
459
- Associations::HasManyAssociation
460
- end
461
- when :has_one
462
- if options[:through]
463
- Associations::HasOneThroughAssociation
464
- else
465
- Associations::HasOneAssociation
466
- end
467
- end
468
- end
501
+ def association_class; raise NotImplementedError; end
469
502
 
470
503
  def polymorphic?
471
504
  options[:polymorphic]
@@ -474,6 +507,18 @@ module ActiveRecord
474
507
  VALID_AUTOMATIC_INVERSE_MACROS = [:has_many, :has_one, :belongs_to]
475
508
  INVALID_AUTOMATIC_INVERSE_OPTIONS = [:conditions, :through, :polymorphic, :foreign_key]
476
509
 
510
+ def add_as_source(seed)
511
+ seed
512
+ end
513
+
514
+ def add_as_polymorphic_through(reflection, seed)
515
+ seed + [PolymorphicReflection.new(self, reflection)]
516
+ end
517
+
518
+ def add_as_through(seed)
519
+ seed + [self]
520
+ end
521
+
477
522
  protected
478
523
 
479
524
  def actual_source_reflection # FIXME: this is a horrible name
@@ -483,14 +528,7 @@ module ActiveRecord
483
528
  private
484
529
 
485
530
  def calculate_constructable(macro, options)
486
- case macro
487
- when :belongs_to
488
- !polymorphic?
489
- when :has_one
490
- !options[:through]
491
- else
492
- true
493
- end
531
+ true
494
532
  end
495
533
 
496
534
  # Attempts to find the inverse association name automatically.
@@ -506,7 +544,7 @@ module ActiveRecord
506
544
  end
507
545
  end
508
546
 
509
- # returns either nil or the inverse association name that it finds.
547
+ # returns either false or the inverse association name that it finds.
510
548
  def automatic_inverse_of
511
549
  if can_find_inverse_of_automatically?(self)
512
550
  inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name.demodulize).to_sym
@@ -583,42 +621,66 @@ module ActiveRecord
583
621
  end
584
622
 
585
623
  class HasManyReflection < AssociationReflection # :nodoc:
586
- def initialize(name, scope, options, active_record)
587
- super(name, scope, options, active_record)
588
- end
589
-
590
624
  def macro; :has_many; end
591
625
 
592
626
  def collection?; true; end
593
- end
594
627
 
595
- class HasOneReflection < AssociationReflection # :nodoc:
596
- def initialize(name, scope, options, active_record)
597
- super(name, scope, options, active_record)
628
+ def association_class
629
+ if options[:through]
630
+ Associations::HasManyThroughAssociation
631
+ else
632
+ Associations::HasManyAssociation
633
+ end
598
634
  end
635
+ end
599
636
 
637
+ class HasOneReflection < AssociationReflection # :nodoc:
600
638
  def macro; :has_one; end
601
639
 
602
640
  def has_one?; true; end
603
- end
604
641
 
605
- class BelongsToReflection < AssociationReflection # :nodoc:
606
- def initialize(name, scope, options, active_record)
607
- super(name, scope, options, active_record)
642
+ def association_class
643
+ if options[:through]
644
+ Associations::HasOneThroughAssociation
645
+ else
646
+ Associations::HasOneAssociation
647
+ end
608
648
  end
609
649
 
650
+ private
651
+
652
+ def calculate_constructable(macro, options)
653
+ !options[:through]
654
+ end
655
+ end
656
+
657
+ class BelongsToReflection < AssociationReflection # :nodoc:
610
658
  def macro; :belongs_to; end
611
659
 
612
660
  def belongs_to?; true; end
613
661
 
614
- def join_keys(assoc_klass)
615
- key = polymorphic? ? association_primary_key(assoc_klass) : association_primary_key
662
+ def association_class
663
+ if polymorphic?
664
+ Associations::BelongsToPolymorphicAssociation
665
+ else
666
+ Associations::BelongsToAssociation
667
+ end
668
+ end
669
+
670
+ def join_keys(association_klass)
671
+ key = polymorphic? ? association_primary_key(association_klass) : association_primary_key
616
672
  JoinKeys.new(key, foreign_key)
617
673
  end
618
674
 
619
675
  def join_id_for(owner) # :nodoc:
620
676
  owner[foreign_key]
621
677
  end
678
+
679
+ private
680
+
681
+ def calculate_constructable(macro, options)
682
+ !polymorphic?
683
+ end
622
684
  end
623
685
 
624
686
  class HasAndBelongsToManyReflection < AssociationReflection # :nodoc:
@@ -646,6 +708,10 @@ module ActiveRecord
646
708
  @source_reflection_name = delegate_reflection.options[:source]
647
709
  end
648
710
 
711
+ def through_reflection?
712
+ true
713
+ end
714
+
649
715
  def klass
650
716
  @klass ||= delegate_reflection.compute_class(class_name)
651
717
  end
@@ -704,14 +770,16 @@ module ActiveRecord
704
770
  # # => [<ActiveRecord::Reflection::ThroughReflection: @delegate_reflection=#<ActiveRecord::Reflection::HasManyReflection: @name=:tags...>,
705
771
  # <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @options={}, @active_record=Post>]
706
772
  #
707
- def chain
708
- @chain ||= begin
709
- a = source_reflection.chain
710
- b = through_reflection.chain
711
- chain = a + b
712
- chain[0] = self # Use self so we don't lose the information from :source_type
713
- chain
714
- end
773
+ def collect_join_chain
774
+ collect_join_reflections [self]
775
+ end
776
+
777
+ # This is for clearing cache on the reflection. Useful for tests that need to compare
778
+ # SQL queries on associations.
779
+ def clear_association_scope_cache # :nodoc:
780
+ delegate_reflection.clear_association_scope_cache
781
+ source_reflection.clear_association_scope_cache
782
+ through_reflection.clear_association_scope_cache
715
783
  end
716
784
 
717
785
  # Consider the following example:
@@ -755,23 +823,19 @@ module ActiveRecord
755
823
  end
756
824
  end
757
825
 
758
- def join_keys(assoc_klass)
759
- source_reflection.join_keys(assoc_klass)
826
+ def has_scope?
827
+ scope || options[:source_type] ||
828
+ source_reflection.has_scope? ||
829
+ through_reflection.has_scope?
760
830
  end
761
831
 
762
- # The macro used by the source association
763
- def source_macro
764
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
765
- ActiveRecord::Base.source_macro is deprecated and will be removed
766
- without replacement.
767
- MSG
768
-
769
- source_reflection.source_macro
832
+ def join_keys(association_klass)
833
+ source_reflection.join_keys(association_klass)
770
834
  end
771
835
 
772
836
  # A through association is nested if there would be more than one join table
773
837
  def nested?
774
- chain.length > 2
838
+ source_reflection.through_reflection? || through_reflection.through_reflection?
775
839
  end
776
840
 
777
841
  # We want to use the klass from this reflection, rather than just delegate straight to
@@ -801,7 +865,7 @@ module ActiveRecord
801
865
  def source_reflection_name # :nodoc:
802
866
  return @source_reflection_name if @source_reflection_name
803
867
 
804
- names = [name.to_s.singularize, name].collect { |n| n.to_sym }.uniq
868
+ names = [name.to_s.singularize, name].collect(&:to_sym).uniq
805
869
  names = names.find_all { |n|
806
870
  through_reflection.klass._reflect_on_association(n)
807
871
  }
@@ -810,7 +874,7 @@ module ActiveRecord
810
874
  example_options = options.dup
811
875
  example_options[:source] = source_reflection_names.first
812
876
  ActiveSupport::Deprecation.warn \
813
- "Ambiguous source reflection for through association. Please " \
877
+ "Ambiguous source reflection for through association. Please " \
814
878
  "specify a :source directive on your declaration like:\n" \
815
879
  "\n" \
816
880
  " class #{active_record.name} < ActiveRecord::Base\n" \
@@ -865,6 +929,33 @@ module ActiveRecord
865
929
  check_validity_of_inverse!
866
930
  end
867
931
 
932
+ def constraints
933
+ scope_chain = source_reflection.constraints
934
+ scope_chain << scope if scope
935
+ scope_chain
936
+ end
937
+
938
+ def add_as_source(seed)
939
+ collect_join_reflections seed
940
+ end
941
+
942
+ def add_as_polymorphic_through(reflection, seed)
943
+ collect_join_reflections(seed + [PolymorphicReflection.new(self, reflection)])
944
+ end
945
+
946
+ def add_as_through(seed)
947
+ collect_join_reflections(seed + [self])
948
+ end
949
+
950
+ def collect_join_reflections(seed)
951
+ a = source_reflection.add_as_source seed
952
+ if options[:source_type]
953
+ through_reflection.add_as_polymorphic_through self, a
954
+ else
955
+ through_reflection.add_as_through a
956
+ end
957
+ end
958
+
868
959
  protected
869
960
 
870
961
  def actual_source_reflection # FIXME: this is a horrible name
@@ -889,5 +980,81 @@ module ActiveRecord
889
980
  delegate(*delegate_methods, to: :delegate_reflection)
890
981
 
891
982
  end
983
+
984
+ class PolymorphicReflection < ThroughReflection # :nodoc:
985
+ def initialize(reflection, previous_reflection)
986
+ @reflection = reflection
987
+ @previous_reflection = previous_reflection
988
+ end
989
+
990
+ def klass
991
+ @reflection.klass
992
+ end
993
+
994
+ def scope
995
+ @reflection.scope
996
+ end
997
+
998
+ def table_name
999
+ @reflection.table_name
1000
+ end
1001
+
1002
+ def plural_name
1003
+ @reflection.plural_name
1004
+ end
1005
+
1006
+ def join_keys(association_klass)
1007
+ @reflection.join_keys(association_klass)
1008
+ end
1009
+
1010
+ def type
1011
+ @reflection.type
1012
+ end
1013
+
1014
+ def constraints
1015
+ @reflection.constraints + [source_type_info]
1016
+ end
1017
+
1018
+ def source_type_info
1019
+ type = @previous_reflection.foreign_type
1020
+ source_type = @previous_reflection.options[:source_type]
1021
+ lambda { |object| where(type => source_type) }
1022
+ end
1023
+ end
1024
+
1025
+ class RuntimeReflection < PolymorphicReflection # :nodoc:
1026
+ attr_accessor :next
1027
+
1028
+ def initialize(reflection, association)
1029
+ @reflection = reflection
1030
+ @association = association
1031
+ end
1032
+
1033
+ def klass
1034
+ @association.klass
1035
+ end
1036
+
1037
+ def table_name
1038
+ klass.table_name
1039
+ end
1040
+
1041
+ def constraints
1042
+ @reflection.constraints
1043
+ end
1044
+
1045
+ def source_type_info
1046
+ @reflection.source_type_info
1047
+ end
1048
+
1049
+ def alias_candidate(name)
1050
+ "#{plural_name}_#{name}_join"
1051
+ end
1052
+
1053
+ def alias_name
1054
+ Arel::Table.new(table_name)
1055
+ end
1056
+
1057
+ def all_includes; yield; end
1058
+ end
892
1059
  end
893
1060
  end
@@ -0,0 +1,67 @@
1
+ module ActiveRecord
2
+ module Batches
3
+ class BatchEnumerator
4
+ include Enumerable
5
+
6
+ def initialize(of: 1000, start: nil, finish: nil, relation:) #:nodoc:
7
+ @of = of
8
+ @relation = relation
9
+ @start = start
10
+ @finish = finish
11
+ end
12
+
13
+ # Looping through a collection of records from the database (using the
14
+ # +all+ method, for example) is very inefficient since it will try to
15
+ # instantiate all the objects at once.
16
+ #
17
+ # In that case, batch processing methods allow you to work with the
18
+ # records in batches, thereby greatly reducing memory consumption.
19
+ #
20
+ # Person.in_batches.each_record do |person|
21
+ # person.do_awesome_stuff
22
+ # end
23
+ #
24
+ # Person.where("age > 21").in_batches(of: 10).each_record do |person|
25
+ # person.party_all_night!
26
+ # end
27
+ #
28
+ # If you do not provide a block to #each_record, it will return an Enumerator
29
+ # for chaining with other methods:
30
+ #
31
+ # Person.in_batches.each_record.with_index do |person, index|
32
+ # person.award_trophy(index + 1)
33
+ # end
34
+ def each_record
35
+ return to_enum(:each_record) unless block_given?
36
+
37
+ @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: true).each do |relation|
38
+ relation.records.each { |record| yield record }
39
+ end
40
+ end
41
+
42
+ # Delegates #delete_all, #update_all, #destroy_all methods to each batch.
43
+ #
44
+ # People.in_batches.delete_all
45
+ # People.where('age < 10').in_batches.destroy_all
46
+ # People.in_batches.update_all('age = age + 1')
47
+ [:delete_all, :update_all, :destroy_all].each do |method|
48
+ define_method(method) do |*args, &block|
49
+ @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: false).each do |relation|
50
+ relation.send(method, *args, &block)
51
+ end
52
+ end
53
+ end
54
+
55
+ # Yields an ActiveRecord::Relation object for each batch of records.
56
+ #
57
+ # Person.in_batches.each do |relation|
58
+ # relation.update_all(awesome: true)
59
+ # end
60
+ def each
61
+ enum = @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: false)
62
+ return enum.each { |relation| yield relation } if block_given?
63
+ enum
64
+ end
65
+ end
66
+ end
67
+ end