activerecord 6.0.1 → 6.1.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (270) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1363 -647
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/lib/active_record/aggregations.rb +5 -6
  6. data/lib/active_record/association_relation.rb +26 -15
  7. data/lib/active_record/associations/alias_tracker.rb +19 -16
  8. data/lib/active_record/associations/association.rb +55 -37
  9. data/lib/active_record/associations/association_scope.rb +19 -15
  10. data/lib/active_record/associations/belongs_to_association.rb +23 -10
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
  12. data/lib/active_record/associations/builder/association.rb +32 -5
  13. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  14. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -3
  16. data/lib/active_record/associations/builder/has_many.rb +6 -2
  17. data/lib/active_record/associations/builder/has_one.rb +11 -14
  18. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  19. data/lib/active_record/associations/collection_association.rb +38 -13
  20. data/lib/active_record/associations/collection_proxy.rb +14 -7
  21. data/lib/active_record/associations/foreign_association.rb +13 -0
  22. data/lib/active_record/associations/has_many_association.rb +24 -3
  23. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  24. data/lib/active_record/associations/has_one_association.rb +15 -1
  25. data/lib/active_record/associations/join_dependency/join_association.rb +39 -16
  26. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  27. data/lib/active_record/associations/join_dependency.rb +73 -42
  28. data/lib/active_record/associations/preloader/association.rb +49 -25
  29. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  30. data/lib/active_record/associations/preloader.rb +12 -7
  31. data/lib/active_record/associations/singular_association.rb +1 -1
  32. data/lib/active_record/associations/through_association.rb +1 -1
  33. data/lib/active_record/associations.rb +119 -12
  34. data/lib/active_record/attribute_assignment.rb +10 -9
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
  36. data/lib/active_record/attribute_methods/dirty.rb +3 -13
  37. data/lib/active_record/attribute_methods/primary_key.rb +6 -4
  38. data/lib/active_record/attribute_methods/query.rb +3 -6
  39. data/lib/active_record/attribute_methods/read.rb +8 -12
  40. data/lib/active_record/attribute_methods/serialization.rb +11 -6
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  42. data/lib/active_record/attribute_methods/write.rb +12 -21
  43. data/lib/active_record/attribute_methods.rb +64 -54
  44. data/lib/active_record/attributes.rb +33 -9
  45. data/lib/active_record/autosave_association.rb +56 -41
  46. data/lib/active_record/base.rb +2 -14
  47. data/lib/active_record/callbacks.rb +153 -24
  48. data/lib/active_record/coders/yaml_column.rb +24 -3
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +190 -136
  50. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -38
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -9
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +44 -35
  54. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +152 -116
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +145 -52
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +267 -105
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +94 -36
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +63 -77
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +136 -111
  62. data/lib/active_record/connection_adapters/column.rb +15 -1
  63. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  64. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  65. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/database_statements.rb +30 -36
  67. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  68. data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
  69. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
  70. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  71. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
  72. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -13
  73. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  74. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
  75. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  76. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  77. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  78. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +21 -56
  79. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  81. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  83. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  87. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  91. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
  92. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/quoting.rb +30 -4
  96. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  97. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
  99. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  100. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
  101. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  102. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  103. data/lib/active_record/connection_adapters/postgresql_adapter.rb +80 -66
  104. data/lib/active_record/connection_adapters/schema_cache.rb +130 -15
  105. data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
  106. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +38 -12
  107. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
  108. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  109. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
  110. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +57 -57
  111. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  112. data/lib/active_record/connection_adapters.rb +52 -0
  113. data/lib/active_record/connection_handling.rb +218 -87
  114. data/lib/active_record/core.rb +276 -68
  115. data/lib/active_record/counter_cache.rb +4 -1
  116. data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
  117. data/lib/active_record/database_configurations/database_config.rb +52 -9
  118. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  119. data/lib/active_record/database_configurations/url_config.rb +15 -41
  120. data/lib/active_record/database_configurations.rb +125 -85
  121. data/lib/active_record/delegated_type.rb +209 -0
  122. data/lib/active_record/destroy_association_async_job.rb +36 -0
  123. data/lib/active_record/dynamic_matchers.rb +2 -3
  124. data/lib/active_record/enum.rb +80 -38
  125. data/lib/active_record/errors.rb +47 -12
  126. data/lib/active_record/explain.rb +9 -5
  127. data/lib/active_record/explain_subscriber.rb +1 -1
  128. data/lib/active_record/fixture_set/file.rb +10 -17
  129. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  130. data/lib/active_record/fixture_set/render_context.rb +1 -1
  131. data/lib/active_record/fixture_set/table_row.rb +2 -3
  132. data/lib/active_record/fixture_set/table_rows.rb +0 -1
  133. data/lib/active_record/fixtures.rb +58 -12
  134. data/lib/active_record/gem_version.rb +3 -3
  135. data/lib/active_record/inheritance.rb +40 -21
  136. data/lib/active_record/insert_all.rb +42 -9
  137. data/lib/active_record/integration.rb +3 -5
  138. data/lib/active_record/internal_metadata.rb +18 -7
  139. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  140. data/lib/active_record/locking/optimistic.rb +33 -18
  141. data/lib/active_record/locking/pessimistic.rb +6 -2
  142. data/lib/active_record/log_subscriber.rb +28 -9
  143. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  144. data/lib/active_record/middleware/database_selector/resolver.rb +6 -2
  145. data/lib/active_record/middleware/database_selector.rb +4 -2
  146. data/lib/active_record/migration/command_recorder.rb +53 -45
  147. data/lib/active_record/migration/compatibility.rb +75 -21
  148. data/lib/active_record/migration/join_table.rb +0 -1
  149. data/lib/active_record/migration.rb +115 -85
  150. data/lib/active_record/model_schema.rb +117 -15
  151. data/lib/active_record/nested_attributes.rb +2 -5
  152. data/lib/active_record/no_touching.rb +1 -1
  153. data/lib/active_record/null_relation.rb +0 -1
  154. data/lib/active_record/persistence.rb +50 -46
  155. data/lib/active_record/query_cache.rb +15 -5
  156. data/lib/active_record/querying.rb +12 -7
  157. data/lib/active_record/railtie.rb +65 -45
  158. data/lib/active_record/railties/console_sandbox.rb +2 -4
  159. data/lib/active_record/railties/databases.rake +280 -99
  160. data/lib/active_record/readonly_attributes.rb +4 -0
  161. data/lib/active_record/reflection.rb +77 -63
  162. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  163. data/lib/active_record/relation/batches.rb +38 -32
  164. data/lib/active_record/relation/calculations.rb +106 -45
  165. data/lib/active_record/relation/delegation.rb +9 -7
  166. data/lib/active_record/relation/finder_methods.rb +45 -16
  167. data/lib/active_record/relation/from_clause.rb +5 -1
  168. data/lib/active_record/relation/merger.rb +27 -26
  169. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  170. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  171. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
  172. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  173. data/lib/active_record/relation/predicate_builder.rb +59 -40
  174. data/lib/active_record/relation/query_methods.rb +341 -188
  175. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  176. data/lib/active_record/relation/spawn_methods.rb +8 -8
  177. data/lib/active_record/relation/where_clause.rb +111 -62
  178. data/lib/active_record/relation.rb +116 -83
  179. data/lib/active_record/result.rb +41 -34
  180. data/lib/active_record/runtime_registry.rb +2 -2
  181. data/lib/active_record/sanitization.rb +6 -17
  182. data/lib/active_record/schema_dumper.rb +34 -4
  183. data/lib/active_record/schema_migration.rb +2 -8
  184. data/lib/active_record/scoping/default.rb +1 -4
  185. data/lib/active_record/scoping/named.rb +7 -18
  186. data/lib/active_record/scoping.rb +0 -1
  187. data/lib/active_record/secure_token.rb +16 -8
  188. data/lib/active_record/serialization.rb +5 -3
  189. data/lib/active_record/signed_id.rb +116 -0
  190. data/lib/active_record/statement_cache.rb +20 -4
  191. data/lib/active_record/store.rb +9 -4
  192. data/lib/active_record/suppressor.rb +2 -2
  193. data/lib/active_record/table_metadata.rb +42 -36
  194. data/lib/active_record/tasks/database_tasks.rb +140 -113
  195. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
  196. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
  197. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
  198. data/lib/active_record/test_databases.rb +5 -4
  199. data/lib/active_record/test_fixtures.rb +87 -20
  200. data/lib/active_record/timestamp.rb +4 -7
  201. data/lib/active_record/touch_later.rb +20 -21
  202. data/lib/active_record/transactions.rb +25 -72
  203. data/lib/active_record/type/adapter_specific_registry.rb +2 -5
  204. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  205. data/lib/active_record/type/serialized.rb +6 -3
  206. data/lib/active_record/type/time.rb +10 -0
  207. data/lib/active_record/type/type_map.rb +0 -1
  208. data/lib/active_record/type/unsigned_integer.rb +0 -1
  209. data/lib/active_record/type.rb +8 -2
  210. data/lib/active_record/type_caster/connection.rb +0 -1
  211. data/lib/active_record/type_caster/map.rb +8 -5
  212. data/lib/active_record/validations/associated.rb +1 -2
  213. data/lib/active_record/validations/numericality.rb +35 -0
  214. data/lib/active_record/validations/uniqueness.rb +24 -4
  215. data/lib/active_record/validations.rb +3 -3
  216. data/lib/active_record.rb +7 -13
  217. data/lib/arel/attributes/attribute.rb +4 -0
  218. data/lib/arel/collectors/bind.rb +5 -0
  219. data/lib/arel/collectors/composite.rb +8 -0
  220. data/lib/arel/collectors/sql_string.rb +7 -0
  221. data/lib/arel/collectors/substitute_binds.rb +7 -0
  222. data/lib/arel/nodes/binary.rb +82 -8
  223. data/lib/arel/nodes/bind_param.rb +8 -0
  224. data/lib/arel/nodes/casted.rb +21 -9
  225. data/lib/arel/nodes/equality.rb +6 -9
  226. data/lib/arel/nodes/grouping.rb +3 -0
  227. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  228. data/lib/arel/nodes/in.rb +8 -1
  229. data/lib/arel/nodes/infix_operation.rb +13 -1
  230. data/lib/arel/nodes/join_source.rb +1 -1
  231. data/lib/arel/nodes/node.rb +7 -6
  232. data/lib/arel/nodes/ordering.rb +27 -0
  233. data/lib/arel/nodes/sql_literal.rb +3 -0
  234. data/lib/arel/nodes/table_alias.rb +7 -3
  235. data/lib/arel/nodes/unary.rb +0 -1
  236. data/lib/arel/nodes.rb +3 -1
  237. data/lib/arel/predications.rb +17 -24
  238. data/lib/arel/select_manager.rb +1 -2
  239. data/lib/arel/table.rb +13 -5
  240. data/lib/arel/visitors/dot.rb +14 -3
  241. data/lib/arel/visitors/mysql.rb +11 -1
  242. data/lib/arel/visitors/postgresql.rb +15 -5
  243. data/lib/arel/visitors/sqlite.rb +0 -1
  244. data/lib/arel/visitors/to_sql.rb +89 -79
  245. data/lib/arel/visitors/visitor.rb +0 -1
  246. data/lib/arel/visitors.rb +0 -7
  247. data/lib/arel.rb +5 -9
  248. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  249. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  250. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  251. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  252. data/lib/rails/generators/active_record/migration.rb +6 -2
  253. data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
  254. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  255. metadata +30 -29
  256. data/lib/active_record/attribute_decorators.rb +0 -90
  257. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  258. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  259. data/lib/active_record/define_callbacks.rb +0 -22
  260. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  261. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  262. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  263. data/lib/arel/attributes.rb +0 -22
  264. data/lib/arel/visitors/depth_first.rb +0 -204
  265. data/lib/arel/visitors/ibm_db.rb +0 -34
  266. data/lib/arel/visitors/informix.rb +0 -62
  267. data/lib/arel/visitors/mssql.rb +0 -157
  268. data/lib/arel/visitors/oracle.rb +0 -159
  269. data/lib/arel/visitors/oracle12.rb +0 -66
  270. data/lib/arel/visitors/where_sql.rb +0 -23
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2004-2019 David Heinemeier Hansson
1
+ Copyright (c) 2004-2022 David Heinemeier Hansson
2
2
 
