activerecord 5.2.1.1 → 6.0.1

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 (270) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +738 -445
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +4 -2
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +9 -2
  7. data/lib/active_record/aggregations.rb +4 -2
  8. data/lib/active_record/association_relation.rb +18 -9
  9. data/lib/active_record/associations.rb +20 -15
  10. data/lib/active_record/associations/association.rb +69 -20
  11. data/lib/active_record/associations/association_scope.rb +4 -6
  12. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  14. data/lib/active_record/associations/builder/association.rb +14 -18
  15. data/lib/active_record/associations/builder/belongs_to.rb +19 -52
  16. data/lib/active_record/associations/builder/collection_association.rb +5 -15
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  18. data/lib/active_record/associations/builder/has_many.rb +2 -0
  19. data/lib/active_record/associations/builder/has_one.rb +35 -1
  20. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  21. data/lib/active_record/associations/collection_association.rb +15 -29
  22. data/lib/active_record/associations/collection_proxy.rb +19 -48
  23. data/lib/active_record/associations/foreign_association.rb +7 -0
  24. data/lib/active_record/associations/has_many_association.rb +11 -10
  25. data/lib/active_record/associations/has_many_through_association.rb +42 -25
  26. data/lib/active_record/associations/has_one_association.rb +28 -30
  27. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  28. data/lib/active_record/associations/join_dependency.rb +28 -28
  29. data/lib/active_record/associations/join_dependency/join_association.rb +27 -7
  30. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  31. data/lib/active_record/associations/preloader.rb +39 -31
  32. data/lib/active_record/associations/preloader/association.rb +38 -36
  33. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  34. data/lib/active_record/associations/singular_association.rb +2 -16
  35. data/lib/active_record/attribute_assignment.rb +7 -10
  36. data/lib/active_record/attribute_methods.rb +28 -100
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  38. data/lib/active_record/attribute_methods/dirty.rb +114 -38
  39. data/lib/active_record/attribute_methods/primary_key.rb +15 -22
  40. data/lib/active_record/attribute_methods/query.rb +2 -3
  41. data/lib/active_record/attribute_methods/read.rb +15 -53
  42. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  44. data/lib/active_record/attribute_methods/write.rb +17 -24
  45. data/lib/active_record/attributes.rb +13 -0
  46. data/lib/active_record/autosave_association.rb +27 -13
  47. data/lib/active_record/base.rb +2 -3
  48. data/lib/active_record/callbacks.rb +6 -20
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +140 -27
  50. data/lib/active_record/connection_adapters/abstract/database_limits.rb +22 -4
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +116 -127
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +26 -11
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
  54. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
  55. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
  56. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +135 -56
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +189 -43
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +151 -198
  61. data/lib/active_record/connection_adapters/column.rb +17 -13
  62. data/lib/active_record/connection_adapters/connection_specification.rb +55 -45
  63. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +9 -4
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +75 -13
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  66. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  67. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  68. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  69. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
  70. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  71. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -9
  72. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
  73. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +22 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +8 -2
  75. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  76. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  77. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  78. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  79. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  81. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  82. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
  83. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
  84. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +47 -0
  85. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  86. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +65 -77
  87. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  88. data/lib/active_record/connection_adapters/postgresql/utils.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql_adapter.rb +172 -74
  90. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  91. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  92. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  93. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +45 -5
  94. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
  95. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +131 -143
  96. data/lib/active_record/connection_handling.rb +155 -26
  97. data/lib/active_record/core.rb +104 -59
  98. data/lib/active_record/counter_cache.rb +4 -29
  99. data/lib/active_record/database_configurations.rb +233 -0
  100. data/lib/active_record/database_configurations/database_config.rb +37 -0
  101. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  102. data/lib/active_record/database_configurations/url_config.rb +79 -0
  103. data/lib/active_record/dynamic_matchers.rb +1 -1
  104. data/lib/active_record/enum.rb +38 -7
  105. data/lib/active_record/errors.rb +30 -16
  106. data/lib/active_record/explain.rb +1 -1
  107. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  108. data/lib/active_record/fixture_set/render_context.rb +17 -0
  109. data/lib/active_record/fixture_set/table_row.rb +153 -0
  110. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  111. data/lib/active_record/fixtures.rb +145 -472
  112. data/lib/active_record/gem_version.rb +3 -3
  113. data/lib/active_record/inheritance.rb +13 -3
  114. data/lib/active_record/insert_all.rb +179 -0
  115. data/lib/active_record/integration.rb +68 -16
  116. data/lib/active_record/internal_metadata.rb +10 -2
  117. data/lib/active_record/locking/optimistic.rb +5 -6
  118. data/lib/active_record/locking/pessimistic.rb +3 -3
  119. data/lib/active_record/log_subscriber.rb +7 -26
  120. data/lib/active_record/middleware/database_selector.rb +75 -0
  121. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  122. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  123. data/lib/active_record/migration.rb +100 -81
  124. data/lib/active_record/migration/command_recorder.rb +50 -6
  125. data/lib/active_record/migration/compatibility.rb +91 -64
  126. data/lib/active_record/model_schema.rb +34 -10
  127. data/lib/active_record/nested_attributes.rb +2 -2
  128. data/lib/active_record/no_touching.rb +7 -0
  129. data/lib/active_record/persistence.rb +233 -28
  130. data/lib/active_record/query_cache.rb +11 -4
  131. data/lib/active_record/querying.rb +33 -21
  132. data/lib/active_record/railtie.rb +81 -46
  133. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  134. data/lib/active_record/railties/controller_runtime.rb +30 -35
  135. data/lib/active_record/railties/databases.rake +196 -46
  136. data/lib/active_record/reflection.rb +42 -44
  137. data/lib/active_record/relation.rb +320 -70
  138. data/lib/active_record/relation/batches.rb +13 -10
  139. data/lib/active_record/relation/calculations.rb +67 -57
  140. data/lib/active_record/relation/delegation.rb +48 -35
  141. data/lib/active_record/relation/finder_methods.rb +30 -30
  142. data/lib/active_record/relation/merger.rb +19 -25
  143. data/lib/active_record/relation/predicate_builder.rb +18 -15
  144. data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -6
  145. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  146. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  147. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  148. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  149. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  150. data/lib/active_record/relation/query_attribute.rb +17 -10
  151. data/lib/active_record/relation/query_methods.rb +236 -73
  152. data/lib/active_record/relation/spawn_methods.rb +1 -1
  153. data/lib/active_record/relation/where_clause.rb +14 -10
  154. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  155. data/lib/active_record/result.rb +30 -11
  156. data/lib/active_record/sanitization.rb +32 -40
  157. data/lib/active_record/schema.rb +2 -11
  158. data/lib/active_record/schema_dumper.rb +22 -7
  159. data/lib/active_record/schema_migration.rb +5 -1
  160. data/lib/active_record/scoping.rb +8 -8
  161. data/lib/active_record/scoping/default.rb +6 -7
  162. data/lib/active_record/scoping/named.rb +21 -15
  163. data/lib/active_record/statement_cache.rb +32 -5
  164. data/lib/active_record/store.rb +87 -8
  165. data/lib/active_record/table_metadata.rb +10 -17
  166. data/lib/active_record/tasks/database_tasks.rb +195 -26
  167. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
  168. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  169. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  170. data/lib/active_record/test_databases.rb +23 -0
  171. data/lib/active_record/test_fixtures.rb +224 -0
  172. data/lib/active_record/timestamp.rb +39 -25
  173. data/lib/active_record/touch_later.rb +4 -2
  174. data/lib/active_record/transactions.rb +57 -66
  175. data/lib/active_record/translation.rb +1 -1
  176. data/lib/active_record/type.rb +3 -4
  177. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  178. data/lib/active_record/type_caster/connection.rb +15 -14
  179. data/lib/active_record/type_caster/map.rb +1 -4
  180. data/lib/active_record/validations.rb +1 -0
  181. data/lib/active_record/validations/uniqueness.rb +15 -27
  182. data/lib/arel.rb +58 -0
  183. data/lib/arel/alias_predication.rb +9 -0
  184. data/lib/arel/attributes.rb +22 -0
  185. data/lib/arel/attributes/attribute.rb +37 -0
  186. data/lib/arel/collectors/bind.rb +24 -0
  187. data/lib/arel/collectors/composite.rb +31 -0
  188. data/lib/arel/collectors/plain_string.rb +20 -0
  189. data/lib/arel/collectors/sql_string.rb +20 -0
  190. data/lib/arel/collectors/substitute_binds.rb +28 -0
  191. data/lib/arel/crud.rb +42 -0
  192. data/lib/arel/delete_manager.rb +18 -0
  193. data/lib/arel/errors.rb +9 -0
  194. data/lib/arel/expressions.rb +29 -0
  195. data/lib/arel/factory_methods.rb +49 -0
  196. data/lib/arel/insert_manager.rb +49 -0
  197. data/lib/arel/math.rb +45 -0
  198. data/lib/arel/nodes.rb +68 -0
  199. data/lib/arel/nodes/and.rb +32 -0
  200. data/lib/arel/nodes/ascending.rb +23 -0
  201. data/lib/arel/nodes/binary.rb +52 -0
  202. data/lib/arel/nodes/bind_param.rb +36 -0
  203. data/lib/arel/nodes/case.rb +55 -0
  204. data/lib/arel/nodes/casted.rb +50 -0
  205. data/lib/arel/nodes/comment.rb +29 -0
  206. data/lib/arel/nodes/count.rb +12 -0
  207. data/lib/arel/nodes/delete_statement.rb +45 -0
  208. data/lib/arel/nodes/descending.rb +23 -0
  209. data/lib/arel/nodes/equality.rb +18 -0
  210. data/lib/arel/nodes/extract.rb +24 -0
  211. data/lib/arel/nodes/false.rb +16 -0
  212. data/lib/arel/nodes/full_outer_join.rb +8 -0
  213. data/lib/arel/nodes/function.rb +44 -0
  214. data/lib/arel/nodes/grouping.rb +8 -0
  215. data/lib/arel/nodes/in.rb +8 -0
  216. data/lib/arel/nodes/infix_operation.rb +80 -0
  217. data/lib/arel/nodes/inner_join.rb +8 -0
  218. data/lib/arel/nodes/insert_statement.rb +37 -0
  219. data/lib/arel/nodes/join_source.rb +20 -0
  220. data/lib/arel/nodes/matches.rb +18 -0
  221. data/lib/arel/nodes/named_function.rb +23 -0
  222. data/lib/arel/nodes/node.rb +50 -0
  223. data/lib/arel/nodes/node_expression.rb +13 -0
  224. data/lib/arel/nodes/outer_join.rb +8 -0
  225. data/lib/arel/nodes/over.rb +15 -0
  226. data/lib/arel/nodes/regexp.rb +16 -0
  227. data/lib/arel/nodes/right_outer_join.rb +8 -0
  228. data/lib/arel/nodes/select_core.rb +67 -0
  229. data/lib/arel/nodes/select_statement.rb +41 -0
  230. data/lib/arel/nodes/sql_literal.rb +16 -0
  231. data/lib/arel/nodes/string_join.rb +11 -0
  232. data/lib/arel/nodes/table_alias.rb +27 -0
  233. data/lib/arel/nodes/terminal.rb +16 -0
  234. data/lib/arel/nodes/true.rb +16 -0
  235. data/lib/arel/nodes/unary.rb +45 -0
  236. data/lib/arel/nodes/unary_operation.rb +20 -0
  237. data/lib/arel/nodes/unqualified_column.rb +22 -0
  238. data/lib/arel/nodes/update_statement.rb +41 -0
  239. data/lib/arel/nodes/values_list.rb +9 -0
  240. data/lib/arel/nodes/window.rb +126 -0
  241. data/lib/arel/nodes/with.rb +11 -0
  242. data/lib/arel/order_predications.rb +13 -0
  243. data/lib/arel/predications.rb +257 -0
  244. data/lib/arel/select_manager.rb +271 -0
  245. data/lib/arel/table.rb +110 -0
  246. data/lib/arel/tree_manager.rb +72 -0
  247. data/lib/arel/update_manager.rb +34 -0
  248. data/lib/arel/visitors.rb +20 -0
  249. data/lib/arel/visitors/depth_first.rb +204 -0
  250. data/lib/arel/visitors/dot.rb +297 -0
  251. data/lib/arel/visitors/ibm_db.rb +34 -0
  252. data/lib/arel/visitors/informix.rb +62 -0
  253. data/lib/arel/visitors/mssql.rb +157 -0
  254. data/lib/arel/visitors/mysql.rb +83 -0
  255. data/lib/arel/visitors/oracle.rb +159 -0
  256. data/lib/arel/visitors/oracle12.rb +66 -0
  257. data/lib/arel/visitors/postgresql.rb +110 -0
  258. data/lib/arel/visitors/sqlite.rb +39 -0
  259. data/lib/arel/visitors/to_sql.rb +889 -0
  260. data/lib/arel/visitors/visitor.rb +46 -0
  261. data/lib/arel/visitors/where_sql.rb +23 -0
  262. data/lib/arel/window_predications.rb +9 -0
  263. data/lib/rails/generators/active_record/migration.rb +14 -1
  264. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  265. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  266. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  267. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  268. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  269. metadata +111 -27
  270. data/lib/active_record/collection_cache_key.rb +0 -53
