activerecord 6.0.1 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (268) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +843 -626
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -3
  5. data/lib/active_record/aggregations.rb +1 -2
  6. data/lib/active_record/association_relation.rb +18 -17
  7. data/lib/active_record/associations/alias_tracker.rb +19 -16
  8. data/lib/active_record/associations/association.rb +49 -37
  9. data/lib/active_record/associations/association_scope.rb +17 -15
  10. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  12. data/lib/active_record/associations/builder/association.rb +9 -3
  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 +25 -8
  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 +36 -14
  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 +51 -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 +115 -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 +32 -8
  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 +1 -2
  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 +82 -37
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -8
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  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 +137 -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 +263 -107
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +82 -35
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +60 -73
  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 +31 -0
  65. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/database_statements.rb +28 -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 +1 -1
  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 +1 -1
  72. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -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 +63 -0
  76. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  77. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  78. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -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/oid.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  90. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
  91. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
  93. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  94. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  95. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  96. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
  97. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
  98. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  99. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
  100. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  101. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  102. data/lib/active_record/connection_adapters/postgresql_adapter.rb +77 -57
  103. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  104. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  105. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +36 -12
  106. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
  107. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  108. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
  109. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +57 -57
  110. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  111. data/lib/active_record/connection_adapters.rb +50 -0
  112. data/lib/active_record/connection_handling.rb +210 -87
  113. data/lib/active_record/core.rb +229 -65
  114. data/lib/active_record/counter_cache.rb +4 -1
  115. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  116. data/lib/active_record/database_configurations/database_config.rb +52 -9
  117. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  118. data/lib/active_record/database_configurations/url_config.rb +15 -41
  119. data/lib/active_record/database_configurations.rb +124 -85
  120. data/lib/active_record/delegated_type.rb +209 -0
  121. data/lib/active_record/destroy_association_async_job.rb +36 -0
  122. data/lib/active_record/dynamic_matchers.rb +2 -3
  123. data/lib/active_record/enum.rb +40 -16
  124. data/lib/active_record/errors.rb +47 -12
  125. data/lib/active_record/explain.rb +9 -5
  126. data/lib/active_record/explain_subscriber.rb +1 -1
  127. data/lib/active_record/fixture_set/file.rb +10 -17
  128. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  129. data/lib/active_record/fixture_set/render_context.rb +1 -1
  130. data/lib/active_record/fixture_set/table_row.rb +2 -3
  131. data/lib/active_record/fixture_set/table_rows.rb +0 -1
  132. data/lib/active_record/fixtures.rb +54 -11
  133. data/lib/active_record/gem_version.rb +2 -2
  134. data/lib/active_record/inheritance.rb +40 -21
  135. data/lib/active_record/insert_all.rb +38 -9
  136. data/lib/active_record/integration.rb +3 -5
  137. data/lib/active_record/internal_metadata.rb +16 -7
  138. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  139. data/lib/active_record/locking/optimistic.rb +22 -17
  140. data/lib/active_record/locking/pessimistic.rb +6 -2
  141. data/lib/active_record/log_subscriber.rb +27 -9
  142. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  143. data/lib/active_record/middleware/database_selector/resolver.rb +6 -2
  144. data/lib/active_record/middleware/database_selector.rb +4 -2
  145. data/lib/active_record/migration/command_recorder.rb +53 -45
  146. data/lib/active_record/migration/compatibility.rb +70 -20
  147. data/lib/active_record/migration/join_table.rb +0 -1
  148. data/lib/active_record/migration.rb +114 -84
  149. data/lib/active_record/model_schema.rb +117 -15
  150. data/lib/active_record/nested_attributes.rb +2 -5
  151. data/lib/active_record/no_touching.rb +1 -1
  152. data/lib/active_record/null_relation.rb +0 -1
  153. data/lib/active_record/persistence.rb +50 -46
  154. data/lib/active_record/query_cache.rb +15 -5
  155. data/lib/active_record/querying.rb +12 -7
  156. data/lib/active_record/railtie.rb +65 -45
  157. data/lib/active_record/railties/databases.rake +267 -93
  158. data/lib/active_record/readonly_attributes.rb +4 -0
  159. data/lib/active_record/reflection.rb +77 -63
  160. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  161. data/lib/active_record/relation/batches.rb +38 -32
  162. data/lib/active_record/relation/calculations.rb +102 -45
  163. data/lib/active_record/relation/delegation.rb +9 -7
  164. data/lib/active_record/relation/finder_methods.rb +45 -16
  165. data/lib/active_record/relation/from_clause.rb +5 -1
  166. data/lib/active_record/relation/merger.rb +27 -26
  167. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  168. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  169. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
  170. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  171. data/lib/active_record/relation/predicate_builder.rb +55 -35
  172. data/lib/active_record/relation/query_methods.rb +335 -187
  173. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  174. data/lib/active_record/relation/spawn_methods.rb +8 -8
  175. data/lib/active_record/relation/where_clause.rb +104 -58
  176. data/lib/active_record/relation.rb +108 -68
  177. data/lib/active_record/result.rb +41 -34
  178. data/lib/active_record/runtime_registry.rb +2 -2
  179. data/lib/active_record/sanitization.rb +6 -17
  180. data/lib/active_record/schema_dumper.rb +34 -4
  181. data/lib/active_record/schema_migration.rb +2 -8
  182. data/lib/active_record/scoping/default.rb +0 -1
  183. data/lib/active_record/scoping/named.rb +7 -18
  184. data/lib/active_record/scoping.rb +0 -1
  185. data/lib/active_record/secure_token.rb +16 -8
  186. data/lib/active_record/serialization.rb +5 -3
  187. data/lib/active_record/signed_id.rb +116 -0
  188. data/lib/active_record/statement_cache.rb +20 -4
  189. data/lib/active_record/store.rb +3 -3
  190. data/lib/active_record/suppressor.rb +2 -2
  191. data/lib/active_record/table_metadata.rb +39 -36
  192. data/lib/active_record/tasks/database_tasks.rb +139 -113
  193. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
  194. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
  195. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
  196. data/lib/active_record/test_databases.rb +5 -4
  197. data/lib/active_record/test_fixtures.rb +38 -16
  198. data/lib/active_record/timestamp.rb +4 -7
  199. data/lib/active_record/touch_later.rb +20 -21
  200. data/lib/active_record/transactions.rb +21 -70
  201. data/lib/active_record/type/adapter_specific_registry.rb +2 -5
  202. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  203. data/lib/active_record/type/serialized.rb +6 -3
  204. data/lib/active_record/type/time.rb +10 -0
  205. data/lib/active_record/type/type_map.rb +0 -1
  206. data/lib/active_record/type/unsigned_integer.rb +0 -1
  207. data/lib/active_record/type.rb +8 -2
  208. data/lib/active_record/type_caster/connection.rb +0 -1
  209. data/lib/active_record/type_caster/map.rb +8 -5
  210. data/lib/active_record/validations/associated.rb +1 -2
  211. data/lib/active_record/validations/numericality.rb +35 -0
  212. data/lib/active_record/validations/uniqueness.rb +24 -4
  213. data/lib/active_record/validations.rb +3 -3
  214. data/lib/active_record.rb +7 -13
  215. data/lib/arel/attributes/attribute.rb +4 -0
  216. data/lib/arel/collectors/bind.rb +5 -0
  217. data/lib/arel/collectors/composite.rb +8 -0
  218. data/lib/arel/collectors/sql_string.rb +7 -0
  219. data/lib/arel/collectors/substitute_binds.rb +7 -0
  220. data/lib/arel/nodes/binary.rb +82 -8
  221. data/lib/arel/nodes/bind_param.rb +8 -0
  222. data/lib/arel/nodes/casted.rb +21 -9
  223. data/lib/arel/nodes/equality.rb +6 -9
  224. data/lib/arel/nodes/grouping.rb +3 -0
  225. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  226. data/lib/arel/nodes/in.rb +8 -1
  227. data/lib/arel/nodes/infix_operation.rb +13 -1
  228. data/lib/arel/nodes/join_source.rb +1 -1
  229. data/lib/arel/nodes/node.rb +7 -6
  230. data/lib/arel/nodes/ordering.rb +27 -0
  231. data/lib/arel/nodes/sql_literal.rb +3 -0
  232. data/lib/arel/nodes/table_alias.rb +7 -3
  233. data/lib/arel/nodes/unary.rb +0 -1
  234. data/lib/arel/nodes.rb +3 -1
  235. data/lib/arel/predications.rb +17 -24
  236. data/lib/arel/select_manager.rb +1 -2
  237. data/lib/arel/table.rb +13 -5
  238. data/lib/arel/visitors/dot.rb +14 -3
  239. data/lib/arel/visitors/mysql.rb +11 -1
  240. data/lib/arel/visitors/postgresql.rb +15 -5
  241. data/lib/arel/visitors/sqlite.rb +0 -1
  242. data/lib/arel/visitors/to_sql.rb +89 -79
  243. data/lib/arel/visitors/visitor.rb +0 -1
  244. data/lib/arel/visitors.rb +0 -7
  245. data/lib/arel.rb +5 -9
  246. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  247. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  248. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  249. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  250. data/lib/rails/generators/active_record/migration.rb +6 -2
  251. data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
  252. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  253. metadata +26 -26
  254. data/lib/active_record/attribute_decorators.rb +0 -90
  255. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  256. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  257. data/lib/active_record/define_callbacks.rb +0 -22
  258. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  259. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  260. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  261. data/lib/arel/attributes.rb +0 -22
  262. data/lib/arel/visitors/depth_first.rb +0 -204
  263. data/lib/arel/visitors/ibm_db.rb +0 -34
  264. data/lib/arel/visitors/informix.rb +0 -62
  265. data/lib/arel/visitors/mssql.rb +0 -157
  266. data/lib/arel/visitors/oracle.rb +0 -159
  267. data/lib/arel/visitors/oracle12.rb +0 -66
  268. 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-2020 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