3
3
  Arel originally copyright (c) 2007-2016 Nick Kallen, Bryan Helmkamp, Emilio Tagua, Aaron Patterson
4
4
 
data/README.rdoc CHANGED
@@ -132,7 +132,7 @@ This would also define the following accessors: <tt>Product#name</tt> and
132
132
  SQLite3[link:classes/ActiveRecord/ConnectionAdapters/SQLite3Adapter.html].
133
133
 
134
134
 
135
- * Logging support for Log4r[https://github.com/colbygk/log4r] and Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc].
135
+ * Logging support for Log4r[https://github.com/colbygk/log4r] and Logger[https://ruby-doc.org/stdlib/libdoc/logger/rdoc/].
136
136
 
137
137
  ActiveRecord::Base.logger = ActiveSupport::Logger.new(STDOUT)
138
138
  ActiveRecord::Base.logger = Log4r::Logger.new('Application Log')
@@ -140,7 +140,7 @@ This would also define the following accessors: <tt>Product#name</tt> and
140
140
 
141
141
  * Database agnostic schema management with Migrations.
142
142
 
143
- class AddSystemSettings < ActiveRecord::Migration[5.0]
143
+ class AddSystemSettings < ActiveRecord::Migration[6.0]
144
144
  def up
145
145
  create_table :system_settings do |t|
146
146
  t.string :name
@@ -194,7 +194,7 @@ The latest version of Active Record can be installed with RubyGems:
194
194
 
195
195
  Source code can be downloaded as part of the Rails project on GitHub:
196
196
 
197
- * https://github.com/rails/rails/tree/master/activerecord
197
+ * https://github.com/rails/rails/tree/main/activerecord
198
198
 
199
199
 
200
200
  == License
@@ -216,4 +216,4 @@ Bug reports for the Ruby on Rails project can be filed here:
216
216
 
217
217
  Feature requests should be discussed on the rails-core mailing list here:
218
218
 
219
- * https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
219
+ * https://discuss.rubyonrails.org/c/rubyonrails-core
@@ -14,7 +14,6 @@ module ActiveRecord
14
14
  end
15
15
 
16
16
  private
17
-
18
17
  def clear_aggregation_cache
19
18
  @aggregation_cache.clear if persisted?
20
19
  end
@@ -142,7 +141,7 @@ module ActiveRecord
142
141
  # converted to an instance of value class if necessary.
143
142
  #
144
143
  # For example, the +NetworkResource+ model has +network_address+ and +cidr_range+ attributes that should be
145
- # aggregated using the +NetAddr::CIDR+ value class (http://www.rubydoc.info/gems/netaddr/1.5.0/NetAddr/CIDR).
144
+ # aggregated using the +NetAddr::CIDR+ value class (https://www.rubydoc.info/gems/netaddr/1.5.0/NetAddr/CIDR).
146
145
  # The constructor for the value class is called +create+ and it expects a CIDR address string as a parameter.
147
146
  # New values can be assigned to the value object using either another +NetAddr::CIDR+ object, a string
148
147
  # or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to meet
@@ -245,8 +244,8 @@ module ActiveRecord
245
244
  private
246
245
  def reader_method(name, class_name, mapping, allow_nil, constructor)
247
246
  define_method(name) do
248
- if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? { |key, _| !_read_attribute(key).nil? })
249
- attrs = mapping.collect { |key, _| _read_attribute(key) }
247
+ if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? { |key, _| !read_attribute(key).nil? })
248
+ attrs = mapping.collect { |key, _| read_attribute(key) }
250
249
  object = constructor.respond_to?(:call) ?
