activerecord 5.0.7 → 5.1.7

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 (219) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +657 -2080
  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/aggregations.rb +244 -244
  8. data/lib/active_record/association_relation.rb +5 -5
  9. data/lib/active_record/associations/alias_tracker.rb +10 -11
  10. data/lib/active_record/associations/association.rb +23 -5
  11. data/lib/active_record/associations/association_scope.rb +95 -81
  12. data/lib/active_record/associations/belongs_to_association.rb +7 -4
  13. data/lib/active_record/associations/builder/belongs_to.rb +30 -16
  14. data/lib/active_record/associations/builder/collection_association.rb +1 -2
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +27 -27
  16. data/lib/active_record/associations/collection_association.rb +36 -205
  17. data/lib/active_record/associations/collection_proxy.rb +132 -63
  18. data/lib/active_record/associations/has_many_association.rb +10 -19
  19. data/lib/active_record/associations/has_many_through_association.rb +12 -4
  20. data/lib/active_record/associations/has_one_association.rb +24 -28
  21. data/lib/active_record/associations/has_one_through_association.rb +5 -1
  22. data/lib/active_record/associations/join_dependency/join_association.rb +4 -28
  23. data/lib/active_record/associations/join_dependency/join_base.rb +1 -1
  24. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  25. data/lib/active_record/associations/join_dependency.rb +121 -118
  26. data/lib/active_record/associations/preloader/association.rb +64 -64
  27. data/lib/active_record/associations/preloader/belongs_to.rb +0 -2
  28. data/lib/active_record/associations/preloader/collection_association.rb +6 -6
  29. data/lib/active_record/associations/preloader/has_many.rb +0 -2
  30. data/lib/active_record/associations/preloader/singular_association.rb +6 -8
  31. data/lib/active_record/associations/preloader/through_association.rb +41 -41
  32. data/lib/active_record/associations/preloader.rb +94 -94
  33. data/lib/active_record/associations/singular_association.rb +8 -25
  34. data/lib/active_record/associations/through_association.rb +2 -5
  35. data/lib/active_record/associations.rb +1591 -1562
  36. data/lib/active_record/attribute/user_provided_default.rb +4 -2
  37. data/lib/active_record/attribute.rb +98 -71
  38. data/lib/active_record/attribute_assignment.rb +61 -61
  39. data/lib/active_record/attribute_decorators.rb +35 -13
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -7
  41. data/lib/active_record/attribute_methods/dirty.rb +229 -46
  42. data/lib/active_record/attribute_methods/primary_key.rb +74 -73
  43. data/lib/active_record/attribute_methods/read.rb +39 -35
  44. data/lib/active_record/attribute_methods/serialization.rb +7 -7
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +35 -58
  46. data/lib/active_record/attribute_methods/write.rb +30 -33
  47. data/lib/active_record/attribute_methods.rb +56 -65
  48. data/lib/active_record/attribute_mutation_tracker.rb +63 -11
  49. data/lib/active_record/attribute_set/builder.rb +27 -33
  50. data/lib/active_record/attribute_set/yaml_encoder.rb +41 -0
  51. data/lib/active_record/attribute_set.rb +9 -6
  52. data/lib/active_record/attributes.rb +22 -22
  53. data/lib/active_record/autosave_association.rb +18 -13
  54. data/lib/active_record/base.rb +24 -22
  55. data/lib/active_record/callbacks.rb +56 -14
  56. data/lib/active_record/coders/yaml_column.rb +9 -11
  57. data/lib/active_record/collection_cache_key.rb +3 -4
  58. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +330 -284
  59. data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -3
  60. data/lib/active_record/connection_adapters/abstract/database_statements.rb +39 -37
  61. data/lib/active_record/connection_adapters/abstract/query_cache.rb +32 -27
  62. data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -51
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +10 -20
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +74 -79
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +53 -41
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +120 -100
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +49 -43
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +165 -135
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +404 -424
  70. data/lib/active_record/connection_adapters/column.rb +26 -4
  71. data/lib/active_record/connection_adapters/connection_specification.rb +128 -118
  72. data/lib/active_record/connection_adapters/mysql/column.rb +6 -31
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -49
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +22 -22
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +6 -12
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +16 -19
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -28
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +43 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +7 -6
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +23 -27
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +32 -53
  83. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +3 -3
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +19 -9
  85. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +5 -3
  86. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -3
  90. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +16 -16
  91. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +0 -10
  92. data/lib/active_record/connection_adapters/postgresql/oid/{rails_5_1_point.rb → legacy_point.rb} +9 -16
  93. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +13 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +28 -8
  96. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +32 -30
  97. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +51 -51
  99. data/lib/active_record/connection_adapters/postgresql/oid.rb +22 -21
  100. data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -35
  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 +182 -222
  105. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +6 -4
  106. data/lib/active_record/connection_adapters/postgresql/utils.rb +7 -5
  107. data/lib/active_record/connection_adapters/postgresql_adapter.rb +198 -167
  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 -19
  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/schema_statements.rb +32 -0
  116. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +184 -167
  117. data/lib/active_record/connection_adapters/statement_pool.rb +7 -7
  118. data/lib/active_record/connection_handling.rb +14 -26
  119. data/lib/active_record/core.rb +109 -93
  120. data/lib/active_record/counter_cache.rb +60 -13
  121. data/lib/active_record/define_callbacks.rb +20 -0
  122. data/lib/active_record/dynamic_matchers.rb +80 -79
  123. data/lib/active_record/enum.rb +8 -6
  124. data/lib/active_record/errors.rb +64 -15
  125. data/lib/active_record/explain.rb +1 -2
  126. data/lib/active_record/explain_registry.rb +1 -1
  127. data/lib/active_record/explain_subscriber.rb +7 -4
  128. data/lib/active_record/fixture_set/file.rb +11 -8
  129. data/lib/active_record/fixtures.rb +66 -53
  130. data/lib/active_record/gem_version.rb +1 -1
  131. data/lib/active_record/inheritance.rb +93 -79
  132. data/lib/active_record/integration.rb +7 -7
  133. data/lib/active_record/internal_metadata.rb +3 -16
  134. data/lib/active_record/legacy_yaml_adapter.rb +1 -1
  135. data/lib/active_record/locking/optimistic.rb +69 -74
  136. data/lib/active_record/locking/pessimistic.rb +10 -1
  137. data/lib/active_record/log_subscriber.rb +23 -28
  138. data/lib/active_record/migration/command_recorder.rb +94 -94
  139. data/lib/active_record/migration/compatibility.rb +100 -47
  140. data/lib/active_record/migration/join_table.rb +6 -6
  141. data/lib/active_record/migration.rb +153 -155
  142. data/lib/active_record/model_schema.rb +94 -107
  143. data/lib/active_record/nested_attributes.rb +200 -199
  144. data/lib/active_record/null_relation.rb +11 -34
  145. data/lib/active_record/persistence.rb +65 -50
  146. data/lib/active_record/query_cache.rb +2 -6
  147. data/lib/active_record/querying.rb +3 -4
  148. data/lib/active_record/railtie.rb +16 -17
  149. data/lib/active_record/railties/controller_runtime.rb +6 -2
  150. data/lib/active_record/railties/databases.rake +105 -133
  151. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  152. data/lib/active_record/readonly_attributes.rb +2 -2
  153. data/lib/active_record/reflection.rb +154 -108
  154. data/lib/active_record/relation/batches/batch_enumerator.rb +1 -1
  155. data/lib/active_record/relation/batches.rb +80 -51
  156. data/lib/active_record/relation/calculations.rb +169 -162
  157. data/lib/active_record/relation/delegation.rb +32 -31
  158. data/lib/active_record/relation/finder_methods.rb +197 -231
  159. data/lib/active_record/relation/merger.rb +58 -62
  160. data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -5
  161. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +23 -23
  162. data/lib/active_record/relation/predicate_builder/base_handler.rb +3 -1
  163. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +0 -8
  164. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +12 -10
  165. data/lib/active_record/relation/predicate_builder/range_handler.rb +0 -8
  166. data/lib/active_record/relation/predicate_builder.rb +92 -89
  167. data/lib/active_record/relation/query_attribute.rb +1 -1
  168. data/lib/active_record/relation/query_methods.rb +255 -293
  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 +80 -65
  172. data/lib/active_record/relation/where_clause_factory.rb +47 -8
  173. data/lib/active_record/relation.rb +93 -119
  174. data/lib/active_record/result.rb +41 -32
  175. data/lib/active_record/runtime_registry.rb +3 -3
  176. data/lib/active_record/sanitization.rb +176 -192
  177. data/lib/active_record/schema.rb +3 -3
  178. data/lib/active_record/schema_dumper.rb +15 -38
  179. data/lib/active_record/schema_migration.rb +8 -4
  180. data/lib/active_record/scoping/default.rb +90 -90
  181. data/lib/active_record/scoping/named.rb +11 -11
  182. data/lib/active_record/scoping.rb +6 -6
  183. data/lib/active_record/secure_token.rb +2 -2
  184. data/lib/active_record/statement_cache.rb +13 -15
  185. data/lib/active_record/store.rb +31 -32
  186. data/lib/active_record/suppressor.rb +2 -1
  187. data/lib/active_record/table_metadata.rb +9 -5
  188. data/lib/active_record/tasks/database_tasks.rb +65 -55
  189. data/lib/active_record/tasks/mysql_database_tasks.rb +76 -73
  190. data/lib/active_record/tasks/postgresql_database_tasks.rb +72 -47
  191. data/lib/active_record/tasks/sqlite_database_tasks.rb +18 -16
  192. data/lib/active_record/timestamp.rb +46 -25
  193. data/lib/active_record/touch_later.rb +1 -2
  194. data/lib/active_record/transactions.rb +97 -109
  195. data/lib/active_record/type/adapter_specific_registry.rb +46 -42
  196. data/lib/active_record/type/decimal_without_scale.rb +13 -0
  197. data/lib/active_record/type/hash_lookup_type_map.rb +3 -3
  198. data/lib/active_record/type/internal/abstract_json.rb +4 -0
  199. data/lib/active_record/type/serialized.rb +14 -8
  200. data/lib/active_record/type/text.rb +9 -0
  201. data/lib/active_record/type/time.rb +0 -1
  202. data/lib/active_record/type/type_map.rb +11 -15
  203. data/lib/active_record/type/unsigned_integer.rb +15 -0
  204. data/lib/active_record/type.rb +17 -13
  205. data/lib/active_record/type_caster/connection.rb +8 -6
  206. data/lib/active_record/type_caster/map.rb +3 -1
  207. data/lib/active_record/type_caster.rb +2 -2
  208. data/lib/active_record/validations/associated.rb +1 -1
  209. data/lib/active_record/validations/presence.rb +2 -2
  210. data/lib/active_record/validations/uniqueness.rb +8 -39
  211. data/lib/active_record/validations.rb +4 -4
  212. data/lib/active_record/version.rb +1 -1
  213. data/lib/active_record.rb +20 -20
  214. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -34
  215. data/lib/rails/generators/active_record/migration.rb +1 -1
  216. data/lib/rails/generators/active_record/model/model_generator.rb +9 -9
  217. data/lib/rails/generators/active_record.rb +4 -4
  218. metadata +24 -13
  219. 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,18 +24,9 @@ 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
 
