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
@@ -28,15 +28,21 @@ module ActiveRecord
28
28
  # Implements the reader method, e.g. foo.items for Foo.has_many :items
29
29
  def reader(force_reload = false)
30
30
  if force_reload
31
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
32
+ Passing an argument to force an association to reload is now
33
+ deprecated and will be removed in Rails 5.1. Please call `reload`
34
+ on the result collection proxy instead.
35
+ MSG
36
+
31
37
  klass.uncached { reload }
32
38
  elsif stale_target?
33
39
  reload
34
40
  end
35
41
 
36
- if owner.new_record?
42
+ if null_scope?
37
43
  # Cache the proxy separately before the owner has an id
38
44
  # or else a post-save proxy will still lack the id
39
- @new_record_proxy ||= CollectionProxy.create(klass, self)
45
+ @null_proxy ||= CollectionProxy.create(klass, self)
40
46
  else
41
47
  @proxy ||= CollectionProxy.create(klass, self)
42
48
  end
@@ -54,17 +60,22 @@ module ActiveRecord
54
60
  record.send(reflection.association_primary_key)
55
61
  end
56
62
  else
57
- column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}"
58
- scope.pluck(column)
63
+ @association_ids ||= (
64
+ column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}"
65
+ scope.pluck(column)
66
+ )
59
67
  end
60
68
  end
61
69
 
62
70
  # Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
63
71
  def ids_writer(ids)
64
72
  pk_type = reflection.primary_key_type
65
- ids = Array(ids).reject { |id| id.blank? }
66
- ids.map! { |i| pk_type.type_cast_from_user(i) }
67
- replace(klass.find(ids).index_by { |r| r.id }.values_at(*ids))
73
+ ids = Array(ids).reject(&:blank?)
74
+ ids.map! { |i| pk_type.cast(i) }
75
+ records = klass.where(reflection.association_primary_key => ids).index_by do |r|
76
+ r.send(reflection.association_primary_key)
77
+ end.values_at(*ids)
78
+ replace(records)
68
79
  end
69
80
 
70
81
  def reset
@@ -125,6 +136,14 @@ module ActiveRecord
125
136
  first_nth_or_last(:forty_two, *args)
126
137
  end
127
138
 
139
+ def third_to_last(*args)
140
+ first_nth_or_last(:third_to_last, *args)
141
+ end
142
+
143
+ def second_to_last(*args)
144
+ first_nth_or_last(:second_to_last, *args)
145
+ end
146
+
128
147
  def last(*args)
129
148
  first_nth_or_last(:last, *args)
130
149
  end
@@ -161,6 +180,7 @@ module ActiveRecord
161
180
  # be chained. Since << flattens its argument list and inserts each record,
162
181
  # +push+ and +concat+ behave identically.
163
182
  def concat(*records)
183
+ records = records.flatten
164
184
  if owner.new_record?
165
185
  load_target
166
186
  concat_records(records)
@@ -228,11 +248,7 @@ module ActiveRecord
228
248
 
229
249
  # Count all records using SQL. Construct options and pass them with
230
250
  # scope to the target class's +count+.
231
- def count(column_name = nil, count_options = {})
232
- # TODO: Remove count_options argument as soon we remove support to
233
- # activerecord-deprecated_finders.
234
- column_name, count_options = nil, column_name if column_name.is_a?(Hash)
235
-
251
+ def count(column_name = nil)
236
252
  relation = scope
237
253
  if association_scope.distinct_value
238
254
  # This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
@@ -264,7 +280,7 @@ module ActiveRecord
264
280
  _options = records.extract_options!
265
281
  dependent = _options[:dependent] || options[:dependent]
266
282
 
267
- records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
283
+ records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
268
284
  delete_or_destroy(records, dependent)
269
285
  end
270
286
 
@@ -275,7 +291,7 @@ module ActiveRecord
275
291
  # +:dependent+ option.
276
292
  def destroy(*records)
277
293
  return if records.empty?
278
- records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
294
+ records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
279
295
  delete_or_destroy(records, :destroy)
280
296
  end
281
297
 
@@ -299,7 +315,7 @@ module ActiveRecord
299
315
  elsif !loaded? && !association_scope.group_values.empty?
300
316
  load_target.size
301
317
  elsif !loaded? && !association_scope.distinct_value && target.is_a?(Array)
