activerecord 6.0.3.4 → 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 (244) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +799 -713
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -2
  5. data/lib/active_record.rb +7 -14
  6. data/lib/active_record/aggregations.rb +1 -1
  7. data/lib/active_record/association_relation.rb +22 -14
  8. data/lib/active_record/associations.rb +114 -11
  9. data/lib/active_record/associations/alias_tracker.rb +19 -15
  10. data/lib/active_record/associations/association.rb +44 -28
  11. data/lib/active_record/associations/association_scope.rb +17 -15
  12. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  14. data/lib/active_record/associations/builder/association.rb +9 -3
  15. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  16. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
  18. data/lib/active_record/associations/builder/has_many.rb +6 -2
  19. data/lib/active_record/associations/builder/has_one.rb +11 -14
  20. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  21. data/lib/active_record/associations/collection_association.rb +19 -6
  22. data/lib/active_record/associations/collection_proxy.rb +13 -5
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +24 -2
  25. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  26. data/lib/active_record/associations/has_one_association.rb +15 -1
  27. data/lib/active_record/associations/join_dependency.rb +72 -50
  28. data/lib/active_record/associations/join_dependency/join_association.rb +36 -14
  29. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  30. data/lib/active_record/associations/preloader.rb +11 -5
  31. data/lib/active_record/associations/preloader/association.rb +51 -25
  32. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  33. data/lib/active_record/associations/singular_association.rb +1 -1
  34. data/lib/active_record/associations/through_association.rb +1 -1
  35. data/lib/active_record/attribute_assignment.rb +10 -8
  36. data/lib/active_record/attribute_methods.rb +64 -54
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
  38. data/lib/active_record/attribute_methods/dirty.rb +1 -11
  39. data/lib/active_record/attribute_methods/primary_key.rb +6 -2
  40. data/lib/active_record/attribute_methods/query.rb +3 -6
  41. data/lib/active_record/attribute_methods/read.rb +8 -11
  42. data/lib/active_record/attribute_methods/serialization.rb +11 -5
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
  44. data/lib/active_record/attribute_methods/write.rb +12 -20
  45. data/lib/active_record/attributes.rb +32 -7
  46. data/lib/active_record/autosave_association.rb +57 -40
  47. data/lib/active_record/base.rb +2 -14
  48. data/lib/active_record/callbacks.rb +152 -22
  49. data/lib/active_record/coders/yaml_column.rb +1 -1
  50. data/lib/active_record/connection_adapters.rb +50 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +191 -134
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -22
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -7
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +112 -27
  59. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  60. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +224 -85
  61. data/lib/active_record/connection_adapters/abstract/transaction.rb +80 -32
  62. data/lib/active_record/connection_adapters/abstract_adapter.rb +54 -71
  63. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +129 -88
  64. data/lib/active_record/connection_adapters/column.rb +15 -1
  65. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  66. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  67. data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -25
  68. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
  69. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  70. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -6
  71. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  72. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  73. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +11 -7
  74. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  75. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
  76. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  77. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  78. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  79. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +13 -54
  80. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  82. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  83. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  84. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -5
  89. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
  90. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  91. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  92. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
  93. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
  94. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  95. data/lib/active_record/connection_adapters/postgresql_adapter.rb +72 -55
  96. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  97. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  98. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +31 -6
  99. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
  100. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  101. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +37 -4
  102. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +49 -50
  103. data/lib/active_record/connection_handling.rb +210 -71
  104. data/lib/active_record/core.rb +229 -63
  105. data/lib/active_record/database_configurations.rb +124 -85
  106. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  107. data/lib/active_record/database_configurations/database_config.rb +52 -9
  108. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  109. data/lib/active_record/database_configurations/url_config.rb +15 -40
  110. data/lib/active_record/delegated_type.rb +209 -0
  111. data/lib/active_record/destroy_association_async_job.rb +36 -0
  112. data/lib/active_record/enum.rb +40 -16
  113. data/lib/active_record/errors.rb +47 -12
  114. data/lib/active_record/explain.rb +9 -4
  115. data/lib/active_record/explain_subscriber.rb +1 -1
  116. data/lib/active_record/fixture_set/file.rb +10 -17
  117. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  118. data/lib/active_record/fixture_set/render_context.rb +1 -1
  119. data/lib/active_record/fixture_set/table_row.rb +2 -2
  120. data/lib/active_record/fixtures.rb +54 -8
  121. data/lib/active_record/gem_version.rb +3 -3
  122. data/lib/active_record/inheritance.rb +40 -18
  123. data/lib/active_record/insert_all.rb +35 -6
  124. data/lib/active_record/integration.rb +3 -5
  125. data/lib/active_record/internal_metadata.rb +16 -7
  126. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  127. data/lib/active_record/locking/optimistic.rb +22 -16
  128. data/lib/active_record/locking/pessimistic.rb +6 -2
  129. data/lib/active_record/log_subscriber.rb +26 -8
  130. data/lib/active_record/middleware/database_selector.rb +4 -1
  131. data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
  132. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  133. data/lib/active_record/migration.rb +113 -83
  134. data/lib/active_record/migration/command_recorder.rb +47 -27
  135. data/lib/active_record/migration/compatibility.rb +67 -17
  136. data/lib/active_record/model_schema.rb +117 -13
  137. data/lib/active_record/nested_attributes.rb +2 -3
  138. data/lib/active_record/no_touching.rb +1 -1
  139. data/lib/active_record/persistence.rb +50 -45
  140. data/lib/active_record/query_cache.rb +15 -5
  141. data/lib/active_record/querying.rb +11 -6
  142. data/lib/active_record/railtie.rb +64 -44
  143. data/lib/active_record/railties/databases.rake +266 -95
  144. data/lib/active_record/readonly_attributes.rb +4 -0
  145. data/lib/active_record/reflection.rb +71 -57
  146. data/lib/active_record/relation.rb +96 -67
  147. data/lib/active_record/relation/batches.rb +38 -31
  148. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  149. data/lib/active_record/relation/calculations.rb +101 -44
  150. data/lib/active_record/relation/delegation.rb +2 -1
  151. data/lib/active_record/relation/finder_methods.rb +45 -15
  152. data/lib/active_record/relation/from_clause.rb +1 -1
  153. data/lib/active_record/relation/merger.rb +27 -25
  154. data/lib/active_record/relation/predicate_builder.rb +57 -33
  155. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  156. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  157. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
  158. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  159. data/lib/active_record/relation/query_methods.rb +330 -195
  160. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  161. data/lib/active_record/relation/spawn_methods.rb +8 -7
  162. data/lib/active_record/relation/where_clause.rb +104 -57
  163. data/lib/active_record/result.rb +41 -33
  164. data/lib/active_record/runtime_registry.rb +2 -2
  165. data/lib/active_record/sanitization.rb +6 -17
  166. data/lib/active_record/schema_dumper.rb +34 -4
  167. data/lib/active_record/schema_migration.rb +2 -8
  168. data/lib/active_record/scoping/named.rb +6 -17
  169. data/lib/active_record/secure_token.rb +16 -8
  170. data/lib/active_record/serialization.rb +5 -3
  171. data/lib/active_record/signed_id.rb +116 -0
  172. data/lib/active_record/statement_cache.rb +20 -4
  173. data/lib/active_record/store.rb +2 -2
  174. data/lib/active_record/suppressor.rb +2 -2
  175. data/lib/active_record/table_metadata.rb +39 -51
  176. data/lib/active_record/tasks/database_tasks.rb +139 -113
  177. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
  178. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
  179. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
  180. data/lib/active_record/test_databases.rb +5 -4
  181. data/lib/active_record/test_fixtures.rb +37 -16
  182. data/lib/active_record/timestamp.rb +4 -6
  183. data/lib/active_record/touch_later.rb +21 -21
  184. data/lib/active_record/transactions.rb +15 -64
  185. data/lib/active_record/type.rb +8 -1
  186. data/lib/active_record/type/serialized.rb +6 -2
  187. data/lib/active_record/type/time.rb +10 -0
  188. data/lib/active_record/type_caster/connection.rb +0 -1
  189. data/lib/active_record/type_caster/map.rb +8 -5
  190. data/lib/active_record/validations.rb +1 -0
  191. data/lib/active_record/validations/numericality.rb +35 -0
  192. data/lib/active_record/validations/uniqueness.rb +24 -4
  193. data/lib/arel.rb +5 -13
  194. data/lib/arel/attributes/attribute.rb +4 -0
  195. data/lib/arel/collectors/bind.rb +5 -0
  196. data/lib/arel/collectors/composite.rb +8 -0
  197. data/lib/arel/collectors/sql_string.rb +7 -0
  198. data/lib/arel/collectors/substitute_binds.rb +7 -0
  199. data/lib/arel/nodes.rb +3 -1
  200. data/lib/arel/nodes/binary.rb +82 -8
  201. data/lib/arel/nodes/bind_param.rb +8 -0
  202. data/lib/arel/nodes/casted.rb +21 -9
  203. data/lib/arel/nodes/equality.rb +6 -9
  204. data/lib/arel/nodes/grouping.rb +3 -0
  205. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  206. data/lib/arel/nodes/in.rb +8 -1
  207. data/lib/arel/nodes/infix_operation.rb +13 -1
  208. data/lib/arel/nodes/join_source.rb +1 -1
  209. data/lib/arel/nodes/node.rb +7 -6
  210. data/lib/arel/nodes/ordering.rb +27 -0
  211. data/lib/arel/nodes/sql_literal.rb +3 -0
  212. data/lib/arel/nodes/table_alias.rb +7 -3
  213. data/lib/arel/nodes/unary.rb +0 -1
  214. data/lib/arel/predications.rb +12 -18
  215. data/lib/arel/select_manager.rb +1 -2
  216. data/lib/arel/table.rb +13 -5
  217. data/lib/arel/visitors.rb +0 -7
  218. data/lib/arel/visitors/dot.rb +14 -2
  219. data/lib/arel/visitors/mysql.rb +11 -1
  220. data/lib/arel/visitors/postgresql.rb +15 -4
  221. data/lib/arel/visitors/to_sql.rb +89 -78
  222. data/lib/rails/generators/active_record/migration.rb +6 -1
  223. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  224. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  225. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
  226. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  227. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  228. metadata +27 -28
  229. data/lib/active_record/advisory_lock_base.rb +0 -18
  230. data/lib/active_record/attribute_decorators.rb +0 -88
  231. data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
  232. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  233. data/lib/active_record/define_callbacks.rb +0 -22
  234. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  235. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  236. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  237. data/lib/arel/attributes.rb +0 -22
  238. data/lib/arel/visitors/depth_first.rb +0 -203
  239. data/lib/arel/visitors/ibm_db.rb +0 -34
  240. data/lib/arel/visitors/informix.rb +0 -62
  241. data/lib/arel/visitors/mssql.rb +0 -156
  242. data/lib/arel/visitors/oracle.rb +0 -158
  243. data/lib/arel/visitors/oracle12.rb +0 -65
  244. data/lib/arel/visitors/where_sql.rb +0 -22