@@ -51,14 +42,9 @@ module ActiveRecord
51
42
  # Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
52
43
  def ids_reader
53
44
  if loaded?
54
- load_target.map do |record|
55
- record.send(reflection.association_primary_key)
56
- end
45
+ target.pluck(reflection.association_primary_key)
57
46
  else
58
- @association_ids ||= (
59
- column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}"
60
- scope.pluck(column)
61
- )
47
+ @association_ids ||= scope.pluck(reflection.association_primary_key)
62
48
  end
63
49
  end
64
50
 
@@ -85,14 +71,6 @@ module ActiveRecord
85
71
  @target = []
86
72
  end
87
73
 
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
74
  def find(*args)
97
75
  if block_given?
98
76
  load_target.find(*args) { |*block_args| yield(*block_args) }
@@ -114,52 +92,6 @@ module ActiveRecord
114
92
  end
115
93
  end
116
94
 
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
95
  def build(attributes = {}, &block)
164
96
  if attributes.is_a?(Array)
165
97
  attributes.collect { |attr| build(attr, &block) }
@@ -170,14 +102,6 @@ module ActiveRecord
170
102
  end
171
103
  end
172
104
 
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
105
  # Add +records+ to this association. Returns +self+ so method calls may
182
106
  # be chained. Since << flattens its argument list and inserts each record,