302
- unsaved_records = target.select { |r| r.new_record? }
318
+ unsaved_records = target.select(&:new_record?)
303
319
  unsaved_records.size + count_records
304
320
  else
305
321
  count_records
@@ -332,7 +348,8 @@ module ActiveRecord
332
348
  end
333
349
 
334
350
  # Returns true if the collections is not empty.
335
- # Equivalent to +!collection.empty?+.
351
+ # If block given, loads all records and checks for one or more matches.
352
+ # Otherwise, equivalent to +!collection.empty?+.
336
353
  def any?
337
354
  if block_given?
338
355
  load_target.any? { |*block_args| yield(*block_args) }
@@ -342,7 +359,8 @@ module ActiveRecord
342
359
  end
343
360
 
344
361
  # Returns true if the collection has more than 1 record.
345
- # Equivalent to +collection.size > 1+.
362
+ # If block given, loads all records and checks for two or more matches.
363
+ # Otherwise, equivalent to +collection.size > 1+.
346
364
  def many?
347
365
  if block_given?
348
366
  load_target.many? { |*block_args| yield(*block_args) }
@@ -371,6 +389,8 @@ module ActiveRecord
371
389
  replace_common_records_in_memory(other_array, original_target)
372
390
  if other_array != original_target
373
391
  transaction { replace_records(other_array, original_target) }
392
+ else
393
+ other_array
374
394
  end
375
395
  end
376
396
  end
@@ -405,12 +425,16 @@ module ActiveRecord
405
425
 
406
426
  def replace_on_target(record, index, skip_callbacks)
407
427
  callback(:before_add, record) unless skip_callbacks
428
+
429
+ was_loaded = loaded?
408
430
  yield(record) if block_given?
409
431
 
410
- if index
411
- @target[index] = record
412
- else
413
- @target << record
432
+ unless !was_loaded && loaded?
433
+ if index
434
+ @target[index] = record
435
+ else
436
+ @target << record
437
+ end
414
438
  end
415
439
 
416
440
  callback(:after_add, record) unless skip_callbacks
@@ -510,7 +534,7 @@ module ActiveRecord
510
534
  def delete_or_destroy(records, method)
511
535
  records = records.flatten
512
536
  records.each { |record| raise_on_type_mismatch!(record) }
513
- existing_records = records.reject { |r| r.new_record? }
537
+ existing_records = records.reject(&:new_record?)
514
538
 
515
539
  if existing_records.empty?
516
540
  remove_records(existing_records, records, method)
@@ -557,7 +581,7 @@ module ActiveRecord
557
581
  def concat_records(records, should_raise = false)
558
582
  result = true
559
583
 
560
- records.flatten.each do |record|
584
+ records.each do |record|
561
585
  raise_on_type_mismatch!(record)
562
586
  add_to_target(record) do |rec|
563
587
  result &&= insert_record(rec, true, should_raise) unless owner.new_record?
@@ -601,13 +625,8 @@ module ActiveRecord
601
625
  if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
602
626
  assoc = owner.association(reflection.through_reflection.name)
603
627
  assoc.reader.any? { |source|
604
- target_association = source.send(reflection.source_reflection.name)
605
-
606
- if target_association.respond_to?(:include?)
607
- target_association.include?(record)
608
- else
609
- target_association == record
610
- end
628
+ target_reflection = source.send(reflection.source_reflection.name)
629
+ target_reflection.respond_to?(:include?) ? target_reflection.include?(record) : target_reflection == record
611
630
  } || target.include?(record)
612
631
  else
613
632
  target.include?(record)
@@ -618,7 +637,7 @@ module ActiveRecord
618
637
  # specified, then #find scans the entire collection.
619
638
  def find_by_scan(*args)
620
639
  expects_array = args.first.kind_of?(Array)
621
- ids = args.flatten.compact.map{ |arg| arg.to_s }.uniq
640
+ ids = args.flatten.compact.map(&:to_s).uniq
622
641
 
623
642
  if ids.size == 1
624
643
  id = ids.first
@@ -33,7 +33,7 @@ module ActiveRecord
33
33
 
34
34
  def initialize(klass, association) #:nodoc:
35
35
  @association = association
36
- super klass, klass.arel_table
36
+ super klass, klass.arel_table, klass.predicate_builder
37
37
  merge! association.scope(nullify: false)
38
38
  end
39
39
 
