activerecord 5.0.7.2 → 5.1.0.beta1

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 (216) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +389 -2252
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/examples/performance.rb +28 -28
  6. data/examples/simple.rb +3 -3
  7. data/lib/active_record.rb +20 -20
  8. data/lib/active_record/aggregations.rb +244 -244
  9. data/lib/active_record/association_relation.rb +5 -5
  10. data/lib/active_record/associations.rb +1579 -1569
  11. data/lib/active_record/associations/alias_tracker.rb +1 -1
  12. data/lib/active_record/associations/association.rb +23 -15
  13. data/lib/active_record/associations/association_scope.rb +83 -81
  14. data/lib/active_record/associations/belongs_to_association.rb +0 -1
  15. data/lib/active_record/associations/builder/belongs_to.rb +16 -14
  16. data/lib/active_record/associations/builder/collection_association.rb +1 -2
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +27 -27
  18. data/lib/active_record/associations/collection_association.rb +74 -241
  19. data/lib/active_record/associations/collection_proxy.rb +144 -70
  20. data/lib/active_record/associations/has_many_association.rb +15 -19
  21. data/lib/active_record/associations/has_many_through_association.rb +12 -5
  22. data/lib/active_record/associations/has_one_association.rb +22 -28
  23. data/lib/active_record/associations/has_one_through_association.rb +5 -1
  24. data/lib/active_record/associations/join_dependency.rb +117 -115
  25. data/lib/active_record/associations/join_dependency/join_association.rb +16 -13
  26. data/lib/active_record/associations/join_dependency/join_base.rb +1 -1
  27. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  28. data/lib/active_record/associations/preloader.rb +94 -94
  29. data/lib/active_record/associations/preloader/association.rb +87 -64
  30. data/lib/active_record/associations/preloader/belongs_to.rb +0 -2
  31. data/lib/active_record/associations/preloader/collection_association.rb +6 -6
  32. data/lib/active_record/associations/preloader/has_many.rb +0 -2
  33. data/lib/active_record/associations/preloader/singular_association.rb +6 -8
  34. data/lib/active_record/associations/preloader/through_association.rb +34 -41
  35. data/lib/active_record/associations/singular_association.rb +8 -25
  36. data/lib/active_record/associations/through_association.rb +3 -6
  37. data/lib/active_record/attribute.rb +98 -71
  38. data/lib/active_record/attribute/user_provided_default.rb +4 -2
  39. data/lib/active_record/attribute_assignment.rb +61 -61
  40. data/lib/active_record/attribute_decorators.rb +35 -13
  41. data/lib/active_record/attribute_methods.rb +56 -65
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -7
  43. data/lib/active_record/attribute_methods/dirty.rb +216 -34
  44. data/lib/active_record/attribute_methods/primary_key.rb +78 -73
  45. data/lib/active_record/attribute_methods/read.rb +39 -35
  46. data/lib/active_record/attribute_methods/serialization.rb +7 -7
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +35 -58
  48. data/lib/active_record/attribute_methods/write.rb +36 -30
  49. data/lib/active_record/attribute_mutation_tracker.rb +53 -10
  50. data/lib/active_record/attribute_set.rb +9 -6
  51. data/lib/active_record/attribute_set/builder.rb +41 -49
  52. data/lib/active_record/attribute_set/yaml_encoder.rb +41 -0
  53. data/lib/active_record/attributes.rb +21 -21
  54. data/lib/active_record/autosave_association.rb +13 -13
  55. data/lib/active_record/base.rb +24 -22
  56. data/lib/active_record/callbacks.rb +52 -14
  57. data/lib/active_record/coders/yaml_column.rb +9 -11
  58. data/lib/active_record/collection_cache_key.rb +6 -17
  59. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +320 -278
  60. data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -3
  61. data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -34
  62. data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -27
  63. data/lib/active_record/connection_adapters/abstract/quoting.rb +44 -57
  64. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +9 -19
  65. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +78 -79
  66. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +53 -41
  67. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +99 -93
  68. data/lib/active_record/connection_adapters/abstract/transaction.rb +1 -5
  69. data/lib/active_record/connection_adapters/abstract_adapter.rb +156 -128
  70. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +424 -382
  71. data/lib/active_record/connection_adapters/column.rb +27 -5
  72. data/lib/active_record/connection_adapters/connection_specification.rb +128 -118
  73. data/lib/active_record/connection_adapters/mysql/column.rb +6 -31
  74. data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -43
  75. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +22 -22
  76. data/lib/active_record/connection_adapters/mysql/quoting.rb +6 -12
  77. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
  78. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +16 -19
  79. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +49 -31
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +5 -6
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +24 -26
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +1 -28
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -35
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +3 -3
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +22 -21
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +9 -9
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +5 -3
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
  91. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -3
  92. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +16 -16
  93. data/lib/active_record/connection_adapters/postgresql/oid/{rails_5_1_point.rb → legacy_point.rb} +9 -16
  94. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  95. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +13 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +28 -8
  97. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +28 -30
  98. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -1
  99. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +51 -51
  100. data/lib/active_record/connection_adapters/postgresql/quoting.rb +38 -36
  101. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +15 -0
  102. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +37 -24
  103. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +19 -23
  104. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +161 -170
  105. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +4 -4
  106. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -7
  107. data/lib/active_record/connection_adapters/postgresql_adapter.rb +179 -152
  108. data/lib/active_record/connection_adapters/schema_cache.rb +16 -7
  109. data/lib/active_record/connection_adapters/sql_type_metadata.rb +3 -3
  110. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +1 -1
  111. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +16 -20
  112. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +1 -8
  113. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +28 -0
  114. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +17 -0
  115. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +187 -130
  116. data/lib/active_record/connection_adapters/statement_pool.rb +7 -7
  117. data/lib/active_record/connection_handling.rb +14 -26
  118. data/lib/active_record/core.rb +110 -93
  119. data/lib/active_record/counter_cache.rb +62 -13
  120. data/lib/active_record/define_callbacks.rb +20 -0
  121. data/lib/active_record/dynamic_matchers.rb +80 -79
  122. data/lib/active_record/enum.rb +8 -6
  123. data/lib/active_record/errors.rb +58 -15
  124. data/lib/active_record/explain.rb +1 -2
  125. data/lib/active_record/explain_registry.rb +1 -1
  126. data/lib/active_record/explain_subscriber.rb +7 -4
  127. data/lib/active_record/fixture_set/file.rb +11 -8
  128. data/lib/active_record/fixtures.rb +66 -53
  129. data/lib/active_record/gem_version.rb +3 -3
  130. data/lib/active_record/inheritance.rb +93 -79
  131. data/lib/active_record/integration.rb +7 -7
  132. data/lib/active_record/internal_metadata.rb +3 -16
  133. data/lib/active_record/legacy_yaml_adapter.rb +1 -1
  134. data/lib/active_record/locking/optimistic.rb +64 -56
  135. data/lib/active_record/locking/pessimistic.rb +10 -1
  136. data/lib/active_record/log_subscriber.rb +29 -29
  137. data/lib/active_record/migration.rb +155 -172
  138. data/lib/active_record/migration/command_recorder.rb +94 -94
  139. data/lib/active_record/migration/compatibility.rb +76 -37
  140. data/lib/active_record/migration/join_table.rb +6 -6
  141. data/lib/active_record/model_schema.rb +85 -119
  142. data/lib/active_record/nested_attributes.rb +200 -199
  143. data/lib/active_record/null_relation.rb +10 -33
  144. data/lib/active_record/persistence.rb +45 -38
  145. data/lib/active_record/query_cache.rb +4 -8
  146. data/lib/active_record/querying.rb +2 -3
  147. data/lib/active_record/railtie.rb +16 -17
  148. data/lib/active_record/railties/controller_runtime.rb +6 -2
  149. data/lib/active_record/railties/databases.rake +125 -140
  150. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  151. data/lib/active_record/readonly_attributes.rb +2 -2
  152. data/lib/active_record/reflection.rb +79 -96
  153. data/lib/active_record/relation.rb +72 -115
  154. data/lib/active_record/relation/batches.rb +87 -58
  155. data/lib/active_record/relation/batches/batch_enumerator.rb +1 -1
  156. data/lib/active_record/relation/calculations.rb +154 -160
  157. data/lib/active_record/relation/delegation.rb +30 -29
  158. data/lib/active_record/relation/finder_methods.rb +195 -226
  159. data/lib/active_record/relation/merger.rb +58 -62
  160. data/lib/active_record/relation/predicate_builder.rb +92 -89
  161. data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -5
  162. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +23 -23
  163. data/lib/active_record/relation/predicate_builder/base_handler.rb +3 -1
  164. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +0 -8
  165. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +12 -10
  166. data/lib/active_record/relation/predicate_builder/range_handler.rb +0 -8
  167. data/lib/active_record/relation/query_attribute.rb +1 -1
  168. data/lib/active_record/relation/query_methods.rb +247 -295
  169. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  170. data/lib/active_record/relation/spawn_methods.rb +4 -5
  171. data/lib/active_record/relation/where_clause.rb +79 -65
  172. data/lib/active_record/relation/where_clause_factory.rb +47 -8
  173. data/lib/active_record/result.rb +29 -31
  174. data/lib/active_record/runtime_registry.rb +3 -3
  175. data/lib/active_record/sanitization.rb +182 -197
  176. data/lib/active_record/schema.rb +3 -3
  177. data/lib/active_record/schema_dumper.rb +14 -37
  178. data/lib/active_record/schema_migration.rb +3 -3
  179. data/lib/active_record/scoping.rb +9 -10
  180. data/lib/active_record/scoping/default.rb +87 -91
  181. data/lib/active_record/scoping/named.rb +16 -28
  182. data/lib/active_record/secure_token.rb +2 -2
  183. data/lib/active_record/statement_cache.rb +13 -15
  184. data/lib/active_record/store.rb +31 -32
  185. data/lib/active_record/suppressor.rb +2 -1
  186. data/lib/active_record/table_metadata.rb +9 -5
  187. data/lib/active_record/tasks/database_tasks.rb +72 -65
  188. data/lib/active_record/tasks/mysql_database_tasks.rb +75 -72
  189. data/lib/active_record/tasks/postgresql_database_tasks.rb +53 -48
  190. data/lib/active_record/tasks/sqlite_database_tasks.rb +18 -16
  191. data/lib/active_record/timestamp.rb +39 -25
  192. data/lib/active_record/touch_later.rb +1 -2
  193. data/lib/active_record/transactions.rb +98 -110
  194. data/lib/active_record/type.rb +17 -13
  195. data/lib/active_record/type/adapter_specific_registry.rb +46 -42
  196. data/lib/active_record/type/decimal_without_scale.rb +9 -0
  197. data/lib/active_record/type/hash_lookup_type_map.rb +3 -3
  198. data/lib/active_record/type/serialized.rb +8 -8
  199. data/lib/active_record/type/text.rb +9 -0
  200. data/lib/active_record/type/time.rb +0 -1
  201. data/lib/active_record/type/type_map.rb +11 -15
  202. data/lib/active_record/type/unsigned_integer.rb +15 -0
  203. data/lib/active_record/type_caster.rb +2 -2
  204. data/lib/active_record/type_caster/connection.rb +8 -6
  205. data/lib/active_record/type_caster/map.rb +3 -1
  206. data/lib/active_record/validations.rb +4 -4
  207. data/lib/active_record/validations/associated.rb +1 -1
  208. data/lib/active_record/validations/presence.rb +2 -2
  209. data/lib/active_record/validations/uniqueness.rb +8 -39
  210. data/lib/active_record/version.rb +1 -1
  211. data/lib/rails/generators/active_record.rb +4 -4
  212. data/lib/rails/generators/active_record/migration.rb +2 -2
  213. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -34
  214. data/lib/rails/generators/active_record/model/model_generator.rb +9 -9
  215. metadata +22 -13
  216. data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