183
107
  # +push+ and +concat+ behave identically.
@@ -225,12 +149,12 @@ module ActiveRecord
225
149
  end
226
150
 
227
151
  dependent = if dependent
228
- dependent
229
- elsif options[:dependent] == :destroy
230
- :delete_all
231
- else
232
- options[:dependent]
233
- end
152
+ dependent
153
+ elsif options[:dependent] == :destroy
154
+ :delete_all
155
+ else
156
+ options[:dependent]
157
+ end
234
158
 
235
159
  delete_or_nullify_all_records(dependent).tap do
236
160
  reset
@@ -248,28 +172,6 @@ module ActiveRecord
248
172
  end
249
173
  end
250
174
 
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
175
  # Removes +records+ from this association calling +before_remove+ and
274
176
  # +after_remove+ callbacks.
275
177
  #
@@ -279,11 +181,8 @@ module ActiveRecord
279
181
  # +delete_records+. They are in any case removed from the collection.
280
182
  def delete(*records)
281
183
  return if records.empty?
282
- _options = records.extract_options!
283
- dependent = _options[:dependent] || options[:dependent]
284
-
285
184
  records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
286
- delete_or_destroy(records, dependent)
185
+ delete_or_destroy(records, options[:dependent])
287
186
  end
288
187
 