@@ -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
@@ -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,30 @@ module ActiveRecord
15
15
  other == records
16
16
  end
17
17
 
18
- def build(attributes = nil, &block)
19
- block = _deprecated_scope_block("new", &block)
20
- @association.scoping(self) do
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
+
30
+ private
31
+ def _new(attributes, &block)
21
32
  @association.build(attributes, &block)
22
33
  end
23
- end
24
- alias new build
25
34
 
26
- def create(attributes = nil, &block)
27
- block = _deprecated_scope_block("create", &block)
28
- @association.scoping(self) do
35
+ def _create(attributes, &block)
29
36
  @association.create(attributes, &block)
30
37
  end
31
- end
32
38
 
33
- def create!(attributes = nil, &block)
34
- block = _deprecated_scope_block("create!", &block)
35
- @association.scoping(self) do
39
+ def _create!(attributes, &block)
36
40
  @association.create!(attributes, &block)
37
41
  end
38
- end
39
-
40
- private
41
42
 
42
43
  def exec_queries
43
44
  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,34 @@ 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 owner.strict_loading? && owner.validation_context.nil?
215
+ Base.strict_loading_violation!(owner: owner.class, association: klass)
216
+ end
217
+
218
+ if reflection.strict_loading? && owner.validation_context.nil?
219
+ Base.strict_loading_violation!(owner: owner.class, association: reflection.name)
220
+ end
221
+
208
222
  scope = self.scope