@@ -112,7 +112,7 @@ module ActiveRecord
112
112
  end
113
113
 
114
114
  # Finds an object in the collection responding to the +id+. Uses the same
115
- # rules as <tt>ActiveRecord::Base.find</tt>. Returns <tt>ActiveRecord::RecordNotFound</tt>
115
+ # rules as ActiveRecord::Base.find. Returns ActiveRecord::RecordNotFound
116
116
  # error if the object cannot be found.
117
117
  #
118
118
  # class Person < ActiveRecord::Base
@@ -127,7 +127,7 @@ module ActiveRecord
127
127
  # # ]
128
128
  #
129
129
  # person.pets.find(1) # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
130
- # person.pets.find(4) # => ActiveRecord::RecordNotFound: Couldn't find Pet with id=4
130
+ # person.pets.find(4) # => ActiveRecord::RecordNotFound: Couldn't find Pet with 'id'=4
131
131
  #
132
132
  # person.pets.find(2) { |pet| pet.name.downcase! }
133
133
  # # => #<Pet id: 2, name: "fancy-fancy", person_id: 1>
@@ -171,32 +171,42 @@ module ActiveRecord
171
171
  @association.first(*args)
172
172
  end
173
173
 
174
- # Same as +first+ except returns only the second record.
174
+ # Same as #first except returns only the second record.
175
175
  def second(*args)
176
176
  @association.second(*args)
177
177
  end
178
178
 
179
- # Same as +first+ except returns only the third record.
179
+ # Same as #first except returns only the third record.
180
180
  def third(*args)
181
181
  @association.third(*args)
182
182
  end
183
183
 
184
- # Same as +first+ except returns only the fourth record.
184
+ # Same as #first except returns only the fourth record.
185
185
  def fourth(*args)
186
186
  @association.fourth(*args)
187
187
  end
188
188
 
189
- # Same as +first+ except returns only the fifth record.
189
+ # Same as #first except returns only the fifth record.
190
190
  def fifth(*args)
191
191
  @association.fifth(*args)
192
192
  end
193
193
 
194
- # Same as +first+ except returns only the forty second record.
194
+ # Same as #first except returns only the forty second record.
195
195
  # Also known as accessing "the reddit".
196
196
  def forty_two(*args)
197
197
  @association.forty_two(*args)
198
198
  end
199
199
 
200
+ # Same as #first except returns only the third-to-last record.
201
+ def third_to_last(*args)
202
+ @association.third_to_last(*args)
203
+ end
204
+
205
+ # Same as #first except returns only the second-to-last record.
206
+ def second_to_last(*args)
207
+ @association.second_to_last(*args)
208
+ end
209
+
200
210
  # Returns the last record, or the last +n+ records, from the collection.
201
211
  # If the collection is empty, the first form returns +nil+, and the second
202
212
  # form returns an empty array.
@@ -227,6 +237,31 @@ module ActiveRecord
227
237
  @association.last(*args)
228
238
  end
229
239
 
240
+ # Gives a record (or N records if a parameter is supplied) from the collection
241
+ # using the same rules as <tt>ActiveRecord::Base.take</tt>.
242
+ #
243
+ # class Person < ActiveRecord::Base
244
+ # has_many :pets
245
+ # end
246
+ #
247
+ # person.pets
248
+ # # => [
249
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
250
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
251
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
252
+ # # ]
253
+ #
254
+ # person.pets.take # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
255
+ #
256
+ # person.pets.take(2)
257
+ # # => [
258
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
259
+ # # #<Pet id: 2, name: "Spook", person_id: 1>
260
+ # # ]
261
+ #
262
+ # another_person_without.pets # => []
263
+ # another_person_without.pets.take # => nil
264
+ # another_person_without.pets.take(2) # => []
230
265
  def take(n = nil)
231
266
  @association.take(n)
232
267
  end
@@ -290,7 +325,7 @@ module ActiveRecord
290
325
  @association.create(attributes, &block)
291
326
  end
292
327
 
293
- # Like +create+, except that if the record is invalid, raises an exception.
328
+ # Like #create, except that if the record is invalid, raises an exception.
294
329
  #
295
330
  # class Person
296
331
  # has_many :pets
@@ -307,8 +342,8 @@ module ActiveRecord
307
342
  end
308
343
 
309
344
  # Add one or more records to the collection by setting their foreign keys