@@ -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
 
@@ -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
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # Copyright (c) 2004-2019 David Heinemeier Hansson
4
+ # Copyright (c) 2004-2020 David Heinemeier Hansson
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining
7
7
  # a copy of this software and associated documentation files (the
@@ -31,17 +31,18 @@ require "yaml"
31
31
 
32
32
  require "active_record/version"
33
33
  require "active_model/attribute_set"
34
+ require "active_record/errors"
34
35
 
35
36
  module ActiveRecord
36
37
  extend ActiveSupport::Autoload
37
38
 
38
- autoload :AdvisoryLockBase
39
39
  autoload :Base
40
40
  autoload :Callbacks
41
41
  autoload :Core
42
42
  autoload :ConnectionHandling
43
43
  autoload :CounterCache
44
44
  autoload :DynamicMatchers
45
+ autoload :DelegatedType
45
46
  autoload :Enum
46
47
  autoload :InternalMetadata
47
48
  autoload :Explain
@@ -68,18 +69,17 @@ module ActiveRecord
68
69
  autoload :Serialization
69
70
  autoload :StatementCache
70
71
  autoload :Store
72
+ autoload :SignedId
71
73
  autoload :Suppressor
72
74
  autoload :Timestamp
73
75
  autoload :Transactions
74
76
  autoload :Translation