289
188
  # Deletes the +records+ and removes them from this association calling
@@ -309,11 +208,7 @@ module ActiveRecord
309
208
  # +count_records+, which is a method descendants have to provide.
310
209
  def size
311
210
  if !find_target? || loaded?
312
- if association_scope.distinct_value
313
- target.uniq.size
314
- else
315
- target.size
316
- end
211
+ target.size
317
212
  elsif !association_scope.group_values.empty?
318
213
  load_target.size
319
214
  elsif !association_scope.distinct_value && target.is_a?(Array)
@@ -324,15 +219,6 @@ module ActiveRecord
324
219
  end
325
220
  end
326
221
 
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
222
  # Returns true if the collection is empty.
337
223
  #
338
224
  # If the collection has been loaded
@@ -349,36 +235,6 @@ module ActiveRecord
349
235
  end
350
236
  end
351
237
 
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
238
  # Replace this collection with +other_array+. This will perform a diff
383
239
  # and delete/add only records that have changed.
384
240
  def replace(other_array)
@@ -435,24 +291,27 @@ module ActiveRecord
435
291
  owner.new_record? && !foreign_key_present?
436
292
  end
437
293
 
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)
294
+ def find_from_target?
295
+ loaded? ||
296
+ owner.new_record? ||
297
+ target.any? { |record| record.new_record? || record.changed? }
452
298
  end
453
299
 
300
+ private
301
+
454
302
  def find_target
455
- get_records do |record|
303
+ return scope.to_a if skip_statement_cache?
304
+
305
+ conn = klass.connection
306
+ sc = reflection.association_scope_cache(conn, owner) do
307
+ StatementCache.create(conn) { |params|
308
+ as = AssociationScope.create { params.bind }
309
+ target_scope.merge as.scope(self, conn)
310
+ }
311
+ end
312
+
313
+ binds = AssociationScope.get_bind_values(owner, reflection.chain)
314
+ sc.execute(binds, klass, conn) do |record|
456
315
  set_inverse_instance(record)
457
316
  end
458
317
  end
@@ -474,7 +333,7 @@ module ActiveRecord
474
333
  persisted.map! do |record|
475
334
  if mem_record = memory.delete(record)
476
335
 
477
- ((record.attribute_names & mem_record.attribute_names) - mem_record.changes.keys).each do |name|
336
+ ((record.attribute_names & mem_record.attribute_names) - mem_record.changed_attribute_names_to_save).each do |name|
478
337
  mem_record[name] = record[name]
479
338
  end
480
339
 