@@ -10,9 +10,9 @@ module ActiveRecord
10
10
  # HasManyAssociation => has_many
11
11
  # HasManyThroughAssociation + ThroughAssociation => has_many :through
12
12
  #
13
- # CollectionAssociation class provides common methods to the collections
13
+ # The CollectionAssociation class provides common methods to the collections
14
14
  # defined by +has_and_belongs_to_many+, +has_many+ or +has_many+ with
15
- # +:through association+ option.
15
+ # the +:through association+ option.
16
16
  #
17
17
  # You need to be careful with assumptions regarding the target: The proxy
18
18
  # does not fetch records from the database until it needs them, but new
@@ -24,23 +24,13 @@ module ActiveRecord
24
24
  # If you need to work on all current children, new and existing records,
25
25
  # +load_target+ and the +loaded+ flag are your friends.
26
26
  class CollectionAssociation < Association #:nodoc:
27
-
28
27
  # Implements the reader method, e.g. foo.items for Foo.has_many :items
29
- def reader(force_reload = false)
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
-
37
- klass.uncached { reload }
38
- elsif stale_target?
28
+ def reader
29
+ if stale_target?
39
30
  reload
40
31
  end
41
32
 
42
- @proxy ||= CollectionProxy.create(klass, self)
43
- @proxy.reset_scope
33
+ CollectionProxy.create(klass, self)
44
34
  end