75
77
  autoload :Validations
76
78
  autoload :SecureToken
77
- autoload :DatabaseSelector, "active_record/middleware/database_selector"
79
+ autoload :DestroyAssociationAsyncJob
78
80
 
79
81
  eager_autoload do
80
- autoload :ActiveRecordError, "active_record/errors"
81
- autoload :ConnectionNotEstablished, "active_record/errors"
82
- autoload :ConnectionAdapters, "active_record/connection_adapters/abstract_adapter"
82
+ autoload :ConnectionAdapters
83
83
 
84
84
  autoload :Aggregations
85
85
  autoload :Associations
@@ -137,14 +137,6 @@ module ActiveRecord
137
137
  end
138
138
  end
139
139
 
140
- module ConnectionAdapters
141
- extend ActiveSupport::Autoload
142
-
143
- eager_autoload do
144
- autoload :AbstractAdapter
145
- end
146
- end
147
-
148
140
  module Scoping
149
141
  extend ActiveSupport::Autoload
150
142
 
@@ -194,3 +186,4 @@ end
194
186
  YAML.load_tags["!ruby/object:ActiveRecord::AttributeSet"] = "ActiveModel::AttributeSet"
195
187
  YAML.load_tags["!ruby/object:ActiveRecord::Attribute::FromDatabase"] = "ActiveModel::Attribute::FromDatabase"