@@ -538,8 +397,9 @@ module ActiveRecord
538
397
  records.each { |record| callback(:after_remove, record) }
539
398
  end
540
399
 
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).
400
+ # Delete the given records from the association,
401
+ # using one of the methods +:destroy+, +:delete_all+
402
+ # or +:nullify+ (or +nil+, in which case a default is used).
543
403
  def delete_records(records, method)
544
404
  raise NotImplementedError
545
405
  end
@@ -610,25 +470,6 @@ module ActiveRecord
610
470
  owner.class.send(full_callback_name)
611
471
  end
612
472
 
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
473
  def include_in_memory?(record)
633
474
  if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
634
475
  assoc = owner.association(reflection.through_reflection.name)
@@ -655,16 +496,6 @@ module ActiveRecord
655
496
  load_target.select { |r| ids.include?(r.id.to_s) }
656
497
  end
657
498
  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
499
  end
669
500
  end
670
501
  end
@@ -53,6 +53,12 @@ module ActiveRecord
53
53
  @association.loaded?
54
54
  end
55
55
 
56
+ ##
57
+ # :method: select
58
+ #
59
+ # :call-seq:
60
+ # select(*fields, &block)
61
+ #
56
62
  # Works in two ways.
57
63
  #
58
64
  # *First:* Specify a subset of fields to be selected from the result set.
@@ -75,7 +81,7 @@ module ActiveRecord
75
81
  # # #<Pet id: nil, name: "Choo-Choo">
76
82
  # # ]
77
83
  #
78
- # person.pets.select(:id, :name )
84
+ # person.pets.select(:id, :name)
79
85
  # # => [
80
86
  # # #<Pet id: 1, name: "Fancy-Fancy">,
81
87
  # # #<Pet id: 2, name: "Spook">,
@@ -100,15 +106,6 @@ module ActiveRecord
100
106
  # # #<Pet id: 2, name: "Spook", person_id: 1>,
101
107
  # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
102
108
  # # ]
103
- #
104
- # person.pets.select(:name) { |pet| pet.name =~ /oo/ }
105
- # # => [
106
- # # #<Pet id: 2, name: "Spook">,
107
- # # #<Pet id: 3, name: "Choo-Choo">
108
- # # ]
109
- def select(*fields, &block)
110
- @association.select(*fields, &block)
111
- end
112
109
 
113
110
  # Finds an object in the collection responding to the +id+. Uses the same
114
111
  # rules as ActiveRecord::Base.find. Returns ActiveRecord::RecordNotFound
@@ -140,6 +137,12 @@ module ActiveRecord
140
137
  @association.find(*args, &block)
141
138
  end
142
139
 
140
+ ##
141
+ # :method: first
142
+ #
143
+ # :call-seq:
144
+ # first(limit = nil)
145
+ #
143
146
  # Returns the first record, or the first +n+ records, from the collection.
144
147
  # If the collection is empty, the first form returns +nil+, and the second
145
148
  # form returns an empty array.
@@ -166,45 +169,63 @@ module ActiveRecord
166
169
  # another_person_without.pets # => []
167
170
  # another_person_without.pets.first # => nil
168
171
  # another_person_without.pets.first(3) # => []
169
- def first(*args)
170
- @association.first(*args)
171
- end
172
172
 
173
+ ##
174
+ # :method: second
175
+ #
176
+ # :call-seq:
177
+ # second()
178
+ #
173
179
  # Same as #first except returns only the second record.
174
- def second(*args)
175
- @association.second(*args)
176
- end
177
180
 
181
+ ##
182
+ # :method: third
183
+ #
184
+ # :call-seq:
185
+ # third()
186
+ #
178
187
  # Same as #first except returns only the third record.
179
- def third(*args)
180
- @association.third(*args)
181
- end
182
188
 
189
+ ##
190
+ # :method: fourth
191
+ #
192
+ # :call-seq:
193
+ # fourth()
194
+ #
183
195
  # Same as #first except returns only the fourth record.
184
- def fourth(*args)
185
- @association.fourth(*args)
186
- end
187
196
 
197
+ ##
198
+ # :method: fifth
199
+ #
200
+ # :call-seq:
201
+ # fifth()
202
+ #
188
203
  # Same as #first except returns only the fifth record.