209
223
  return scope.to_a if skip_statement_cache?(scope)
210
224
 
211
- conn = klass.connection
212
- sc = reflection.association_scope_cache(conn, owner) do |params|
225
+ sc = reflection.association_scope_cache(klass, owner) do |params|
213
226
  as = AssociationScope.create { params.bind }
214
227
  target_scope.merge!(as.scope(self))
215
228
  end
216
229
 
217
230
  binds = AssociationScope.get_bind_values(owner, reflection.chain)
218
- sc.execute(binds, conn) { |record| set_inverse_instance(record) } || []
231
+ sc.execute(binds, klass.connection) { |record| set_inverse_instance(record) }
219
232
  end
220
233
 
221
234
  # The scope for this association.
@@ -244,25 +257,6 @@ module ActiveRecord
244
257
  !loaded? && (!owner.new_record? || foreign_key_present?) && klass
245
258
  end
246
259
 
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
260
  # Returns true if there is a foreign key present on the owner which
267
261
  # references the target. This is used to determine whether we can load
268
262
  # the target if the owner is currently a new record (and therefore
@@ -310,7 +304,7 @@ module ActiveRecord
310
304
 
311
305
  # Returns true if record contains the foreign_key
312
306
  def foreign_key_for?(record)
313
- record.has_attribute?(reflection.foreign_key)
307
+ record._has_attribute?(reflection.foreign_key)
314
308
  end
315
309
 
316
310
  # This should be implemented to return the values of the relevant key(s) on the owner,
@@ -335,6 +329,24 @@ module ActiveRecord
335
329
  klass.scope_attributes? ||
336
330
  reflection.source_reflection.active_record.default_scopes.any?
337
331
  end