196
188
  YAML.load_tags["!ruby/object:ActiveRecord::LazyAttributeHash"] = "ActiveModel::LazyAttributeHash"
189
+ YAML.load_tags["!ruby/object:ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::MysqlString"] = "ActiveRecord::Type::String"
@@ -141,7 +141,7 @@ module ActiveRecord
141
141
  # converted to an instance of value class if necessary.
142
142
  #
143
143
  # For example, the +NetworkResource+ model has +network_address+ and +cidr_range+ attributes that should be
144
- # 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).
145
145
  # The constructor for the value class is called +create+ and it expects a CIDR address string as a parameter.
146
146
  # New values can be assigned to the value object using either another +NetAddr::CIDR+ object, a string
147
147
  # or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to meet
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
- class AssociationRelation < Relation
4
+ class AssociationRelation < Relation # :nodoc:
5
5
  def initialize(klass, association, **)
6
6
  super(klass)
7
7
  @association = association
@@ -15,23 +15,31 @@ 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
- scoping { @association.build(attributes, &block) }
21
- end
22
- alias new build
23
-
24
- def create(attributes = nil, &block)
25
- block = _deprecated_scope_block("create", &block)
26
- scoping { @association.create(attributes, &block) }
27
- end
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
28
24
 
29
- def create!(attributes = nil, &block)
30
- block = _deprecated_scope_block("create!", &block)
31
- scoping { @association.create!(attributes, &block) }
25
+ scoping { klass.#{method}(attributes, **kwargs) }
26
+ end
27
+ RUBY
32
28
  end
33
29
 
34
30
  private
31
+ def _new(attributes, &block)
32
+ @association.build(attributes, &block)
33
+ end
34
+
35
+ def _create(attributes, &block)
36
+ @association.create(attributes, &block)
37
+ end
38
+
39
+ def _create!(attributes, &block)
40
+ @association.create!(attributes, &block)
41
+ end
42
+
35
43
  def exec_queries
36
44
  super do |record|
37
45
  @association.set_inverse_instance_from_queries(record)
@@ -2,38 +2,116 @@
2
2
 
3
3
  require "active_support/core_ext/enumerable"
4
4
  require "active_support/core_ext/string/conversions"
5
- require "active_support/core_ext/module/remove_method"
6
- require "active_record/errors"
7
5
 
8
6
  module ActiveRecord
9
7
  class AssociationNotFoundError < ConfigurationError #:nodoc:
8
+ attr_reader :record, :association_name
10
9
  def initialize(record = nil, association_name = nil)
10
+ @record = record
11
+ @association_name = association_name
11
12
  if record && association_name
12
13
  super("Association named '#{association_name}' was not found on #{record.class.name}; perhaps you misspelled it?")
13
14
  else
14
15
  super("Association was not found.")
15
16
  end
16
17
  end
18
+
19
+ class Correction
20
+ def initialize(error)
21
+ @error = error
22
+ end
23
+
24
+ def corrections
25
+ if @error.association_name
26
+ maybe_these = @error.record.class.reflections.keys
27
+
28
+ maybe_these.sort_by { |n|
29
+ DidYouMean::Jaro.distance(@error.association_name.to_s, n)
30
+ }.reverse.first(4)
31
+ else
32
+ []
33
+ end
34
+ end
35
+ end
36
+
37
+ # We may not have DYM, and DYM might not let us register error handlers
38
+ if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error)
39
+ DidYouMean.correct_error(self, Correction)
40
+ end
17
41
  end