45
35
 
46
36
  # Implements the writer method, e.g. foo.items= for Foo.has_many :items
@@ -51,9 +41,7 @@ module ActiveRecord
51
41
  # Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
52
42
  def ids_reader
53
43
  if loaded?
54
- load_target.map do |record|
55
- record.send(reflection.association_primary_key)
56
- end
44
+ target.pluck(reflection.association_primary_key)
57
45
  else
58
46
  @association_ids ||= (
59
47
  column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}"
@@ -67,14 +55,11 @@ module ActiveRecord
67
55
  pk_type = reflection.association_primary_key_type
68
56
  ids = Array(ids).reject(&:blank?)
69
57
  ids.map! { |i| pk_type.cast(i) }
70
-
71
- primary_key = reflection.association_primary_key
72
- records = klass.where(primary_key => ids).index_by do |r|
73
- r.public_send(primary_key)
58
+ records = klass.where(reflection.association_primary_key => ids).index_by do |r|
59
+ r.send(reflection.association_primary_key)
74
60
  end.values_at(*ids).compact
75
-
76
61
  if records.size != ids.size
77
- klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, primary_key)
62
+ klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, reflection.association_primary_key)
78
63
  else
79
64
  replace(records)
80
65
  end
@@ -85,14 +70,6 @@ module ActiveRecord
85
70
  @target = []