189
- def fifth(*args)
190
- @association.fifth(*args)
191
- end
192
204
 
205
+ ##
206
+ # :method: forty_two
207
+ #
208
+ # :call-seq:
209
+ # forty_two()
210
+ #
193
211
  # Same as #first except returns only the forty second record.
194
212
  # Also known as accessing "the reddit".
195
- def forty_two(*args)
196
- @association.forty_two(*args)
197
- end
198
213
 
214
+ ##
215
+ # :method: third_to_last
216
+ #
217
+ # :call-seq:
218
+ # third_to_last()
219
+ #
199
220
  # Same as #first except returns only the third-to-last record.
200
- def third_to_last(*args)
201
- @association.third_to_last(*args)
202
- end
203
221
 
222
+ ##
223
+ # :method: second_to_last
224
+ #
225
+ # :call-seq:
226
+ # second_to_last()
227
+ #
204
228
  # Same as #first except returns only the second-to-last record.
205
- def second_to_last(*args)
206
- @association.second_to_last(*args)
207
- end
208
229
 
209
230
  # Returns the last record, or the last +n+ records, from the collection.
210
231
  # If the collection is empty, the first form returns +nil+, and the second
@@ -232,8 +253,9 @@ module ActiveRecord
232
253
  # another_person_without.pets # => []
233
254
  # another_person_without.pets.last # => nil
234
255
  # another_person_without.pets.last(3) # => []
235
- def last(*args)
236
- @association.last(*args)
256
+ def last(limit = nil)
257
+ load_target if find_from_target?
258
+ super
237
259
  end
238
260
 
239
261
  # Gives a record (or N records if a parameter is supplied) from the collection
@@ -261,8 +283,9 @@ module ActiveRecord
261
283
  # another_person_without.pets # => []
262
284
  # another_person_without.pets.take # => nil
263
285
  # another_person_without.pets.take(2) # => []
264
- def take(n = nil)
265
- @association.take(n)
286
+ def take(limit = nil)
287
+ load_target if find_from_target?
288
+ super
266
289
  end
267
290
 
268
291
  # Returns a new object of the collection type that has been instantiated
@@ -695,6 +718,12 @@ module ActiveRecord
695
718
  @association.destroy(*records)
696
719
  end
697
720
 
721
+ ##
722
+ # :method: distinct
723
+ #
724
+ # :call-seq:
725
+ # distinct(value = true)
726
+ #
698
727
  # Specifies whether the records should be unique or not.
699
728
  #
700
729
  # class Person < ActiveRecord::Base
@@ -709,17 +738,39 @@ module ActiveRecord
709
738
  #
710
739
  # person.pets.select(:name).distinct
711
740
  # # => [#<Pet name: "Fancy-Fancy">]
712
- def distinct
713
- @association.distinct
741
+ #
742
+ # person.pets.select(:name).distinct.distinct(false)
743
+ # # => [
744
+ # # #<Pet name: "Fancy-Fancy">,
745
+ # # #<Pet name: "Fancy-Fancy">
746
+ # # ]
747
+
748
+ #--
749
+ def uniq
750
+ load_target.uniq
751
+ end
752
+
753
+ def calculate(operation, column_name)
754
+ null_scope? ? scope.calculate(operation, column_name) : super
755
+ end
756
+
757
+ def pluck(*column_names)
758
+ null_scope? ? scope.pluck(*column_names) : super
714
759
  end
715
- alias uniq distinct
716
760
 
717
- # Count all records using SQL.
761
+ ##
762
+ # :method: count
763
+ #
764
+ # :call-seq:
765
+ # count(column_name = nil, &block)
766
+ #
767
+ # Count all records.
718
768
  #
719
769
  # class Person < ActiveRecord::Base
720
770
  # has_many :pets
721
771
  # end
722
772
  #
773
+ # # This will perform the count using SQL.
723
774
  # person.pets.count # => 3
724
775
  # person.pets
725
776
  # # => [
@@ -727,17 +778,11 @@ module ActiveRecord
727
778
  # # #<Pet id: 2, name: "Spook", person_id: 1>,
728
779
  # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
729
780
  # # ]