332
+
333
+ def enqueue_destroy_association(options)
334
+ owner.class.destroy_association_async_job&.perform_later(**options)
335
+ end
336
+
337
+ def inversable?(record)
338
+ record &&
339
+ ((!record.persisted? || !owner.persisted?) || matches_foreign_key?(record))
340
+ end
341
+
342
+ def matches_foreign_key?(record)
343
+ if foreign_key_for?(record)
344
+ record.read_attribute(reflection.foreign_key) == owner.id ||
345
+ (foreign_key_for?(owner) && owner.read_attribute(reflection.foreign_key) == record.id)
346
+ else
347
+ owner.read_attribute(reflection.foreign_key) == record.id
348
+ end
349
+ end
338
350
  end
339
351
  end
340
352
  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,16 @@ 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
+ join_dependency = item.construct_join_dependency(
135
+ item.eager_load_values | item.includes_values, Arel::Nodes::OuterJoin
136
+ )
137
+ scope.joins!(*item.joins_values, join_dependency)
138
+ scope.left_outer_joins!(*item.left_outer_joins_values)
137
139
  end
138
140
 
139
141
  reflection.all_includes do
140
- scope.includes! item.includes_values
142
+ scope.includes_values |= item.includes_values
141
143
  end
142
144
 
143
145
  scope.unscope!(*item.unscope_values)
@@ -11,8 +11,20 @@ module ActiveRecord
11
11
  when :destroy
12
12
  target.destroy
13
13
  raise ActiveRecord::Rollback unless target.destroyed?
14
+ when :destroy_async
15
+ id = owner.public_send(reflection.foreign_key.to_sym)
16
+ primary_key_column = reflection.active_record_primary_key.to_sym
17
+
18
+ enqueue_destroy_association(
19
+ owner_model_name: owner.class.to_s,
20
+ owner_id: owner.id,
21
+ association_class: reflection.klass.to_s,
22
+ association_ids: [id],
23
+ association_primary_key_column: primary_key_column,
24
+ ensuring_owner_was_method: options.fetch(:ensuring_owner_was, nil)
25
+ )
14
26
  else
15
- target.send(options[:dependent])
27
+ target.public_send(options[:dependent])
16
28
  end
17
29
  end
18
30
 
@@ -44,7 +56,7 @@ module ActiveRecord
44
56
 
45
57
  def decrement_counters_before_last_save
46
58
  if reflection.polymorphic?
47
- model_was = owner.attribute_before_last_save(reflection.foreign_type).try(:constantize)
59
+ model_was = owner.attribute_before_last_save(reflection.foreign_type)&.constantize
48
60
  else
49
61
  model_was = klass
50
62
  end
@@ -108,11 +120,9 @@ module ActiveRecord
108
120
  owner._read_attribute(reflection.foreign_key)
109
121
  end
110
122
 
111
- # NOTE - for now, we're only supporting inverse setting from belongs_to back onto
112
- # has_one associations.
113
123
  def invertible_for?(record)
114
124
  inverse = inverse_reflection_for(record)
115
- inverse && inverse.has_one?
125
+ inverse && (inverse.has_one? || ActiveRecord::Base.has_many_inversing)
116
126
  end
117
127
 
118
128
  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?
@@ -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,7 +74,7 @@ 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)
77
79
  end
78
80
 
@@ -118,7 +120,11 @@ module ActiveRecord::Associations::Builder # :nodoc:
118
120
  raise NotImplementedError
119
121
  end
120
122
 
121
- def self.check_dependent_options(dependent)
123
+ def self.check_dependent_options(dependent, model)
124
+ if dependent == :destroy_async && !model.destroy_association_async_job
125
+ err_message = "ActiveJob is required to use destroy_async on associations"
126
+ raise ActiveRecord::ActiveJobRequiredError, err_message
127
+ end
122
128
  unless valid_dependent_options.include? dependent
123
129
  raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{dependent}"
124
130
  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 + [:counter_cache, :optional, :default]