18
42
 
19
43
  class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc:
44
+ attr_reader :reflection, :associated_class
20
45
  def initialize(reflection = nil, associated_class = nil)
21
46
  if reflection
47
+ @reflection = reflection
48
+ @associated_class = associated_class.nil? ? reflection.klass : associated_class
22
49
  super("Could not find the inverse association for #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{associated_class.nil? ? reflection.class_name : associated_class.name})")
23
50
  else
24
51
  super("Could not find the inverse association.")
25
52
  end
26
53
  end
54
+
55
+ class Correction
56
+ def initialize(error)
57
+ @error = error
58
+ end
59
+
60
+ def corrections
61
+ if @error.reflection && @error.associated_class
62
+ maybe_these = @error.associated_class.reflections.keys
63
+
64
+ maybe_these.sort_by { |n|
65
+ DidYouMean::Jaro.distance(@error.reflection.options[:inverse_of].to_s, n)
66
+ }.reverse.first(4)
67
+ else
68
+ []
69
+ end
70
+ end
71
+ end
72
+
73
+ # We may not have DYM, and DYM might not let us register error handlers
74
+ if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error)
75
+ DidYouMean.correct_error(self, Correction)
76
+ end
27
77
  end
28
78
 
29
79
  class HasManyThroughAssociationNotFoundError < ActiveRecordError #:nodoc:
30
- def initialize(owner_class_name = nil, reflection = nil)
31
- if owner_class_name && reflection
32
- super("Could not find the association #{reflection.options[:through].inspect} in model #{owner_class_name}")
80
+ attr_reader :owner_class, :reflection
81
+
82
+ def initialize(owner_class = nil, reflection = nil)
83
+ if owner_class && reflection
84
+ @owner_class = owner_class
85
+ @reflection = reflection
86
+ super("Could not find the association #{reflection.options[:through].inspect} in model #{owner_class.name}")
33
87
  else
34
88
  super("Could not find the association.")
35
89
  end
36
90
  end
91
+
92
+ class Correction
93
+ def initialize(error)
94
+ @error = error
95
+ end
96
+
97
+ def corrections
98
+ if @error.reflection && @error.owner_class
99
+ maybe_these = @error.owner_class.reflections.keys
100
+ maybe_these -= [@error.reflection.name.to_s] # remove failing reflection
101
+
102
+ maybe_these.sort_by { |n|
103
+ DidYouMean::Jaro.distance(@error.reflection.options[:through].to_s, n)
104
+ }.reverse.first(4)
105
+ else
106
+ []
107
+ end
108
+ end
109
+ end
110
+
111
+ # We may not have DYM, and DYM might not let us register error handlers
112
+ if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error)
113
+ DidYouMean.correct_error(self, Correction)
114
+ end
37
115
  end
38
116
 
39
117
  class HasManyThroughAssociationPolymorphicSourceError < ActiveRecordError #:nodoc:
@@ -702,9 +780,8 @@ module ActiveRecord
702
780
  # inverse detection only works on #has_many, #has_one, and
703
781
  # #belongs_to associations.
704
782
  #