@@ -13,33 +13,37 @@ module ActiveRecord
13
13
  class_attribute :aggregate_reflections, instance_writer: false, default: {}
14
14
  end
15
15
 
16
- def self.create(macro, name, scope, options, ar)
17
- klass = \
18
- case macro
19
- when :composed_of
20
- AggregateReflection
21
- when :has_many
22
- HasManyReflection
23
- when :has_one
24
- HasOneReflection
25
- when :belongs_to
26
- BelongsToReflection
27
- else
28
- raise "Unsupported Macro: #{macro}"
29
- end
16
+ class << self
17
+ def create(macro, name, scope, options, ar)
18
+ reflection = reflection_class_for(macro).new(name, scope, options, ar)
19
+ options[:through] ? ThroughReflection.new(reflection) : reflection
20
+ end
30
21
 
31
- reflection = klass.new(name, scope, options, ar)
32
- options[:through] ? ThroughReflection.new(reflection) : reflection
33
- end
22
+ def add_reflection(ar, name, reflection)
23
+ ar.clear_reflections_cache
24
+ name = -name.to_s
25
+ ar._reflections = ar._reflections.except(name).merge!(name => reflection)
26
+ end
34
27
 
35
- def self.add_reflection(ar, name, reflection)
36
- ar.clear_reflections_cache
37
- name = name.to_s
38
- ar._reflections = ar._reflections.except(name).merge!(name => reflection)
39
- end
28
+ def add_aggregate_reflection(ar, name, reflection)
29
+ ar.aggregate_reflections = ar.aggregate_reflections.merge(-name.to_s => reflection)
30
+ end
40
31
 