86
71
  end
87
72
 
88
- def select(*fields)
89
- if block_given?
90
- load_target.select.each { |e| yield e }
91
- else
92
- scope.select(*fields)
93
- end
94
- end
95
-
96
73
  def find(*args)
97
74
  if block_given?
98
75
  load_target.find(*args) { |*block_args| yield(*block_args) }
@@ -114,52 +91,6 @@ module ActiveRecord
114
91
  end
115
92
  end
116
93
 
117
- def first(*args)
118
- first_nth_or_last(:first, *args)
119
- end
120
-
121
- def second(*args)
122
- first_nth_or_last(:second, *args)
123
- end
124
-
125
- def third(*args)
126
- first_nth_or_last(:third, *args)
127
- end
128
-
129
- def fourth(*args)
130
- first_nth_or_last(:fourth, *args)
131
- end
132
-
133
- def fifth(*args)
134
- first_nth_or_last(:fifth, *args)
135
- end
136
-
137
- def forty_two(*args)
138
- first_nth_or_last(:forty_two, *args)
139
- end
140
-
141
- def third_to_last(*args)
142
- first_nth_or_last(:third_to_last, *args)
143
- end
144
-
145
- def second_to_last(*args)
146
- first_nth_or_last(:second_to_last, *args)
147
- end
148
-
149
- def last(*args)
150
- first_nth_or_last(:last, *args)
151
- end
152
-
153
- def take(n = nil)
154
- if loaded?
155
- n ? target.take(n) : target.first
156
- else
157
- scope.take(n).tap do |record|
158
- set_inverse_instance record if record.is_a? ActiveRecord::Base
159
- end
160
- end
161
- end
162
-
163
94
  def build(attributes = {}, &block)