705
- # Extra options on the associations, as defined in the
706
- # <tt>AssociationReflection::INVALID_AUTOMATIC_INVERSE_OPTIONS</tt>
707
- # constant, or a custom scope, will also prevent the association's inverse
783
+ # <tt>:foreign_key</tt> and <tt>:through</tt> options on the associations,
784
+ # or a custom scope, will also prevent the association's inverse
708
785
  # from being found automatically.
709
786
  #
710
787
  # The automatic guessing of the inverse association uses a heuristic based
@@ -1292,7 +1369,9 @@ module ActiveRecord
1292
1369
  # similar callbacks may affect the <tt>:dependent</tt> behavior, and the
1293
1370
  # <tt>:dependent</tt> behavior may affect other callbacks.
1294
1371
  #
1372
+ # * <tt>nil</tt> do nothing (default).
1295
1373
  # * <tt>:destroy</tt> causes all the associated objects to also be destroyed.
1374
+ # * <tt>:destroy_async</tt> destroys all the associated objects in a background job.
1296
1375
  # * <tt>:delete_all</tt> causes all the associated objects to be deleted directly from the database (so callbacks will not be executed).
1297
1376
  # * <tt>:nullify</tt> causes the foreign keys to be set to +NULL+. Polymorphic type will also be nullified
1298
1377
  # on polymorphic associations. Callbacks are not executed.
@@ -1357,6 +1436,11 @@ module ActiveRecord
1357
1436
  # Specifies a module or array of modules that will be extended into the association object returned.
1358
1437
  # Useful for defining methods on associations, especially when they should be shared between multiple
1359
1438
  # association objects.
1439
+ # [:strict_loading]
1440
+ # Enforces strict loading every time the associated record is loaded through this association.
1441
+ # [:ensuring_owner_was]
1442
+ # Specifies an instance method to be called on the owner. The method must return true in order for the
1443
+ # associated records to be deleted in a background job.
1360
1444
  #
1361
1445
  # Option examples:
1362
1446
  # has_many :comments, -> { order("posted_on") }
@@ -1367,6 +1451,7 @@ module ActiveRecord
1367
1451
  # has_many :tags, as: :taggable
1368
1452
  # has_many :reports, -> { readonly }
1369
1453
  # has_many :subscribers, through: :subscriptions, source: :user
1454
+ # has_many :comments, strict_loading: true
1370
1455
  def has_many(name, scope = nil, **options, &extension)
1371
1456
  reflection = Builder::HasMany.build(self, name, scope, options, &extension)
1372
1457
  Reflection.add_reflection self, name, reflection
@@ -1436,7 +1521,9 @@ module ActiveRecord
1436
1521
  # Controls what happens to the associated object when
1437
1522
  # its owner is destroyed:
1438
1523
  #
1524
+ # * <tt>nil</tt> do nothing (default).
1439
1525
  # * <tt>:destroy</tt> causes the associated object to also be destroyed
1526
+ # * <tt>:destroy_async</tt> causes all the associated object to be destroyed in a background job.
1440
1527
  # * <tt>:delete</tt> causes the associated object to be deleted directly from the database (so callbacks will not execute)
1441
1528
  # * <tt>:nullify</tt> causes the foreign key to be set to +NULL+. Polymorphic type column is also nullified
1442
1529
  # on polymorphic associations. Callbacks are not executed.
@@ -1495,6 +1582,11 @@ module ActiveRecord
1495
1582
  # When set to +true+, the association will also have its presence validated.
1496
1583
  # This will validate the association itself, not the id. You can use
1497
1584
  # +:inverse_of+ to avoid an extra query during validation.
1585
+ # [:strict_loading]
1586
+ # Enforces strict loading every time the associated record is loaded through this association.
1587
+ # [:ensuring_owner_was]
1588
+ # Specifies an instance method to be called on the owner. The method must return true in order for the
1589
+ # associated records to be deleted in a background job.
1498
1590
  #
1499
1591
  # Option examples:
1500
1592
  # has_one :credit_card, dependent: :destroy # destroys the associated credit card
@@ -1507,6 +1599,7 @@ module ActiveRecord
1507
1599
  # has_one :club, through: :membership
1508
1600
  # has_one :primary_address, -> { where(primary: true) }, through: :addressables, source: :addressable