41
- def self.add_aggregate_reflection(ar, name, reflection)
42
- ar.aggregate_reflections = ar.aggregate_reflections.merge(name.to_s => reflection)
32
+ private
33
+ def reflection_class_for(macro)
34
+ case macro
35
+ when :composed_of
36
+ AggregateReflection
37
+ when :has_many
38
+ HasManyReflection
39
+ when :has_one
40
+ HasOneReflection
41
+ when :belongs_to
42
+ BelongsToReflection
43
+ else
44
+ raise "Unsupported Macro: #{macro}"
45
+ end
46
+ end
43
47
  end
44
48
 
45
49
  # \Reflection enables the ability to examine the associations and aggregations of
@@ -174,28 +178,24 @@ module ActiveRecord
174
178
  scope ? [scope] : []
175
179
  end
176
180
 
177
- def build_join_constraint(table, foreign_table)
178
- key = join_keys.key
179
- foreign_key = join_keys.foreign_key
180
-
181
- constraint = table[key].eq(foreign_table[foreign_key])
182
-
183
- if klass.finder_needs_type_condition?
184
- table.create_and([constraint, klass.send(:type_condition, table)])
185
- else
186
- constraint
187
- end
188
- end
189
-
190
- def join_scope(table, foreign_klass)
181
+ def join_scope(table, foreign_table, foreign_klass)
191
182
  predicate_builder = predicate_builder(table)