310
- # to the association's primary key. Since << flattens its argument list and
311
- # inserts each record, +push+ and +concat+ behave identically. Returns +self+
345
+ # to the association's primary key. Since #<< flattens its argument list and
346
+ # inserts each record, +push+ and #concat behave identically. Returns +self+
312
347
  # so method calls may be chained.
313
348
  #
314
349
  # class Person < ActiveRecord::Base
@@ -364,7 +399,7 @@ module ActiveRecord
364
399
  # specified by the +:dependent+ option. If no +:dependent+ option is given,
365
400
  # then it will follow the default strategy.
366
401
  #
367
- # For +has_many :through+ associations, the default deletion strategy is
402
+ # For <tt>has_many :through</tt> associations, the default deletion strategy is
368
403
  # +:delete_all+.
369
404
  #
370
405
  # For +has_many+ associations, the default deletion strategy is +:nullify+.
@@ -399,7 +434,7 @@ module ActiveRecord
399
434
  # # #<Pet id: 3, name: "Choo-Choo", person_id: nil>
400
435
  # # ]
401
436
  #
402
- # Both +has_many+ and +has_many :through+ dependencies default to the
437
+ # Both +has_many+ and <tt>has_many :through</tt> dependencies default to the
403
438
  # +:delete_all+ strategy if the +:dependent+ option is set to +:destroy+.
404
439
  # Records are not instantiated and callbacks will not be fired.
405
440
  #
@@ -418,7 +453,7 @@ module ActiveRecord
418
453
  # person.pets.delete_all
419
454
  #
420
455
  # Pet.find(1, 2, 3)
421
- # # => ActiveRecord::RecordNotFound
456
+ # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
422
457
  #
423
458
  # If it is set to <tt>:delete_all</tt>, all the objects are deleted
424
459
  # *without* calling their +destroy+ method.
@@ -438,7 +473,7 @@ module ActiveRecord
438
473
  # person.pets.delete_all
439
474
  #
440
475
  # Pet.find(1, 2, 3)
441
- # # => ActiveRecord::RecordNotFound
476
+ # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
442
477
  def delete_all(dependent = nil)
443
478
  @association.delete_all(dependent)
444
479
  end
@@ -475,7 +510,7 @@ module ActiveRecord
475
510
  # then it will follow the default strategy. Returns an array with the
476
511
  # deleted records.
477
512
  #
478
- # For +has_many :through+ associations, the default deletion strategy is
513
+ # For <tt>has_many :through</tt> associations, the default deletion strategy is
479
514
  # +:delete_all+.
480
515
  #
481
516
  # For +has_many+ associations, the default deletion strategy is +:nullify+.
@@ -532,7 +567,7 @@ module ActiveRecord
532
567
  # # => [#<Pet id: 2, name: "Spook", person_id: 1>]
533
568
  #
534
569
  # Pet.find(1, 3)
535
- # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (1, 3)
570
+ # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 3)
536
571
  #
537
572
  # If it is set to <tt>:delete_all</tt>, all the +records+ are deleted
538
573
  # *without* calling their +destroy+ method.
@@ -560,9 +595,9 @@ module ActiveRecord
560
595
  # # ]
561
596
  #
562
597
  # Pet.find(1)
563
- # # => ActiveRecord::RecordNotFound: Couldn't find Pet with id=1
598
+ # # => ActiveRecord::RecordNotFound: Couldn't find Pet with 'id'=1
564
599
  #
565
- # You can pass +Fixnum+ or +String+ values, it finds the records
600
+ # You can pass +Integer+ or +String+ values, it finds the records
566
601
  # responding to the +id+ and executes delete on them.
567
602
  #
568
603
  # class Person < ActiveRecord::Base
@@ -624,9 +659,9 @@ module ActiveRecord
624
659
  # person.pets.size # => 0
625
660
  # person.pets # => []
626
661
  #
627
- # Pet.find(1, 2, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (1, 2, 3)
662
+ # Pet.find(1, 2, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
628
663
  #
629
- # You can pass +Fixnum+ or +String+ values, it finds the records
664
+ # You can pass +Integer+ or +String+ values, it finds the records
630
665
  # responding to the +id+ and then deletes them from the database.
631
666
  #
632
667
  # person.pets.size # => 3
@@ -656,7 +691,7 @@ module ActiveRecord
656
691
  # person.pets.size # => 0
657
692
  # person.pets # => []