730
- def count(column_name = nil)
731
- @association.count(column_name)
732
- end
733
-
734
- def calculate(operation, column_name)
735
- null_scope? ? scope.calculate(operation, column_name) : super
736
- end
737
-
738
- def pluck(*column_names)
739
- null_scope? ? scope.pluck(*column_names) : super
740
- end
781
+ #
782
+ # Passing a block will select all of a person's pets in SQL and then
783
+ # perform the count using Ruby.
784
+ #
785
+ # person.pets.count { |pet| pet.name.include?('-') } # => 2
741
786
 
742
787
  # Returns the size of the collection. If the collection hasn't been loaded,
743
788
  # it executes a <tt>SELECT COUNT(*)</tt> query. Else it calls <tt>collection.size</tt>.
@@ -767,6 +812,12 @@ module ActiveRecord
767
812
  @association.size
768
813
  end
769
814
 
815
+ ##
816
+ # :method: length
817
+ #
818
+ # :call-seq:
819
+ # length()
820
+ #
770
821
  # Returns the size of the collection calling +size+ on the target.
771
822
  # If the collection has been already loaded, +length+ and +size+ are
772
823
  # equivalent. If not and you are going to need the records anyway this
@@ -787,9 +838,6 @@ module ActiveRecord
787
838
  # # #<Pet id: 2, name: "Spook", person_id: 1>,
788
839
  # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
789
840
  # # ]
790
- def length
791
- @association.length
792
- end
793
841
 
794
842
  # Returns +true+ if the collection is empty. If the collection has been
795
843
  # loaded it is equivalent
@@ -813,6 +861,12 @@ module ActiveRecord
813
861
  @association.empty?
814
862
  end
815
863
 
864
+ ##
865
+ # :method: any?
866
+ #
867
+ # :call-seq:
868
+ # any?()
869
+ #
816
870
  # Returns +true+ if the collection is not empty.
817
871
  #
818
872
  # class Person < ActiveRecord::Base
@@ -842,10 +896,13 @@ module ActiveRecord
842
896
  # pet.group == 'dogs'
843
897
  # end
844
898
  # # => true
845
- def any?(&block)
846
- @association.any?(&block)
847
- end
848
899
 
900
+ ##
901
+ # :method: many?
902
+ #
903
+ # :call-seq:
904
+ # many?()
905
+ #
849
906
  # Returns true if the collection has more than one record.
850
907
  # Equivalent to <tt>collection.size > 1</tt>.
851
908
  #
@@ -880,9 +937,6 @@ module ActiveRecord
880
937
  # pet.group == 'cats'
881
938
  # end
882
939
  # # => true
883
- def many?(&block)
884
- @association.many?(&block)
885
- end
886
940
 
887
941
  # Returns +true+ if the given +record+ is present in the collection.
888
942
  #
@@ -1064,6 +1118,7 @@ module ActiveRecord
1064
1118
  end
1065
1119
 
1066
1120
  def reset_scope # :nodoc:
1121
+ @offsets = {}
1067
1122
  @scope = nil
1068
1123
  self
1069
1124
  end
@@ -1073,16 +1128,30 @@ module ActiveRecord
1073
1128
  SpawnMethods,
1074
1129
  ].flat_map { |klass|
1075
1130
  klass.public_instance_methods(false)
1076
- } - self.public_instance_methods(false) + [:scoping]
1131
+ } - self.public_instance_methods(false) - [:select] + [:scoping]
1077
1132
 
1078
1133
  delegate(*delegate_methods, to: :scope)
1079
1134
 
1080
1135
  private
1081
1136
 
1137
+ def find_nth_with_limit(index, limit)
1138
+ load_target if find_from_target?
1139
+ super
1140
+ end
1141
+
1142
+ def find_nth_from_last(index)
1143
+ load_target if find_from_target?
1144
+ super
1145
+ end
1146
+
1082
1147
  def null_scope?
1083
1148
  @association.null_scope?
1084
1149
  end
1085
1150
 
1151
+ def find_from_target?
1152
+ @association.find_from_target?
1153
+ end
1154
+
1086
1155
  def exec_queries
1087
1156
  load_target
1088
1157
  end