192
183
  scope_chain_items = join_scopes(table, predicate_builder)
193
184
  klass_scope = klass_join_scope(table, predicate_builder)
194
185
 
186
+ key = join_keys.key
187
+ foreign_key = join_keys.foreign_key
188
+
189
+ klass_scope.where!(table[key].eq(foreign_table[foreign_key]))
190
+
195
191
  if type
196
192
  klass_scope.where!(type => foreign_klass.polymorphic_name)
197
193
  end
198
194
 
195
+ if klass.finder_needs_type_condition?
196
+ klass_scope.where!(klass.send(:type_condition, table))
197
+ end
198
+
199
199
  scope_chain_items.inject(klass_scope, &:merge!)
200
200
  end
201
201
 
@@ -417,7 +417,7 @@ module ActiveRecord
417
417
  class AssociationReflection < MacroReflection #:nodoc:
418
418
  def compute_class(name)
419
419
  if polymorphic?
420
- raise ArgumentError, "Polymorphic association does not support to compute class."
420
+ raise ArgumentError, "Polymorphic associations do not support computing the class."
421
421
  end
422
422
  active_record.send(:compute_type, name)
423
423
  end
@@ -477,7 +477,7 @@ module ActiveRecord
477
477
  def check_preloadable!
478
478
  return unless scope
479
479
 
480
- if scope.arity > 0
480
+ unless scope.arity == 0
481
481
  raise ArgumentError, <<-MSG.squish
482
482
  The association scope '#{name}' is instance dependent (the scope
483
483
  block takes an argument). Preloading instance dependent scopes is
@@ -965,16 +965,14 @@ module ActiveRecord
965
965
  collect_join_reflections(seed + [self])
966
966
  end
967
967
 
968
- # TODO Change this to private once we've dropped Ruby 2.2 support.
969
- # Workaround for Ruby 2.2 "private attribute?" warning.
970
968
  protected
971
- attr_reader :delegate_reflection
972
-
973
969
  def actual_source_reflection # FIXME: this is a horrible name
974
970
  source_reflection.actual_source_reflection
975
971
  end
976
972
 
977
973
  private
974
+ attr_reader :delegate_reflection
975
+
978
976
  def collect_join_reflections(seed)
979
977
  a = source_reflection.add_as_source seed
980
978
  if options[:source_type]
@@ -5,10 +5,11 @@ module ActiveRecord
5
5
  class Relation
6
6
  MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
7
7
  :order, :joins, :left_outer_joins, :references,
8
- :extending, :unscope]
8
+ :extending, :unscope, :optimizer_hints, :annotate]
9
9
 
10
10
  SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering,
11
11
  :reverse_order, :distinct, :create_with, :skip_query_cache]
12
+
12
13
  CLAUSE_METHODS = [:where, :having, :from]
13
14
  INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :group, :having]
14
15
 
@@ -18,6 +19,7 @@ module ActiveRecord
18
19
  include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
19
20
 
20
21
  attr_reader :table, :klass, :loaded, :predicate_builder
22
+ attr_accessor :skip_preloading_value
21
23
  alias :model :klass
22
24
  alias :loaded? :loaded
23
25
  alias :locked? :lock_value
@@ -41,6 +43,17 @@ module ActiveRecord
41
43
  klass.arel_attribute(name, table)
42
44
  end
43
45
 
46
+ def bind_attribute(name, value) # :nodoc:
47
+ if reflection = klass._reflect_on_association(name)
48
+ name = reflection.foreign_key
49
+ value = value.read_attribute(reflection.klass.primary_key) unless value.nil?
50
+ end
51
+
52
+ attr = arel_attribute(name)
53
+ bind = predicate_builder.build_bind_attribute(attr.name, value)
54
+ yield attr, bind
55
+ end
56
+
44
57
  # Initializes new record from relation while maintaining the current
45
58
  # scope.
46
59
  #
@@ -54,7 +67,8 @@ module ActiveRecord
54
67
  # user = users.new { |user| user.name = 'Oscar' }
55
68
  # user.name # => Oscar
56
69
  def new(attributes = nil, &block)
57
- scoping { klass.new(scope_for_create(attributes), &block) }
70
+ block = _deprecated_scope_block("new", &block)
71
+ scoping { klass.new(attributes, &block) }
58
72
  end
59
73
 
60
74
  alias build new
@@ -82,7 +96,8 @@ module ActiveRecord
82
96
  if attributes.is_a?(Array)
83
97
  attributes.collect { |attr| create(attr, &block) }
84
98
  else
85
- scoping { klass.create(scope_for_create(attributes), &block) }
99
+ block = _deprecated_scope_block("create", &block)
100
+ scoping { klass.create(attributes, &block) }
86
101
  end
87
102
  end
88
103
 
@@ -96,7 +111,8 @@ module ActiveRecord
96
111
  if attributes.is_a?(Array)
97
112
  attributes.collect { |attr| create!(attr, &block) }
98
113
  else
99
- scoping { klass.create!(scope_for_create(attributes), &block) }
114
+ block = _deprecated_scope_block("create!", &block)
115
+ scoping { klass.create!(attributes, &block) }
100
116
  end
101
117
  end
102
118
 
@@ -143,23 +159,12 @@ module ActiveRecord
143
159
  # failed due to validation errors it won't be persisted, you get what
144
160
  # #create returns in such situation.
145
161
  #