658
693
  #
659
- # Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (4, 5, 6)
694
+ # Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (4, 5, 6)
660
695
  def destroy(*records)
661
696
  @association.destroy(*records)
662
697
  end
@@ -693,10 +728,8 @@ module ActiveRecord
693
728
  # # #<Pet id: 2, name: "Spook", person_id: 1>,
694
729
  # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
695
730
  # # ]
696
- def count(column_name = nil, options = {})
697
- # TODO: Remove options argument as soon we remove support to
698
- # activerecord-deprecated_finders.
699
- @association.count(column_name, options)
731
+ def count(column_name = nil)
732
+ @association.count(column_name)
700
733
  end
701
734
 
702
735
  # Returns the size of the collection. If the collection hasn't been loaded,
@@ -783,7 +816,7 @@ module ActiveRecord
783
816
  # person.pets.any? # => false
784
817
  #
785
818
  # person.pets << Pet.new(name: 'Snoop')
786
- # person.pets.count # => 0
819
+ # person.pets.count # => 1
787
820
  # person.pets.any? # => true
788
821
  #
789
822
  # You can also pass a +block+ to define criteria. The behavior
@@ -858,7 +891,7 @@ module ActiveRecord
858
891
  !!@association.include?(record)
859
892
  end
860
893
 
861
- def arel
894
+ def arel #:nodoc:
862
895
  scope.arel
863
896
  end
864
897
 
@@ -946,6 +979,10 @@ module ActiveRecord
946
979
  end
947
980
  alias_method :to_a, :to_ary
948
981
 
982
+ def records # :nodoc:
983
+ load_target
984
+ end
985
+
949
986
  # Adds one or more +records+ to the collection by setting their foreign keys
950
987
  # to the association's primary key. Returns +self+, so several appends may be
951
988
  # chained together.
@@ -973,12 +1010,15 @@ module ActiveRecord
973
1010
  alias_method :append, :<<
974
1011
 
975
1012
  def prepend(*args)
976
- raise NoMethodError, "prepend on association is not defined. Please use << or append"
1013
+ raise NoMethodError, "prepend on association is not defined. Please use <<, push or append"
977
1014
  end
978
1015
 
979
1016
  # Equivalent to +delete_all+. The difference is that returns +self+, instead
980
1017
  # of an array with the deleted objects, so methods can be chained. See
981
1018
  # +delete_all+ for more information.
1019
+ # Note that because +delete_all+ removes records by directly
1020
+ # running an SQL query into the database, the +updated_at+ column of
1021
+ # the object is not changed.
982
1022
  def clear
983
1023
  delete_all
984
1024
  self
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord::Associations
2
- module ForeignAssociation
2
+ module ForeignAssociation # :nodoc:
3
3
  def foreign_key_present?
4
4
  if reflection.klass.primary_key
5
5
  owner.attribute_present?(reflection.active_record_primary_key)
@@ -15,9 +15,16 @@ module ActiveRecord
15
15
 
16
16
  when :restrict_with_error
17
17
  unless empty?
18
- record = klass.human_attribute_name(reflection.name).downcase
19
- owner.errors.add(:base, :"restrict_dependent_destroy.many", record: record)
20
- false
18
+ record = owner.class.human_attribute_name(reflection.name).downcase
19
+ message = owner.errors.generate_message(:base, :'restrict_dependent_destroy.many', record: record, raise: true) rescue nil
20
+ if message
21
+ ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
22
+ The error key `:'restrict_dependent_destroy.many'` has been deprecated and will be removed in Rails 5.1.
23
+ Please use `:'restrict_dependent_destroy.has_many'` instead.
24
+ MESSAGE
25
+ end
26
+ owner.errors.add(:base, message || :'restrict_dependent_destroy.has_many', record: record)
27
+ throw(:abort)
21
28
  end
22
29
 
23
30
  else
@@ -43,7 +50,7 @@ module ActiveRecord
43
50
  end
44
51
 
45
52
  def empty?
46
- if has_cached_counter?
53
+ if reflection.has_cached_counter?
47
54
  size.zero?
48
55
  else
49
56
  super
@@ -66,8 +73,8 @@ module ActiveRecord
66
73
  # If the collection is empty the target is set to an empty array and
67
74
  # the loaded flag is set to true as well.
68
75
  def count_records