164
95
  if attributes.is_a?(Array)
165
96
  attributes.collect { |attr| build(attr, &block) }
@@ -170,14 +101,6 @@ module ActiveRecord
170
101
  end
171
102
  end
172
103
 
173
- def create(attributes = {}, &block)
174
- _create_record(attributes, &block)
175
- end
176
-
177
- def create!(attributes = {}, &block)
178
- _create_record(attributes, true, &block)
179
- end
180
-
181
104
  # Add +records+ to this association. Returns +self+ so method calls may
182
105
  # be chained. Since << flattens its argument list and inserts each record,
183
106
  # +push+ and +concat+ behave identically.
@@ -225,12 +148,12 @@ module ActiveRecord
225
148
  end
226
149
 
227
150
  dependent = if dependent
228
- dependent
229
- elsif options[:dependent] == :destroy
230
- :delete_all
231
- else
232
- options[:dependent]
233
- end
151
+ dependent
152
+ elsif options[:dependent] == :destroy
153
+ :delete_all
154
+ else
155
+ options[:dependent]
156
+ end
234
157
 
235
158
  delete_or_nullify_all_records(dependent).tap do
236
159
  reset
@@ -248,28 +171,6 @@ module ActiveRecord
248
171
  end
249
172
  end
250
173
 
251
- # Count all records using SQL. Construct options and pass them with
252
- # scope to the target class's +count+.
253
- def count(column_name = nil)
254
- relation = scope
255
- if association_scope.distinct_value
256
- # This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
257
- column_name ||= reflection.klass.primary_key
258
- relation = relation.distinct
259
- end
260
-
261
- value = relation.count(column_name)
262
-
263
- limit = options[:limit]
264
- offset = options[:offset]
265
-
266
- if limit || offset
267
- [ [value - offset.to_i, 0].max, limit.to_i ].min
268
- else
269
- value
270
- end
271
- end
272
-
273
174
  # Removes +records+ from this association calling +before_remove+ and
274
175
  # +after_remove+ callbacks.
275
176
  #
@@ -279,11 +180,8 @@ module ActiveRecord
279
180
  # +delete_records+. They are in any case removed from the collection.
280
181
  def delete(*records)
281
182
  return if records.empty?
282
- _options = records.extract_options!
283
- dependent = _options[:dependent] || options[:dependent]
284
-
285
183
  records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
286
- delete_or_destroy(records, dependent)
184
+ delete_or_destroy(records, options[:dependent])
287
185
  end
288
186
 
289
187
  # Deletes the +records+ and removes them from this association calling
@@ -309,11 +207,7 @@ module ActiveRecord
309
207
  # +count_records+, which is a method descendants have to provide.
310
208
  def size
311
209
  if !find_target? || loaded?
312
- if association_scope.distinct_value
313
- target.uniq.size
314
- else
315
- target.size
316
- end
210
+ target.size
317
211
  elsif !association_scope.group_values.empty?
318
212
  load_target.size
319
213
  elsif !association_scope.distinct_value && target.is_a?(Array)
@@ -324,15 +218,6 @@ module ActiveRecord
324
218
  end
325
219
  end
326
220
 
327
- # Returns the size of the collection calling +size+ on the target.
328
- #
329
- # If the collection has been already loaded +length+ and +size+ are
330
- # equivalent. If not and you are going to need the records anyway this
331
- # method will take one less query. Otherwise +size+ is more efficient.
332
- def length
333
- load_target.size
334
- end
335
-
336
221
  # Returns true if the collection is empty.
337
222
  #
338
223
  # If the collection has been loaded
@@ -349,36 +234,6 @@ module ActiveRecord
349
234
  end
350
235
  end
351
236
 