146
- # Please note *this method is not atomic*, it runs first a SELECT, and if
162
+ # Please note <b>this method is not atomic</b>, it runs first a SELECT, and if
147
163
  # there are no results an INSERT is attempted. If there are other threads
148
164
  # or processes there is a race condition between both calls and it could
149
165
  # be the case that you end up with two similar records.
150
166
  #
151
- # Whether that is a problem or not depends on the logic of the
152
- # application, but in the particular case in which rows have a UNIQUE
153
- # constraint an exception may be raised, just retry:
154
- #
155
- # begin
156
- # CreditAccount.transaction(requires_new: true) do
157
- # CreditAccount.find_or_create_by(user_id: user.id)
158
- # end
159
- # rescue ActiveRecord::RecordNotUnique
160
- # retry
161
- # end
162
- #
167
+ # If this might be a problem for your application, please see #create_or_find_by.
163
168
  def find_or_create_by(attributes, &block)
164
169
  find_by(attributes) || create(attributes, &block)
165
170
  end
@@ -171,6 +176,51 @@ module ActiveRecord
171
176
  find_by(attributes) || create!(attributes, &block)
172
177
  end
173
178
 
179
+ # Attempts to create a record with the given attributes in a table that has a unique constraint
180
+ # on one or several of its columns. If a row already exists with one or several of these
181
+ # unique constraints, the exception such an insertion would normally raise is caught,
182
+ # and the existing record with those attributes is found using #find_by!.
183
+ #
184
+ # This is similar to #find_or_create_by, but avoids the problem of stale reads between the SELECT
185
+ # and the INSERT, as that method needs to first query the table, then attempt to insert a row
186
+ # if none is found.
187
+ #
188
+ # There are several drawbacks to #create_or_find_by, though:
189
+ #
190
+ # * The underlying table must have the relevant columns defined with unique constraints.
191
+ # * A unique constraint violation may be triggered by only one, or at least less than all,
192
+ # of the given attributes. This means that the subsequent #find_by! may fail to find a
193
+ # matching record, which will then raise an <tt>ActiveRecord::RecordNotFound</tt> exception,
194
+ # rather than a record with the given attributes.
195
+ # * While we avoid the race condition between SELECT -> INSERT from #find_or_create_by,
196
+ # we actually have another race condition between INSERT -> SELECT, which can be triggered
197
+ # if a DELETE between those two statements is run by another client. But for most applications,
198
+ # that's a significantly less likely condition to hit.
199
+ # * It relies on exception handling to handle control flow, which may be marginally slower.
200
+ # * The primary key may auto-increment on each create, even if it fails. This can accelerate
201
+ # the problem of running out of integers, if the underlying table is still stuck on a primary
202
+ # key of type int (note: All Rails apps since 5.1+ have defaulted to bigint, which is not liable
203
+ # to this problem).
204
+ #
205
+ # This method will return a record if all given attributes are covered by unique constraints
206
+ # (unless the INSERT -> DELETE -> SELECT race condition is triggered), but if creation was attempted
207
+ # and failed due to validation errors it won't be persisted, you get what #create returns in
208
+ # such situation.
209
+ def create_or_find_by(attributes, &block)
210
+ transaction(requires_new: true) { create(attributes, &block) }
211
+ rescue ActiveRecord::RecordNotUnique
212
+ find_by!(attributes)
213
+ end
214
+
215
+ # Like #create_or_find_by, but calls
216
+ # {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
217
+ # is raised if the created record is invalid.
218
+ def create_or_find_by!(attributes, &block)
219
+ transaction(requires_new: true) { create!(attributes, &block) }
220
+ rescue ActiveRecord::RecordNotUnique
221
+ find_by!(attributes)
222
+ end
223
+
174
224
  # Like #find_or_create_by, but calls {new}[rdoc-ref:Core#new]
175
225
  # instead of {create}[rdoc-ref:Persistence::ClassMethods#create].
176
226
  def find_or_initialize_by(attributes, &block)
@@ -185,7 +235,7 @@ module ActiveRecord
185
235
  # are needed by the next ones when eager loading is going on.
186
236
  #
187
237
  # Please see further details in the
188
- # {Active Record Query Interface guide}[http://guides.rubyonrails.org/active_record_querying.html#running-explain].
238
+ # {Active Record Query Interface guide}[https://guides.rubyonrails.org/active_record_querying.html#running-explain].
189
239
  def explain
190
240
  exec_explain(collecting_queries_for_explain { exec_queries })
191
241
  end
@@ -241,31 +291,99 @@ module ActiveRecord
241
291
  limit_value ? records.many? : size > 1
242
292
  end
243
293
 
244
- # Returns a cache key that can be used to identify the records fetched by
245
- # this query. The cache key is built with a fingerprint of the sql query,
246
- # the number of records matched by the query and a timestamp of the last
247
- # updated record. When a new record comes to match the query, or any of
248
- # the existing records is updated or deleted, the cache key changes.
294
+ # Returns a stable cache key that can be used to identify this query.
295
+ # The cache key is built with a fingerprint of the SQL query.
249
296
  #
250
- # Product.where("name like ?", "%Cosmic Encounter%").cache_key
251
- # # => "products/query-1850ab3d302391b85b8693e941286659-1-20150714212553907087000"
297
+ # Product.where("name like ?", "%Cosmic Encounter%").cache_key
298
+ # # => "products/query-1850ab3d302391b85b8693e941286659"
252
299
  #