69
- count = if has_cached_counter?
70
- owner._read_attribute cached_counter_attribute_name
76
+ count = if reflection.has_cached_counter?
77
+ owner._read_attribute reflection.counter_cache_column
71
78
  else
72
79
  scope.count
73
80
  end
@@ -80,78 +87,20 @@ module ActiveRecord
80
87
  [association_scope.limit_value, count].compact.min
81
88
  end
82
89
 
83
-
84
- # Returns whether a counter cache should be used for this association.
85
- #
86
- # The counter_cache option must be given on either the owner or inverse
87
- # association, and the column must be present on the owner.
88
- def has_cached_counter?(reflection = reflection())
89
- if reflection.options[:counter_cache] || (inverse = inverse_which_updates_counter_cache(reflection)) && inverse.options[:counter_cache]
90
- owner.attribute_present?(cached_counter_attribute_name(reflection))
91
- end
92
- end
93
-
94
- def cached_counter_attribute_name(reflection = reflection())
95
- if reflection.options[:counter_cache]
96
- reflection.options[:counter_cache].to_s
97
- else
98
- "#{reflection.name}_count"
99
- end
100
- end
101
-
102
90
  def update_counter(difference, reflection = reflection())
103
- update_counter_in_database(difference, reflection)
104
- update_counter_in_memory(difference, reflection)
105
- end
106
-
107
- def update_counter_in_database(difference, reflection = reflection())
108
- if has_cached_counter?(reflection)
109
- counter = cached_counter_attribute_name(reflection)
110
- owner.class.update_counters(owner.id, counter => difference)
91
+ if reflection.has_cached_counter?
92
+ owner.increment!(reflection.counter_cache_column, difference)
111
93
  end
112
94
  end
113
95
 
114
96
  def update_counter_in_memory(difference, reflection = reflection())
115
- if counter_must_be_updated_by_has_many?(reflection)
116
- counter = cached_counter_attribute_name(reflection)
117
- owner[counter] += difference
118
- owner.send(:clear_attribute_changes, counter) # eww
97
+ if reflection.counter_must_be_updated_by_has_many?
98
+ counter = reflection.counter_cache_column
99
+ owner.increment(counter, difference)
100
+ owner.send(:clear_attribute_change, counter) # eww
119
101
  end
120
102
  end
121
103
 
122
- # This shit is nasty. We need to avoid the following situation:
123
- #
124
- # * An associated record is deleted via record.destroy
125
- # * Hence the callbacks run, and they find a belongs_to on the record with a
126
- # :counter_cache options which points back at our owner. So they update the
127
- # counter cache.
128
- # * In which case, we must make sure to *not* update the counter cache, or else
129
- # it will be decremented twice.
130
- #
131
- # Hence this method.
132
- def inverse_which_updates_counter_cache(reflection = reflection())
133
- counter_name = cached_counter_attribute_name(reflection)
134
- inverse_which_updates_counter_named(counter_name, reflection)
135
- end
136
- alias inverse_updates_counter_cache? inverse_which_updates_counter_cache
137
-
138
- def inverse_which_updates_counter_named(counter_name, reflection)
139
- reflection.klass._reflections.values.find { |inverse_reflection|
140
- inverse_reflection.belongs_to? &&
141
- inverse_reflection.counter_cache_column == counter_name
142
- }
143
- end
144
- alias inverse_updates_counter_named? inverse_which_updates_counter_named
145
-
146
- def inverse_updates_counter_in_memory?(reflection)
147
- inverse = inverse_which_updates_counter_cache(reflection)
148
- inverse && inverse == reflection.inverse_of
149
- end
150
-
151
- def counter_must_be_updated_by_has_many?(reflection)
152
- !inverse_updates_counter_in_memory?(reflection) && has_cached_counter?(reflection)
153
- end
154
-
155
104
  def delete_count(method, scope)
156
105
  if method == :delete_all
157
106
  scope.delete_all
@@ -169,7 +118,7 @@ module ActiveRecord
169
118
  def delete_records(records, method)
170
119
  if method == :destroy
171
120
  records.each(&:destroy!)
172
- update_counter(-records.length) unless inverse_updates_counter_cache?
121
+ update_counter(-records.length) unless reflection.inverse_updates_counter_cache?
173
122
  else
174
123
  scope = self.scope.where(reflection.klass.primary_key => records)
175
124
  update_counter(-delete_count(method, scope))