352
- # Returns true if the collections is not empty.
353
- # If block given, loads all records and checks for one or more matches.
354
- # Otherwise, equivalent to +!collection.empty?+.
355
- def any?
356
- if block_given?
357
- load_target.any? { |*block_args| yield(*block_args) }
358
- else
359
- !empty?
360
- end
361
- end
362
-
363
- # Returns true if the collection has more than 1 record.
364
- # If block given, loads all records and checks for two or more matches.
365
- # Otherwise, equivalent to +collection.size > 1+.
366
- def many?
367
- if block_given?
368
- load_target.many? { |*block_args| yield(*block_args) }
369
- else
370
- size > 1
371
- end
372
- end
373
-
374
- def distinct
375
- seen = {}
376
- load_target.find_all do |record|
377
- seen[record.id] = true unless seen.key?(record.id)
378
- end
379
- end
380
- alias uniq distinct
381
-
382
237
  # Replace this collection with +other_array+. This will perform a diff
383
238
  # and delete/add only records that have changed.
384
239
  def replace(other_array)
@@ -425,6 +280,35 @@ module ActiveRecord
425
280
  replace_on_target(record, index, skip_callbacks, &block)
426
281
  end
427
282
 
283
+ def replace_on_target(record, index, skip_callbacks)
284
+ callback(:before_add, record) unless skip_callbacks
285
+
286
+ begin
287
+ if index
288
+ record_was = target[index]
289
+ target[index] = record
290
+ else
291
+ target << record
292
+ end
293
+
294
+ set_inverse_instance(record)
295
+
296
+ yield(record) if block_given?
297
+ rescue
298
+ if index
299
+ target[index] = record_was
300
+ else
301
+ target.delete(record)
302
+ end
303
+
304
+ raise
305
+ end
306
+
307
+ callback(:after_add, record) unless skip_callbacks
308
+
309
+ record
310
+ end
311
+
428
312
  def scope
429
313
  scope = super
430
314
  scope.none! if null_scope?
@@ -435,24 +319,27 @@ module ActiveRecord
435
319
  owner.new_record? && !foreign_key_present?
436
320
  end
437
321
 
438
- private
439
- def get_records(&block)
440
- return scope.to_a if skip_statement_cache?
441
-
442
- conn = klass.connection
443
- sc = reflection.association_scope_cache(conn, owner) do
444
- StatementCache.create(conn) { |params|
445
- as = AssociationScope.create { params.bind }
446
- target_scope.merge as.scope(self, conn)
447
- }
448
- end
449
-
450
- binds = AssociationScope.get_bind_values(owner, reflection.chain)
451
- sc.execute(binds, klass, klass.connection, &block)
322
+ def find_from_target?
323
+ loaded? ||
324
+ owner.new_record? ||
325
+ target.any? { |record| record.new_record? || record.changed? }
452
326
  end
453
327
 
328
+ private
329
+
454
330
  def find_target
455
- get_records do |record|
331
+ return scope.to_a if skip_statement_cache?
332
+
333
+ conn = klass.connection
334
+ sc = reflection.association_scope_cache(conn, owner) do
335
+ StatementCache.create(conn) { |params|
336
+ as = AssociationScope.create { params.bind }
337
+ target_scope.merge as.scope(self, conn)
338
+ }
339
+ end
340
+
341
+ binds = AssociationScope.get_bind_values(owner, reflection.chain)
342
+ sc.execute(binds, klass, conn) do |record|
456
343
  set_inverse_instance(record)
457
344
  end
458
345
  end
@@ -474,7 +361,7 @@ module ActiveRecord
474
361
  persisted.map! do |record|
475
362
  if mem_record = memory.delete(record)
476
363
 
477
- ((record.attribute_names & mem_record.attribute_names) - mem_record.changes.keys).each do |name|
364
+ ((record.attribute_names & mem_record.attribute_names) - mem_record.changed_attribute_names_to_save).each do |name|
478
365
  mem_record[name] = record[name]
479
366
  end
480
367
 
@@ -498,19 +385,15 @@ module ActiveRecord
498
385
  transaction do
499
386
  add_to_target(build_record(attributes)) do |record|
500
387
  yield(record) if block_given?
501
- insert_record(record, true, raise) { @_was_loaded = loaded? }
388
+ insert_record(record, true, raise)
502
389
  end