1509
1601
  # has_one :credit_card, required: true
1602
+ # has_one :credit_card, strict_loading: true
1510
1603
  def has_one(name, scope = nil, **options)
1511
1604
  reflection = Builder::HasOne.build(self, name, scope, options)
1512
1605
  Reflection.add_reflection self, name, reflection
@@ -1588,7 +1681,8 @@ module ActiveRecord
1588
1681
  # By default this is +id+.
1589
1682
  # [:dependent]
1590
1683
  # If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
1591
- # <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method.
1684
+ # <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method. If set to
1685
+ # <tt>:destroy_async</tt>, the associated object is scheduled to be destroyed in a background job.
1592
1686
  # This option should not be specified when #belongs_to is used in conjunction with
1593
1687
  # a #has_many relationship on another class because of the potential to leave
1594
1688
  # orphaned records behind.
@@ -1640,6 +1734,11 @@ module ActiveRecord
1640
1734
  # [:default]
1641
1735
  # Provide a callable (i.e. proc or lambda) to specify that the association should
1642
1736
  # be initialized with a particular record before validation.
1737
+ # [:strict_loading]
1738
+ # Enforces strict loading every time the associated record is loaded through this association.
1739
+ # [:ensuring_owner_was]
1740
+ # Specifies an instance method to be called on the owner. The method must return true in order for the
1741
+ # associated records to be deleted in a background job.
1643
1742
  #
1644
1743
  # Option examples:
1645
1744
  # belongs_to :firm, foreign_key: "client_of"
@@ -1654,6 +1753,7 @@ module ActiveRecord
1654
1753
  # belongs_to :company, touch: :employees_last_updated_at
1655
1754
  # belongs_to :user, optional: true
1656
1755
  # belongs_to :account, default: -> { company.account }
1756
+ # belongs_to :account, strict_loading: true
1657
1757
  def belongs_to(name, scope = nil, **options)
1658
1758
  reflection = Builder::BelongsTo.build(self, name, scope, options)
1659
1759
  Reflection.add_reflection self, name, reflection
@@ -1676,7 +1776,7 @@ module ActiveRecord
1676
1776
  # The join table should not have a primary key or a model associated with it. You must manually generate the
1677
1777
  # join table with a migration such as this:
1678
1778
  #
1679
- # class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[5.0]
1779
+ # class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[6.0]
1680
1780
  # def change
1681
1781
  # create_join_table :developers, :projects
1682
1782
  # end
@@ -1816,6 +1916,8 @@ module ActiveRecord
1816
1916
  #
1817
1917
  # Note that NestedAttributes::ClassMethods#accepts_nested_attributes_for sets
1818
1918
  # <tt>:autosave</tt> to <tt>true</tt>.
1919
+ # [:strict_loading]
1920
+ # Enforces strict loading every time an associated record is loaded through this association.
1819
1921
  #
1820
1922
  # Option examples:
1821
1923
  # has_and_belongs_to_many :projects
@@ -1823,6 +1925,7 @@ module ActiveRecord
1823
1925
  # has_and_belongs_to_many :nations, class_name: "Country"
1824
1926
  # has_and_belongs_to_many :categories, join_table: "prods_cats"
1825
1927
  # has_and_belongs_to_many :categories, -> { readonly }
1928
+ # has_and_belongs_to_many :categories, strict_loading: true
1826
1929
  def has_and_belongs_to_many(name, scope = nil, **options, &extension)
1827
1930
  habtm_reflection = ActiveRecord::Reflection::HasAndBelongsToManyReflection.new(name, scope, options, self)
1828
1931
 
@@ -1853,7 +1956,7 @@ module ActiveRecord
1853
1956
  hm_options[:through] = middle_reflection.name
1854
1957
  hm_options[:source] = join_model.right_reflection.name
1855
1958
 
1856
- [:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table, :class_name, :extend].each do |k|
1959
+ [:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table, :class_name, :extend, :strict_loading].each do |k|
1857
1960
  hm_options[k] = options[k] if options.key? k
1858
1961
  end
1859
1962
 
@@ -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,25 +51,26 @@ 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