253
- # If the collection is loaded, the method will iterate through the records
254
- # to generate the timestamp, otherwise it will trigger one SQL query like:
300
+ # If ActiveRecord::Base.collection_cache_versioning is turned off, as it was
301
+ # in Rails 6.0 and earlier, the cache key will also include a version.
255
302
  #
256
- # SELECT COUNT(*), MAX("products"."updated_at") FROM "products" WHERE (name like '%Cosmic Encounter%')
303
+ # ActiveRecord::Base.collection_cache_versioning = false
304
+ # Product.where("name like ?", "%Cosmic Encounter%").cache_key
305
+ # # => "products/query-1850ab3d302391b85b8693e941286659-1-20150714212553907087000"
257
306
  #
258
307
  # You can also pass a custom timestamp column to fetch the timestamp of the
259
308
  # last updated record.
260
309
  #
261
310
  # Product.where("name like ?", "%Game%").cache_key(:last_reviewed_at)
262
- #
263
- # You can customize the strategy to generate the key on a per model basis
264
- # overriding ActiveRecord::Base#collection_cache_key.
265
311
  def cache_key(timestamp_column = :updated_at)
266
312
  @cache_keys ||= {}
267
- @cache_keys[timestamp_column] ||= @klass.collection_cache_key(self, timestamp_column)
313
+ @cache_keys[timestamp_column] ||= klass.collection_cache_key(self, timestamp_column)
314
+ end
315
+
316
+ def compute_cache_key(timestamp_column = :updated_at) # :nodoc:
317
+ query_signature = ActiveSupport::Digest.hexdigest(to_sql)
318
+ key = "#{klass.model_name.cache_key}/query-#{query_signature}"
319
+
320
+ if cache_version(timestamp_column)
321
+ key
322
+ else
323
+ "#{key}-#{compute_cache_version(timestamp_column)}"
324
+ end
325
+ end
326
+ private :compute_cache_key
327
+
328
+ # Returns a cache version that can be used together with the cache key to form
329
+ # a recyclable caching scheme. The cache version is built with the number of records
330
+ # matching the query, and the timestamp of the last updated record. When a new record
331
+ # comes to match the query, or any of the existing records is updated or deleted,
332
+ # the cache version changes.
333
+ #
334
+ # If the collection is loaded, the method will iterate through the records
335
+ # to generate the timestamp, otherwise it will trigger one SQL query like:
336
+ #
337
+ # SELECT COUNT(*), MAX("products"."updated_at") FROM "products" WHERE (name like '%Cosmic Encounter%')
338
+ def cache_version(timestamp_column = :updated_at)
339
+ if collection_cache_versioning
340
+ @cache_versions ||= {}
341
+ @cache_versions[timestamp_column] ||= compute_cache_version(timestamp_column)
342
+ end
343
+ end
344
+
345
+ def compute_cache_version(timestamp_column) # :nodoc:
346
+ if loaded? || distinct_value
347
+ size = records.size
348
+ if size > 0
349
+ timestamp = max_by(&timestamp_column)._read_attribute(timestamp_column)
350
+ end
351
+ else
352
+ collection = eager_loading? ? apply_join_dependency : self
353
+
354
+ column = connection.visitor.compile(arel_attribute(timestamp_column))
355
+ select_values = "COUNT(*) AS #{connection.quote_column_name("size")}, MAX(%s) AS timestamp"
356
+
357
+ if collection.has_limit_or_offset?
358
+ query = collection.select("#{column} AS collection_cache_key_timestamp")
359
+ subquery_alias = "subquery_for_cache_key"
360
+ subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
361
+ arel = query.build_subquery(subquery_alias, select_values % subquery_column)
362
+ else
363
+ query = collection.unscope(:order)
364
+ query.select_values = [select_values % column]
365
+ arel = query.arel
366
+ end
367
+
368
+ result = connection.select_one(arel, nil)
369
+
370
+ if result
371
+ column_type = klass.type_for_attribute(timestamp_column)
372
+ timestamp = column_type.deserialize(result["timestamp"])
373
+ size = result["size"]
374
+ else
375
+ timestamp = nil
376
+ size = 0
377
+ end
378
+ end
379
+
380
+ if timestamp
381
+ "#{size}-#{timestamp.utc.to_s(cache_timestamp_format)}"
382
+ else
383
+ "#{size}"
384
+ end
268
385
  end
386
+ private :compute_cache_version
269
387
 
270
388
  # Scope all queries to the current scope.
271
389
  #
@@ -277,15 +395,12 @@ module ActiveRecord
277
395
  # Please check unscoped if you want to remove all previous scopes (including
278
396
  # the default_scope) during the execution of a block.
279
397
  def scoping
280
- previous, klass.current_scope = klass.current_scope(true), self unless @delegate_to_klass
281
- yield
282
- ensure
283
- klass.current_scope = previous unless @delegate_to_klass
398
+ already_in_scope? ? yield : _scoping(self) { yield }
284
399
  end
285
400
 
286
- def _exec_scope(*args, &block) # :nodoc:
401
+ def _exec_scope(name, *args, &block) # :nodoc:
287
402
  @delegate_to_klass = true
288
- instance_exec(*args, &block) || self
403
+ _scoping(_deprecated_spawn(name)) { instance_exec(*args, &block) || self }
289
404
  ensure
290
405
  @delegate_to_klass = false
291
406
  end
@@ -295,6 +410,8 @@ module ActiveRecord
295
410
  # trigger Active Record callbacks or validations. However, values passed to #update_all will still go through
296
411
  # Active Record's normal type casting and serialization.
297
412
  #