503
390
  end
504
391
  end
505
392
  end
506
393
 
507
394
  # Do the relevant stuff to insert the given record into the association collection.
508
- def insert_record(record, validate = true, raise = false, &block)
509
- if raise
510
- record.save!(validate: validate, &block)
511
- else
512
- record.save(validate: validate, &block)
513
- end
395
+ def insert_record(record, validate = true, raise = false)
396
+ raise NotImplementedError
514
397
  end
515
398
 
516
399
  def create_scope
@@ -538,8 +421,9 @@ module ActiveRecord
538
421
  records.each { |record| callback(:after_remove, record) }
539
422
  end
540
423
 
541
- # Delete the given records from the association, using one of the methods :destroy,
542
- # :delete_all or :nullify (or nil, in which case a default is used).
424
+ # Delete the given records from the association,
425
+ # using one of the methods +:destroy+, +:delete_all+
426
+ # or +:nullify+ (or +nil+, in which case a default is used).
543
427
  def delete_records(records, method)
544
428
  raise NotImplementedError
545
429
  end
@@ -564,41 +448,19 @@ module ActiveRecord
564
448
  end
565
449
  end
566
450
 
567
- def concat_records(records, raise = false)
451
+ def concat_records(records, should_raise = false)
568
452
  result = true
569
453
 
570
454
  records.each do |record|
571
455
  raise_on_type_mismatch!(record)
572
- add_to_target(record) do
573
- result &&= insert_record(record, true, raise) { @_was_loaded = loaded? } unless owner.new_record?
456
+ add_to_target(record) do |rec|
457
+ result &&= insert_record(rec, true, should_raise) unless owner.new_record?
574
458
  end
575
459
  end
576
460
 
577
461
  result && records
578
462
  end
579
463
 
580
- def replace_on_target(record, index, skip_callbacks)
581
- callback(:before_add, record) unless skip_callbacks
582
-
583
- set_inverse_instance(record)
584
-
585
- @_was_loaded = true
586
-
587
- yield(record) if block_given?
588
-
589
- if index
590
- target[index] = record
591
- elsif @_was_loaded || !loaded?
592
- target << record
593
- end
594
-
595
- callback(:after_add, record) unless skip_callbacks
596
-
597
- record
598
- ensure
599
- @_was_loaded = nil
600
- end
601
-
602
464
  def callback(method, record)
603
465
  callbacks_for(method).each do |callback|
604
466
  callback.call(method, owner, record)
@@ -610,25 +472,6 @@ module ActiveRecord
610
472
  owner.class.send(full_callback_name)
611
473
  end
612
474
 
613
- # Should we deal with assoc.first or assoc.last by issuing an independent query to
614
- # the database, or by getting the target, and then taking the first/last item from that?
615
- #
616
- # If the args is just a non-empty options hash, go to the database.
617
- #
618
- # Otherwise, go to the database only if none of the following are true:
619
- # * target already loaded
620
- # * owner is new record
621
- # * target contains new or changed record(s)
622
- def fetch_first_nth_or_last_using_find?(args)
623
- if args.first.is_a?(Hash)
624
- true
625
- else
626
- !(loaded? ||
627
- owner.new_record? ||
628
- target.any? { |record| record.new_record? || record.changed? })
629
- end
630
- end
631
-
632
475
  def include_in_memory?(record)
633
476
  if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
634
477
  assoc = owner.association(reflection.through_reflection.name)
@@ -655,16 +498,6 @@ module ActiveRecord
655
498
  load_target.select { |r| ids.include?(r.id.to_s) }
656
499
  end
657
500
  end
658
-
659
- # Fetches the first/last using SQL if possible, otherwise from the target array.
660
- def first_nth_or_last(type, *args)
661
- args.shift if args.first.is_a?(Hash) && args.first.empty?
662
-
663
- collection = fetch_first_nth_or_last_using_find?(args) ? scope : load_target
664
- collection.send(type, *args).tap do |record|
665
- set_inverse_instance record if record.is_a? ActiveRecord::Base
666
- end
667
- end
668
501
  end
669
502
  end
670
503
  end