11
+ valid += [:polymorphic, :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
@@ -7,8 +7,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
7
7
  CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
8
8
 
9
9
  def self.valid_options(options)
10
- super + [:table_name, :before_add,
11
- :after_add, :before_remove, :after_remove, :extend]
10
+ super + [:before_add, :after_add, :before_remove, :after_remove, :extend]
12
11
  end
13
12
 
14
13
  def self.define_callbacks(model, reflection)
@@ -31,8 +30,10 @@ module ActiveRecord::Associations::Builder # :nodoc:
31
30
  def self.define_callback(model, callback_name, name, options)
32
31
  full_callback_name = "#{callback_name}_for_#{name}"
33
32
 
34
- # TODO : why do i need method_defined? I think its because of the inheritance chain
35
- model.class_attribute full_callback_name unless model.method_defined?(full_callback_name)
33
+ unless model.method_defined?(full_callback_name)
34
+ model.class_attribute(full_callback_name, instance_accessor: false, instance_predicate: false)
35
+ end
36
+
36
37
  callbacks = Array(options[callback_name.to_sym]).map do |callback|
37
38
  case callback
38
39
  when Symbol
@@ -46,7 +46,6 @@ module ActiveRecord::Associations::Builder # :nodoc:
46
46
  end
47
47
 
48
48
  private
49
-
50
49
  def self.suppress_composite_primary_key(pk)
51
50
  pk unless pk.is_a?(Array)
52
51
  end
@@ -73,11 +72,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
73
72
  end
74
73
 
75
74
  private
76
-
77
75
  def middle_options(join_model)
78
76
  middle_options = {}
79
77
  middle_options[:class_name] = "#{lhs_model.name}::#{join_model.name}"
80
- middle_options[:source] = join_model.left_reflection.name
81
78
  if options.key? :foreign_key
82
79
  middle_options[:foreign_key] = options[:foreign_key]
83
80
  end
@@ -7,11 +7,15 @@ module ActiveRecord::Associations::Builder # :nodoc:
7
7
  end
8
8
 
9
9
  def self.valid_options(options)
10
- super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache, :join_table, :foreign_type, :index_errors]
10
+ valid = super + [:counter_cache, :join_table, :index_errors, :ensuring_owner_was]
11
+ valid += [:as, :foreign_type] if options[:as]
12
+ valid += [:through, :source, :source_type] if options[:through]
13
+ valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
14
+ valid
11
15
  end
12
16
 
13
17
  def self.valid_dependent_options
14
- [:destroy, :delete_all, :nullify, :restrict_with_error, :restrict_with_exception]
18
+ [:destroy, :delete_all, :nullify, :restrict_with_error, :restrict_with_exception, :destroy_async]
15
19
  end
16
20
 
17
21
  private_class_method :macro, :valid_options, :valid_dependent_options
@@ -7,13 +7,15 @@ module ActiveRecord::Associations::Builder # :nodoc:
7
7
  end
8
8
 
9
9
  def self.valid_options(options)
10
- valid = super + [:as, :touch]
10
+ valid = super
11
+ valid += [:as, :foreign_type] if options[:as]
12
+ valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
11
13
  valid += [:through, :source, :source_type] if options[:through]
12
14
  valid
13
15
  end
14
16
 
15
17
  def self.valid_dependent_options
16
- [:destroy, :delete, :nullify, :restrict_with_error, :restrict_with_exception]
18
+ [:destroy, :destroy_async, :delete, :nullify, :restrict_with_error, :restrict_with_exception]
17
19
  end
18
20
 
19
21
  def self.define_callbacks(model, reflection)
@@ -32,15 +34,12 @@ module ActiveRecord::Associations::Builder # :nodoc:
32
34
  end
33
35
  end
34
36
 
35
- def self.touch_record(o, name, touch)
36
- record = o.send name
37
+ def self.touch_record(record, name, touch)
38
+ instance = record.send(name)
37
39
 
38
- return unless record && record.persisted?
39
-
40
- if touch != true
41
- record.touch(touch)
42
- else
43
- record.touch
40
+ if instance&.persisted?
41
+ touch != true ?
42
+ instance.touch(touch) : instance.touch
44
43
  end
45
44
  end
46
45
 
@@ -48,11 +47,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
48
47
  name = reflection.name
49
48
  touch = reflection.options[:touch]
50
49
 
51
- callback = lambda { |record|
52
- HasOne.touch_record(record, name, touch)
53
- }
54
-
50
+ callback = -> (record) { HasOne.touch_record(record, name, touch) }
55
51
  model.after_create callback, if: :saved_changes?
52
+ model.after_create_commit { association(name).reset_negative_cache }
56
53
  model.after_update callback, if: :saved_changes?
57
54
  model.after_destroy callback
58
55
  model.after_touch callback
@@ -5,7 +5,7 @@
5
5
  module ActiveRecord::Associations::Builder # :nodoc:
6
6
  class SingularAssociation < Association #:nodoc:
7
7
  def self.valid_options(options)
8
- super + [:foreign_type, :dependent, :primary_key, :inverse_of, :required]
8
+ super + [:required, :touch]
9
9
  end
10
10
 
11
11
  def self.define_accessors(model, reflection)