413
+ # Note: As Active Record callbacks are not triggered, this method will not automatically update +updated_at+/+updated_on+ columns.
414
+ #
298
415
  # ==== Parameters
299
416
  #
300
417
  # * +updates+ - A string, array, or hash representing the SET part of an SQL statement.
@@ -321,20 +438,79 @@ module ActiveRecord
321
438
  end
322
439
 
323
440
  stmt = Arel::UpdateManager.new
441
+ stmt.table(arel.join_sources.empty? ? table : arel.source)
442
+ stmt.key = arel_attribute(primary_key)
443
+ stmt.take(arel.limit)
444
+ stmt.offset(arel.offset)
445
+ stmt.order(*arel.orders)
446
+ stmt.wheres = arel.constraints
447
+
448
+ if updates.is_a?(Hash)
449
+ if klass.locking_enabled? &&
450
+ !updates.key?(klass.locking_column) &&
451
+ !updates.key?(klass.locking_column.to_sym)
452
+ attr = arel_attribute(klass.locking_column)
453
+ updates[attr.name] = _increment_attribute(attr)
454
+ end
455
+ stmt.set _substitute_values(updates)
456
+ else
457
+ stmt.set Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
458
+ end
324
459
 
325
- stmt.set Arel.sql(@klass.sanitize_sql_for_assignment(updates))
326
- stmt.table(table)
460
+ @klass.connection.update stmt, "#{@klass} Update All"
461
+ end
327
462
 
328
- if has_join_values? || offset_value
329
- @klass.connection.join_to_update(stmt, arel, arel_attribute(primary_key))
463
+ def update(id = :all, attributes) # :nodoc:
464
+ if id == :all
465
+ each { |record| record.update(attributes) }
330
466
  else
331
- stmt.key = arel_attribute(primary_key)
332
- stmt.take(arel.limit)
333
- stmt.order(*arel.orders)
334
- stmt.wheres = arel.constraints
467
+ klass.update(id, attributes)
335
468
  end
469
+ end
336
470
 
337
- @klass.connection.update stmt, "#{@klass} Update All"
471
+ def update_counters(counters) # :nodoc:
472
+ touch = counters.delete(:touch)
473
+
474
+ updates = {}
475
+ counters.each do |counter_name, value|
476
+ attr = arel_attribute(counter_name)
477
+ updates[attr.name] = _increment_attribute(attr, value)
478
+ end
479
+
480
+ if touch
481
+ names = touch if touch != true
482
+ touch_updates = klass.touch_attributes_with_time(*names)
483
+ updates.merge!(touch_updates) unless touch_updates.empty?
484
+ end
485
+
486
+ update_all updates
487
+ end
488
+
489
+ # Touches all records in the current relation without instantiating records first with the +updated_at+/+updated_on+ attributes
490
+ # set to the current time or the time specified.
491
+ # This method can be passed attribute names and an optional time argument.
492
+ # If attribute names are passed, they are updated along with +updated_at+/+updated_on+ attributes.
493
+ # If no time argument is passed, the current time is used as default.
494
+ #
495
+ # === Examples
496
+ #
497
+ # # Touch all records
498
+ # Person.all.touch_all
499
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670'"
500
+ #
501
+ # # Touch multiple records with a custom attribute
502
+ # Person.all.touch_all(:created_at)
503
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670', \"created_at\" = '2018-01-04 22:55:23.132670'"
504
+ #
505
+ # # Touch multiple records with a specified time
506
+ # Person.all.touch_all(time: Time.new(2020, 5, 16, 0, 0, 0))
507
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2020-05-16 00:00:00'"
508
+ #
509
+ # # Touch records with scope
510
+ # Person.where(name: 'David').touch_all
511
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670' WHERE \"people\".\"name\" = 'David'"
512
+ def touch_all(*names, time: nil)
513
+ update_all klass.touch_attributes_with_time(*names, time: time)
338
514
  end
339
515
 
340
516
  # Destroys the records by instantiating each
@@ -377,8 +553,8 @@ module ActiveRecord
377
553
  # # => ActiveRecord::ActiveRecordError: delete_all doesn't support distinct
378
554
  def delete_all
379
555
  invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select do |method|
380
- value = get_value(method)
381
- SINGLE_VALUE_METHODS.include?(method) ? value : value.any?
556
+ value = @values[method]
557
+ method == :distinct ? value : value&.any?
382
558
  end
383
559
  if invalid_methods.any?
384
560
  raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
@@ -390,13 +566,12 @@ module ActiveRecord
390
566
  end
391
567
 
392
568
  stmt = Arel::DeleteManager.new
393
- stmt.from(table)
394
-
395
- if has_join_values? || has_limit_or_offset?
396
- @klass.connection.join_to_delete(stmt, arel, arel_attribute(primary_key))
397
- else
398
- stmt.wheres = arel.constraints
399
- end
569
+ stmt.from(arel.join_sources.empty? ? table : arel.source)
570
+ stmt.key = arel_attribute(primary_key)
571
+ stmt.take(arel.limit)
572
+ stmt.offset(arel.offset)
573
+ stmt.order(*arel.orders)
574
+ stmt.wheres = arel.constraints
400
575
 
401
576
  affected = @klass.connection.delete(stmt, "#{@klass} Destroy")
402
577
 
@@ -404,6 +579,32 @@ module ActiveRecord
404
579
  affected
405
580
  end
406
581
 