251
250
  constructor.call(*attrs) :
252
251
  class_name.constantize.send(constructor, *attrs)
@@ -272,10 +271,10 @@ module ActiveRecord
272
271
  end
273
272
 
274
273
  if part.nil? && allow_nil
275
- mapping.each { |key, _| self[key] = nil }
274
+ mapping.each { |key, _| write_attribute(key, nil) }
276
275
  @aggregation_cache[name] = nil
277
276
  else
278
- mapping.each { |key, value| self[key] = part.send(value) }
277
+ mapping.each { |key, value| write_attribute(key, part.send(value)) }
279
278
  @aggregation_cache[name] = part.freeze
280
279
  end
281
280
  end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
- class AssociationRelation < Relation
5
- def initialize(klass, association)
4
+ class AssociationRelation < Relation # :nodoc:
5
+ def initialize(klass, association, **)
6
6
  super(klass)
7
7
  @association = association
8
8
  end
@@ -15,29 +15,40 @@ module ActiveRecord
15
15
  other == records
16
16
  end
17
17
 
18
+ %w(insert insert_all insert! insert_all! upsert upsert_all).each do |method|
19
+ class_eval <<~RUBY
20
+ def #{method}(attributes, **kwargs)
21
+ if @association.reflection.through_reflection?
22
+ raise ArgumentError, "Bulk insert or upsert is currently not supported for has_many through association"
23
+ end
24
+
25
+ scoping { klass.#{method}(attributes, **kwargs) }
26
+ end
27
+ RUBY
28
+ end
29
+
18
30
  def build(attributes = nil, &block)
19
- block = _deprecated_scope_block("new", &block)
20
- @association.scoping(self) do
21
- @association.build(attributes, &block)
31
+ if attributes.is_a?(Array)
32
+ attributes.collect { |attr| build(attr, &block) }
33
+ else
34
+ block = current_scope_restoring_block(&block)
35
+ scoping { _new(attributes, &block) }
22
36
  end
23
37
  end
24
38
  alias new build
25
39
 
26
- def create(attributes = nil, &block)
27
- block = _deprecated_scope_block("create", &block)
28
- @association.scoping(self) do
40
+ private
41
+ def _new(attributes, &block)
42
+ @association.build(attributes, &block)
43
+ end
44
+
45
+ def _create(attributes, &block)
29
46
  @association.create(attributes, &block)
30
47
  end
31
- end
32
48
 
33
- def create!(attributes = nil, &block)
34
- block = _deprecated_scope_block("create!", &block)
35
- @association.scoping(self) do
49
+ def _create!(attributes, &block)
36
50
  @association.create!(attributes, &block)
37
51
  end
38
- end
39
-
40
- private
41
52
 
42
53
  def exec_queries
43
54
  super do |record|
@@ -6,9 +6,14 @@ module ActiveRecord
6
6
  module Associations
7
7
  # Keeps track of table aliases for ActiveRecord::Associations::JoinDependency
8
8
  class AliasTracker # :nodoc:
9
- def self.create(connection, initial_table, joins)
9
+ def self.create(connection, initial_table, joins, aliases = nil)
10
10
  if joins.empty?
11
- aliases = Hash.new(0)
11
+ aliases ||= Hash.new(0)
12
+ elsif aliases
13
+ default_proc = aliases.default_proc || proc { 0 }
14
+ aliases.default_proc = proc { |h, k|
15
+ h[k] = initial_count_for(connection, k, joins) + default_proc.call(h, k)
16
+ }
12
17
  else
13
18
  aliases = Hash.new { |h, k|
14
19
  h[k] = initial_count_for(connection, k, joins)
@@ -32,8 +37,6 @@ module ActiveRecord
32
37
  ).size
33
38
  elsif join.is_a?(Arel::Nodes::Join)
34
39
  join.left.name == name ? 1 : 0
35
- elsif join.is_a?(Hash)
36
- join[name]
37
40
  else
38
41
  raise ArgumentError, "joins list should be initialized by list of Arel::Nodes::Join"
39
42
  end
@@ -48,31 +51,31 @@ module ActiveRecord
48
51
  @connection = connection
49
52
  end
50
53
 
51
- def aliased_table_for(table_name, aliased_name, type_caster)
52
- if aliases[table_name].zero?
54
+ def aliased_table_for(arel_table, table_name = nil)
55
+ table_name ||= arel_table.name
56
+
57
+ if aliases[table_name] == 0
53
58
  # If it's zero, we can have our table_name
54
59
  aliases[table_name] = 1
55
- Arel::Table.new(table_name, type_caster: type_caster)
60
+ arel_table = arel_table.alias(table_name) if arel_table.name != table_name
56
61
  else
57
62
  # Otherwise, we need to use an alias
58
- aliased_name = @connection.table_alias_for(aliased_name)
63
+ aliased_name = @connection.table_alias_for(yield)
59
64
 
60
65
  # Update the count
61
- aliases[aliased_name] += 1
66
+ count = aliases[aliased_name] += 1
62
67
 
63
- table_alias = if aliases[aliased_name] > 1
64
- "#{truncate(aliased_name)}_#{aliases[aliased_name]}"
65
- else
66
- aliased_name
67
- end
68
- Arel::Table.new(table_name, type_caster: type_caster).alias(table_alias)
68
+ aliased_name = "#{truncate(aliased_name)}_#{count}" if count > 1
69
+
70
+ arel_table = arel_table.alias(aliased_name)
69
71
  end
72
+
73
+ arel_table
70
74
  end
71
75
 
72
76
  attr_reader :aliases
73
77
 
74
78
  private
75
-
76
79
  def truncate(name)
77
80
  name.slice(0, @connection.table_alias_length - 2)
78
81
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/array/wrap"
4
-
5
3
  module ActiveRecord
6
4
  module Associations
7
5
  # = Active Record Associations
@@ -43,7 +41,6 @@ module ActiveRecord
43
41
  reflection.check_validity!
44
42
 
45
43
  @owner, @reflection = owner, reflection
46
- @_scope = nil
47
44
 
48
45
  reset
49
46
  reset_scope
@@ -57,6 +54,10 @@ module ActiveRecord
57
54
  @inversed = false
58
55
  end
59
56
 
57
+ def reset_negative_cache # :nodoc:
58
+ reset if loaded? && target.nil?
59
+ end
60
+
60
61
  # Reloads the \target and returns +self+ on success.
61
62
  # The QueryCache is cleared if +force+ is true.
62
63
  def reload(force = false)
@@ -96,7 +97,11 @@ module ActiveRecord
96
97
  end
97
98
 
98
99
  def scope
99
- @_scope&.spawn || target_scope.merge!(association_scope)
100
+ if (scope = klass.current_scope) && scope.try(:proxy_association) == self
101
+ scope.spawn
102
+ else
103
+ target_scope.merge!(association_scope)
104
+ end
100
105
  end
101
106
 
102
107
  def reset_scope
@@ -129,7 +134,15 @@ module ActiveRecord
129
134
  self.target = record
130
135
  @inversed = !!record
131
136
  end
132
- alias :inversed_from_queries :inversed_from
137
+
138
+ def inversed_from_queries(record)
139
+ if inversable?(record)
140
+ self.target = record
141
+ @inversed = true
142
+ else
143
+ @inversed = false
144
+ end
145
+ end
133
146
 
134
147
  # Returns the class of the target. belongs_to polymorphic overrides this to look at the
135
148
  # polymorphic_type field on the owner.
@@ -188,34 +201,36 @@ module ActiveRecord
188
201
  set_inverse_instance(record)
189
202
  end
190
203
 
191
- def create(attributes = {}, &block)
204
+ def create(attributes = nil, &block)
192
205
  _create_record(attributes, &block)
193
206
  end
194
207
 
195
- def create!(attributes = {}, &block)
208
+ def create!(attributes = nil, &block)
196
209
  _create_record(attributes, true, &block)
197
210
  end
198
211
 
199
- def scoping(relation, &block)
200
- @_scope = relation
201
- relation.scoping(&block)
202
- ensure
203
- @_scope = nil
204
- end
205
-
206
212
  private
207
213
  def find_target
214
+ if strict_loading? && owner.validation_context.nil?
215
+ Base.strict_loading_violation!(owner: owner.class, reflection: reflection)
216
+ end
217
+
208
218
  scope = self.scope
209
219
  return scope.to_a if skip_statement_cache?(scope)
210
220
 
211
- conn = klass.connection
212
- sc = reflection.association_scope_cache(conn, owner) do |params|
221
+ sc = reflection.association_scope_cache(klass, owner) do |params|
213
222
  as = AssociationScope.create { params.bind }
214
223
  target_scope.merge!(as.scope(self))
215
224
  end
216
225
 
217
226
  binds = AssociationScope.get_bind_values(owner, reflection.chain)
218
- sc.execute(binds, conn) { |record| set_inverse_instance(record) } || []
227
+ sc.execute(binds, klass.connection) { |record| set_inverse_instance(record) }
228
+ end
229
+
230
+ def strict_loading?
231
+ return reflection.strict_loading? if reflection.options.key?(:strict_loading)
232
+
233
+ owner.strict_loading?
219
234
  end
220
235
 
221
236
  # The scope for this association.
@@ -244,25 +259,6 @@ module ActiveRecord
244
259
  !loaded? && (!owner.new_record? || foreign_key_present?) && klass
245
260
  end
246
261
 
247
- def creation_attributes
248
- attributes = {}
249
-
250
- if (reflection.has_one? || reflection.collection?) && !options[:through]
251
- attributes[reflection.foreign_key] = owner[reflection.active_record_primary_key]
252
-
253
- if reflection.type
254
- attributes[reflection.type] = owner.class.polymorphic_name
255
- end
256
- end
257
-
258
- attributes
259
- end
260
-
261
- # Sets the owner attributes on the given record
262
- def set_owner_attributes(record)
263
- creation_attributes.each { |key, value| record[key] = value }
264
- end
265
-
266
262
  # Returns true if there is a foreign key present on the owner which
267
263
  # references the target. This is used to determine whether we can load
268
264
  # the target if the owner is currently a new record (and therefore
@@ -310,7 +306,7 @@ module ActiveRecord
310
306
 
311
307
  # Returns true if record contains the foreign_key
312
308
  def foreign_key_for?(record)
313
- record.has_attribute?(reflection.foreign_key)
309
+ record._has_attribute?(reflection.foreign_key)
314
310
  end
315
311
 
316
312
  # This should be implemented to return the values of the relevant key(s) on the owner,
@@ -335,6 +331,28 @@ module ActiveRecord
335
331
  klass.scope_attributes? ||
336
332
  reflection.source_reflection.active_record.default_scopes.any?
337
333
  end
334
+
335
+ def enqueue_destroy_association(options)
336
+ job_class = owner.class.destroy_association_async_job
337
+
338
+ if job_class
339
+ owner._after_commit_jobs.push([job_class, options])
340
+ end
341
+ end
342
+
343
+ def inversable?(record)
344
+ record &&
345
+ ((!record.persisted? || !owner.persisted?) || matches_foreign_key?(record))
346
+ end
347
+
348
+ def matches_foreign_key?(record)
349
+ if foreign_key_for?(record)
350
+ record.read_attribute(reflection.foreign_key) == owner.id ||
351
+ (foreign_key_for?(owner) && owner.read_attribute(reflection.foreign_key) == record.id)
352
+ else
353
+ owner.read_attribute(reflection.foreign_key) == record.id
354
+ end
355
+ end
338
356
  end
339
357
  end
340
358
  end
@@ -52,17 +52,16 @@ module ActiveRecord
52
52
  attr_reader :value_transformation
53
53
 
54
54
  def join(table, constraint)
55
- table.create_join(table, table.create_on(constraint))
55
+ Arel::Nodes::LeadingJoin.new(table, Arel::Nodes::On.new(constraint))
56
56
  end
57
57
 
58
58
  def last_chain_scope(scope, reflection, owner)
59
- join_keys = reflection.join_keys
60
- key = join_keys.key
61
- foreign_key = join_keys.foreign_key
59
+ primary_key = reflection.join_primary_key
60
+ foreign_key = reflection.join_foreign_key
62
61
 
63
62
  table = reflection.aliased_table
64
63
  value = transform_value(owner[foreign_key])
65
- scope = apply_scope(scope, table, key, value)
64
+ scope = apply_scope(scope, table, primary_key, value)
66
65
 
67
66
  if reflection.type
68
67
  polymorphic_type = transform_value(owner.class.polymorphic_name)
@@ -77,13 +76,12 @@ module ActiveRecord
77
76
  end
78
77
 
79
78
  def next_chain_scope(scope, reflection, next_reflection)
80
- join_keys = reflection.join_keys
81
- key = join_keys.key
82
- foreign_key = join_keys.foreign_key
79
+ primary_key = reflection.join_primary_key
80
+ foreign_key = reflection.join_foreign_key
83
81
 
84
82
  table = reflection.aliased_table
85
83
  foreign_table = next_reflection.aliased_table
86
- constraint = table[key].eq(foreign_table[foreign_key])
84
+ constraint = table[primary_key].eq(foreign_table[foreign_key])
87
85
 
88
86
  if reflection.type
89
87
  value = transform_value(next_reflection.klass.polymorphic_name)
@@ -108,11 +106,9 @@ module ActiveRecord
108
106
  name = reflection.name
109
107
  chain = [Reflection::RuntimeReflection.new(reflection, association)]
110
108
  reflection.chain.drop(1).each do |refl|
111
- aliased_table = tracker.aliased_table_for(
112
- refl.table_name,
113
- refl.alias_candidate(name),
114
- refl.klass.type_caster
115
- )
109
+ aliased_table = tracker.aliased_table_for(refl.klass.arel_table) do
110
+ refl.alias_candidate(name)
111
+ end
116
112
  chain << ReflectionProxy.new(refl, aliased_table)
117
113
  end
118
114
  chain
@@ -134,10 +130,18 @@ module ActiveRecord
134
130
 
135
131
  if scope_chain_item == chain_head.scope
136
132
  scope.merge! item.except(:where, :includes, :unscope, :order)
133
+ elsif !item.references_values.empty?
134
+ scope.merge! item.only(:joins, :left_outer_joins)
135
+
136
+ associations = item.eager_load_values | item.includes_values
137
+
138
+ unless associations.empty?
139
+ scope.joins! item.construct_join_dependency(associations, Arel::Nodes::OuterJoin)
140
+ end
137
141
  end
138
142
 
139
143
  reflection.all_includes do
140
- scope.includes! item.includes_values
144
+ scope.includes_values |= item.includes_values
141
145
  end
142
146
 
143
147
  scope.unscope!(*item.unscope_values)
@@ -9,10 +9,21 @@ module ActiveRecord
9
9
 
10
10
  case options[:dependent]
11
11
  when :destroy
12
- target.destroy
13
- raise ActiveRecord::Rollback unless target.destroyed?
12
+ raise ActiveRecord::Rollback unless target.destroy
13
+ when :destroy_async
14
+ id = owner.public_send(reflection.foreign_key.to_sym)
15
+ primary_key_column = reflection.active_record_primary_key.to_sym
16
+
17
+ enqueue_destroy_association(
18
+ owner_model_name: owner.class.to_s,
19
+ owner_id: owner.id,
20
+ association_class: reflection.klass.to_s,
21
+ association_ids: [id],
22
+ association_primary_key_column: primary_key_column,
23
+ ensuring_owner_was_method: options.fetch(:ensuring_owner_was, nil)
24
+ )
14
25
  else
15
- target.send(options[:dependent])
26
+ target.public_send(options[:dependent])
16
27
  end
17
28
  end
18
29
 
@@ -44,7 +55,7 @@ module ActiveRecord
44
55
 
45
56
  def decrement_counters_before_last_save
46
57
  if reflection.polymorphic?
47
- model_was = owner.attribute_before_last_save(reflection.foreign_type).try(:constantize)
58
+ model_was = owner.attribute_before_last_save(reflection.foreign_type)&.constantize
48
59
  else
49
60
  model_was = klass
50
61
  end
@@ -68,7 +79,7 @@ module ActiveRecord
68
79
  @updated = true
69
80
  end
70
81
 
71
- replace_keys(record)
82
+ replace_keys(record, force: true)
72
83
 
73
84
  self.target = record
74
85
  end
@@ -96,8 +107,12 @@ module ActiveRecord
96
107
  reflection.counter_cache_column && owner.persisted?
97
108
  end
98
109
 
99
- def replace_keys(record)
100
- owner[reflection.foreign_key] = record ? record._read_attribute(primary_key(record.class)) : nil
110
+ def replace_keys(record, force: false)
111
+ target_key = record ? record._read_attribute(primary_key(record.class)) : nil
112
+
113
+ if force || owner[reflection.foreign_key] != target_key
114
+ owner[reflection.foreign_key] = target_key
115
+ end
101
116
  end
102
117
 
103
118
  def primary_key(klass)
@@ -108,11 +123,9 @@ module ActiveRecord
108
123
  owner._read_attribute(reflection.foreign_key)
109
124
  end
110
125
 
111
- # NOTE - for now, we're only supporting inverse setting from belongs_to back onto
112
- # has_one associations.
113
126
  def invertible_for?(record)
114
127
  inverse = inverse_reflection_for(record)
115
- inverse && inverse.has_one?
128
+ inverse && (inverse.has_one? || ActiveRecord::Base.has_many_inversing)
116
129
  end
117
130
 
118
131
  def stale_state
@@ -6,7 +6,7 @@ module ActiveRecord
6
6
  class BelongsToPolymorphicAssociation < BelongsToAssociation #:nodoc:
7
7
  def klass
8
8
  type = owner[reflection.foreign_type]
9
- type.presence && type.constantize
9
+ type.presence && owner.class.polymorphic_class_for(type)
10
10
  end
11
11
 
12
12
  def target_changed?
@@ -14,9 +14,14 @@ module ActiveRecord
14
14
  end
15
15
 
16
16
  private
17
- def replace_keys(record)
17
+ def replace_keys(record, force: false)
18
18
  super
19
- owner[reflection.foreign_type] = record ? record.class.polymorphic_name : nil
19
+
20
+ target_type = record ? record.class.polymorphic_name : nil
21
+
22
+ if force || owner[reflection.foreign_type] != target_type
23
+ owner[reflection.foreign_type] = target_type
24
+ end
20
25
  end
21
26
 
22
27
  def inverse_reflection_for(record)
@@ -18,7 +18,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
18
18
  end
19
19
  self.extensions = []
20
20
 
21
- VALID_OPTIONS = [:class_name, :anonymous_class, :foreign_key, :validate] # :nodoc:
21
+ VALID_OPTIONS = [
22
+ :class_name, :anonymous_class, :primary_key, :foreign_key, :dependent, :validate, :inverse_of, :strict_loading
23
+ ].freeze # :nodoc:
22
24
 
23
25
  def self.build(model, name, scope, options, &block)
24
26
  if model.dangerous_attribute_method?(name)
@@ -72,8 +74,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
72
74
 
73
75
  def self.define_callbacks(model, reflection)
74
76
  if dependent = reflection.options[:dependent]
75
- check_dependent_options(dependent)
77
+ check_dependent_options(dependent, model)
76
78
  add_destroy_callbacks(model, reflection)
79
+ add_after_commit_jobs_callback(model, dependent)
77
80
  end
78
81
 
79
82
  Association.extensions.each do |extension|
@@ -118,7 +121,11 @@ module ActiveRecord::Associations::Builder # :nodoc:
118
121
  raise NotImplementedError
119
122
  end
120
123
 
121
- def self.check_dependent_options(dependent)
124
+ def self.check_dependent_options(dependent, model)
125
+ if dependent == :destroy_async && !model.destroy_association_async_job
126
+ err_message = "ActiveJob is required to use destroy_async on associations"
127
+ raise ActiveRecord::ActiveJobRequiredError, err_message
128
+ end
122
129
  unless valid_dependent_options.include? dependent
123
130
  raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{dependent}"
124
131
  end
@@ -126,11 +133,31 @@ module ActiveRecord::Associations::Builder # :nodoc:
126
133
 
127
134
  def self.add_destroy_callbacks(model, reflection)
128
135
  name = reflection.name
129
- model.before_destroy lambda { |o| o.association(name).handle_dependency }
136
+ model.before_destroy(->(o) { o.association(name).handle_dependency })
137
+ end
138
+
139
+ def self.add_after_commit_jobs_callback(model, dependent)
140
+ if dependent == :destroy_async
141
+ mixin = model.generated_association_methods
142
+
143
+ unless mixin.method_defined?(:_after_commit_jobs)
144
+ model.after_commit(-> do
145
+ _after_commit_jobs.each do |job_class, job_arguments|
146
+ job_class.perform_later(**job_arguments)
147
+ end
148
+ end)
149
+
150
+ mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
151
+ def _after_commit_jobs
152
+ @_after_commit_jobs ||= []
153
+ end
154
+ CODE
155
+ end
156
+ end
130
157
  end
131
158
 
132
159
  private_class_method :build_scope, :macro, :valid_options, :validate_options, :define_extensions,
133
160
  :define_callbacks, :define_accessors, :define_readers, :define_writers, :define_validations,
134
- :valid_dependent_options, :check_dependent_options, :add_destroy_callbacks
161
+ :valid_dependent_options, :check_dependent_options, :add_destroy_callbacks, :add_after_commit_jobs_callback
135
162
  end
136
163
  end
@@ -7,11 +7,14 @@ module ActiveRecord::Associations::Builder # :nodoc:
7
7
  end
8
8
 
9
9
  def self.valid_options(options)
10
- super + [:polymorphic, :touch, :counter_cache, :optional, :default]
10
+ valid = super + [:polymorphic, :counter_cache, :optional, :default]
11
+ valid += [:foreign_type] if options[:polymorphic]
12
+ valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
13
+ valid
11
14
  end
12
15
 
13
16
  def self.valid_dependent_options
14
- [:destroy, :delete]
17
+ [:destroy, :delete, :destroy_async]
15
18
  end
16
19
 
17
20
  def self.define_callbacks(model, reflection)
@@ -55,19 +58,19 @@ module ActiveRecord::Associations::Builder # :nodoc:
55
58
 
56
59
  if old_record
57
60
  if touch != true
58
- old_record.send(touch_method, touch)
61
+ old_record.public_send(touch_method, touch)
59
62
  else
60
- old_record.send(touch_method)
63
+ old_record.public_send(touch_method)
61
64
  end
62
65
  end
63
66
  end
64
67
 
65
- record = o.send name
68
+ record = o.public_send name
66
69
  if record && record.persisted?
67
70
  if touch != true
68
- record.send(touch_method, touch)
71
+ record.public_send(touch_method, touch)
69
72
  else
70
- record.send(touch_method)
73
+ record.public_send(touch_method)
71
74
  end
72
75
  end
73
76
  end