582
+ # Finds and destroys all records matching the specified conditions.
583
+ # This is short-hand for <tt>relation.where(condition).destroy_all</tt>.
584
+ # Returns the collection of objects that were destroyed.
585
+ #
586
+ # If no record is found, returns empty array.
587
+ #
588
+ # Person.destroy_by(id: 13)
589
+ # Person.destroy_by(name: 'Spartacus', rating: 4)
590
+ # Person.destroy_by("published_at < ?", 2.weeks.ago)
591
+ def destroy_by(*args)
592
+ where(*args).destroy_all
593
+ end
594
+
595
+ # Finds and deletes all records matching the specified conditions.
596
+ # This is short-hand for <tt>relation.where(condition).delete_all</tt>.
597
+ # Returns the number of rows affected.
598
+ #
599
+ # If no record is found, returns <tt>0</tt> as zero rows were affected.
600
+ #
601
+ # Person.delete_by(id: 13)
602
+ # Person.delete_by(name: 'Spartacus', rating: 4)
603
+ # Person.delete_by("published_at < ?", 2.weeks.ago)
604
+ def delete_by(*args)
605
+ where(*args).delete_all
606
+ end
607
+
407
608
  # Causes the records to be loaded from the database if they have not
408
609
  # been loaded already. You can use this if for some reason you need
409
610
  # to explicitly load some records before actually using them. The
@@ -424,9 +625,11 @@ module ActiveRecord
424
625
 
425
626
  def reset
426
627
  @delegate_to_klass = false
628
+ @_deprecated_scope_source = nil
427
629
  @to_sql = @arel = @loaded = @should_eager_load = nil
428
630
  @records = [].freeze
429
631
  @offsets = {}
632
+ @take = nil
430
633
  self
431
634
  end
432
635
 
@@ -456,10 +659,8 @@ module ActiveRecord
456
659
  where_clause.to_h(relation_table_name)
457
660
  end
458
661
 
459
- def scope_for_create(attributes = nil)
460
- scope = where_values_hash.merge!(create_with_value.stringify_keys)
461
- scope.merge!(attributes) if attributes
462
- scope
662
+ def scope_for_create
663
+ where_values_hash.merge!(create_with_value.stringify_keys)
463
664
  end
464
665
 
465
666
  # Returns true if relation needs eager loading.
@@ -524,17 +725,72 @@ module ActiveRecord
524
725
  ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins)
525
726
  end
526
727
 
728
+ def preload_associations(records) # :nodoc:
729
+ preload = preload_values
730
+ preload += includes_values unless eager_loading?
731
+ preloader = nil
732
+ preload.each do |associations|
733
+ preloader ||= build_preloader
734
+ preloader.preload records, associations
735
+ end
736
+ end
737
+
738
+ attr_reader :_deprecated_scope_source # :nodoc:
739
+
527
740
  protected
741
+ attr_writer :_deprecated_scope_source # :nodoc:
528
742
 
529
743
  def load_records(records)
530
744
  @records = records.freeze
531
745
  @loaded = true
532
746
  end
533
747
 
748
+ def null_relation? # :nodoc:
749
+ is_a?(NullRelation)
750
+ end
751
+
534
752
  private
753
+ def already_in_scope?
754
+ @delegate_to_klass && begin
755
+ scope = klass.current_scope(true)
756
+ scope && !scope._deprecated_scope_source
757
+ end
758
+ end
535
759
 
536
- def has_join_values?
537
- joins_values.any? || left_outer_joins_values.any?
760
+ def _deprecated_spawn(name)
761
+ spawn.tap { |scope| scope._deprecated_scope_source = name }
762
+ end
763
+
764
+ def _deprecated_scope_block(name, &block)
765
+ -> record do
766
+ klass.current_scope = _deprecated_spawn(name)
767
+ yield record if block_given?
768
+ end
769
+ end
770
+
771
+ def _scoping(scope)
772
+ previous, klass.current_scope = klass.current_scope(true), scope
773
+ yield
774
+ ensure
775
+ klass.current_scope = previous
776
+ end
777
+
778
+ def _substitute_values(values)
779
+ values.map do |name, value|
780
+ attr = arel_attribute(name)
781
+ unless Arel.arel_node?(value)
782
+ type = klass.type_for_attribute(attr.name)
783
+ value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
784
+ end
785
+ [attr, value]
786
+ end
787
+ end
788
+
789
+ def _increment_attribute(attribute, value = 1)
790
+ bind = predicate_builder.build_bind_attribute(attribute.name, value.abs)
791
+ expr = table.coalesce(Arel::Nodes::UnqualifiedColumn.new(attribute), 0)
792
+ expr = value < 0 ? expr - bind : expr + bind
793
+ expr.expr
538
794
  end
539
795
 
540
796
  def exec_queries(&block)
@@ -542,7 +798,7 @@ module ActiveRecord
542
798
  @records =
543
799
  if eager_loading?
544
800
  apply_join_dependency do |relation, join_dependency|
545
- if ActiveRecord::NullRelation === relation
801
+ if relation.null_relation?
546
802
  []
547
803
  else
548
804
  relation = join_dependency.apply_column_aliases(relation)
@@ -554,13 +810,7 @@ module ActiveRecord
554
810
  klass.find_by_sql(arel, &block).freeze
555
811
  end
556
812
 
557
- preload = preload_values
558
- preload += includes_values unless eager_loading?
559
- preloader = nil
560
- preload.each do |associations|
561
- preloader ||= build_preloader
562
- preloader.preload @records, associations
563
- end
813
+ preload_associations(@records) unless skip_preloading_value
564
814
 
565
815
  @records.each(&:readonly!) if readonly_value
566
816