activerecord 8.0.2 → 8.1.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +459 -413
- data/README.rdoc +2 -2
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/belongs_to_association.rb +9 -1
- data/lib/active_record/associations/builder/association.rb +16 -5
- data/lib/active_record/associations/builder/belongs_to.rb +17 -4
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_one.rb +1 -1
- data/lib/active_record/associations/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_association.rb +3 -3
- data/lib/active_record/associations/collection_proxy.rb +22 -4
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/errors.rb +3 -0
- data/lib/active_record/associations/join_dependency.rb +2 -0
- data/lib/active_record/associations/preloader/branch.rb +1 -0
- data/lib/active_record/associations.rb +159 -21
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/serialization.rb +17 -4
- data/lib/active_record/attributes.rb +38 -24
- data/lib/active_record/base.rb +0 -1
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +2 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +15 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +51 -12
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +384 -49
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +26 -30
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +19 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -24
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +7 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +26 -34
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +89 -23
- data/lib/active_record/connection_adapters/abstract/transaction.rb +16 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +67 -13
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +43 -11
- data/lib/active_record/connection_adapters/column.rb +17 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +42 -5
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +26 -4
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +27 -22
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -16
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +8 -21
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +65 -30
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +74 -38
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +12 -7
- data/lib/active_record/connection_adapters/schema_cache.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +39 -27
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -13
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +56 -32
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +4 -3
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -1
- data/lib/active_record/connection_adapters.rb +1 -0
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +13 -10
- data/lib/active_record/counter_cache.rb +33 -8
- data/lib/active_record/database_configurations/database_config.rb +5 -1
- data/lib/active_record/database_configurations/hash_config.rb +56 -9
- data/lib/active_record/database_configurations/url_config.rb +13 -3
- data/lib/active_record/database_configurations.rb +7 -3
- data/lib/active_record/delegated_type.rb +2 -2
- data/lib/active_record/dynamic_matchers.rb +54 -69
- data/lib/active_record/encryption/encryptable_record.rb +5 -5
- data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
- data/lib/active_record/encryption/encryptor.rb +27 -25
- data/lib/active_record/encryption/scheme.rb +1 -1
- data/lib/active_record/enum.rb +37 -20
- data/lib/active_record/errors.rb +20 -4
- data/lib/active_record/explain_registry.rb +0 -1
- data/lib/active_record/filter_attribute_handler.rb +73 -0
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +2 -2
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +1 -1
- data/lib/active_record/insert_all.rb +12 -7
- data/lib/active_record/locking/optimistic.rb +7 -0
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +1 -5
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +14 -1
- data/lib/active_record/migration/compatibility.rb +34 -24
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration.rb +31 -21
- data/lib/active_record/model_schema.rb +10 -7
- data/lib/active_record/nested_attributes.rb +2 -0
- data/lib/active_record/persistence.rb +34 -3
- data/lib/active_record/query_cache.rb +22 -15
- data/lib/active_record/query_logs.rb +7 -7
- data/lib/active_record/querying.rb +4 -4
- data/lib/active_record/railtie.rb +34 -5
- data/lib/active_record/railties/databases.rake +23 -19
- data/lib/active_record/railties/job_checkpoints.rb +15 -0
- data/lib/active_record/railties/job_runtime.rb +10 -11
- data/lib/active_record/reflection.rb +42 -3
- data/lib/active_record/relation/batches.rb +26 -12
- data/lib/active_record/relation/calculations.rb +35 -25
- data/lib/active_record/relation/delegation.rb +0 -1
- data/lib/active_record/relation/finder_methods.rb +41 -24
- data/lib/active_record/relation/merger.rb +2 -2
- data/lib/active_record/relation/predicate_builder.rb +2 -2
- data/lib/active_record/relation/query_attribute.rb +3 -1
- data/lib/active_record/relation/query_methods.rb +43 -33
- data/lib/active_record/relation/spawn_methods.rb +6 -6
- data/lib/active_record/relation/where_clause.rb +7 -10
- data/lib/active_record/relation.rb +37 -15
- data/lib/active_record/result.rb +44 -21
- data/lib/active_record/sanitization.rb +2 -0
- data/lib/active_record/schema_dumper.rb +12 -10
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +46 -18
- data/lib/active_record/statement_cache.rb +13 -9
- data/lib/active_record/store.rb +44 -19
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +24 -35
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -40
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -26
- data/lib/active_record/test_databases.rb +11 -3
- data/lib/active_record/test_fixtures.rb +27 -2
- data/lib/active_record/testing/query_assertions.rb +8 -2
- data/lib/active_record/timestamp.rb +4 -2
- data/lib/active_record/transaction.rb +2 -5
- data/lib/active_record/transactions.rb +34 -10
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
- data/lib/active_record/type/internal/timezone.rb +7 -0
- data/lib/active_record/type/json.rb +15 -2
- data/lib/active_record/type/serialized.rb +11 -4
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +2 -1
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record.rb +68 -5
- data/lib/arel/alias_predication.rb +2 -0
- data/lib/arel/crud.rb +8 -11
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/node.rb +1 -1
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +0 -2
- data/lib/arel/select_manager.rb +13 -4
- data/lib/arel/update_manager.rb +5 -0
- data/lib/arel/visitors/dot.rb +2 -3
- data/lib/arel/visitors/postgresql.rb +55 -0
- data/lib/arel/visitors/sqlite.rb +55 -8
- data/lib/arel/visitors/to_sql.rb +5 -21
- data/lib/arel.rb +3 -1
- metadata +15 -11
- data/lib/active_record/normalization.rb +0 -163
data/README.rdoc
CHANGED
@@ -139,7 +139,7 @@ A short rundown of some of the major features:
|
|
139
139
|
|
140
140
|
* Database agnostic schema management with Migrations.
|
141
141
|
|
142
|
-
class AddSystemSettings < ActiveRecord::Migration[8.
|
142
|
+
class AddSystemSettings < ActiveRecord::Migration[8.1]
|
143
143
|
def up
|
144
144
|
create_table :system_settings do |t|
|
145
145
|
t.string :name
|
@@ -214,6 +214,6 @@ Bug reports for the Ruby on \Rails project can be filed here:
|
|
214
214
|
|
215
215
|
* https://github.com/rails/rails/issues
|
216
216
|
|
217
|
-
Feature requests should be discussed on the
|
217
|
+
Feature requests should be discussed on the rubyonrails-core forum here:
|
218
218
|
|
219
219
|
* https://discuss.rubyonrails.org/c/rubyonrails-core
|
@@ -17,7 +17,7 @@ module ActiveRecord
|
|
17
17
|
|
18
18
|
%w(insert insert_all insert! insert_all! upsert upsert_all).each do |method|
|
19
19
|
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
20
|
-
def #{method}(
|
20
|
+
def #{method}(...)
|
21
21
|
if @association.reflection.through_reflection?
|
22
22
|
raise ArgumentError, "Bulk insert or upsert is currently not supported for has_many through association"
|
23
23
|
end
|
@@ -232,7 +232,7 @@ module ActiveRecord
|
|
232
232
|
_create_record(attributes, true, &block)
|
233
233
|
end
|
234
234
|
|
235
|
-
# Whether the association
|
235
|
+
# Whether the association represents a single record
|
236
236
|
# or a collection of records.
|
237
237
|
def collection?
|
238
238
|
false
|
@@ -162,7 +162,15 @@ module ActiveRecord
|
|
162
162
|
end
|
163
163
|
|
164
164
|
def stale_state
|
165
|
-
|
165
|
+
foreign_key = reflection.foreign_key
|
166
|
+
if foreign_key.is_a?(Array)
|
167
|
+
attributes = foreign_key.map do |fk|
|
168
|
+
owner._read_attribute(fk) { |n| owner.send(:missing_attribute, n, caller) }
|
169
|
+
end
|
170
|
+
attributes if attributes.any?
|
171
|
+
else
|
172
|
+
owner._read_attribute(foreign_key) { |n| owner.send(:missing_attribute, n, caller) }
|
173
|
+
end
|
166
174
|
end
|
167
175
|
end
|
168
176
|
end
|
@@ -19,7 +19,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
19
19
|
self.extensions = []
|
20
20
|
|
21
21
|
VALID_OPTIONS = [
|
22
|
-
:
|
22
|
+
:anonymous_class, :primary_key, :foreign_key, :dependent, :validate, :inverse_of, :strict_loading, :query_constraints, :deprecated
|
23
23
|
].freeze # :nodoc:
|
24
24
|
|
25
25
|
def self.build(model, name, scope, options, &block)
|
@@ -102,7 +102,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
102
102
|
def self.define_readers(mixin, name)
|
103
103
|
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
104
104
|
def #{name}
|
105
|
-
association(:#{name})
|
105
|
+
association = association(:#{name})
|
106
|
+
deprecated_associations_api_guard(association, __method__)
|
107
|
+
association.reader
|
106
108
|
end
|
107
109
|
CODE
|
108
110
|
end
|
@@ -110,7 +112,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
110
112
|
def self.define_writers(mixin, name)
|
111
113
|
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
112
114
|
def #{name}=(value)
|
113
|
-
association(:#{name})
|
115
|
+
association = association(:#{name})
|
116
|
+
deprecated_associations_api_guard(association, __method__)
|
117
|
+
association.writer(value)
|
114
118
|
end
|
115
119
|
CODE
|
116
120
|
end
|
@@ -138,8 +142,15 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
138
142
|
end
|
139
143
|
|
140
144
|
def self.add_destroy_callbacks(model, reflection)
|
141
|
-
|
142
|
-
|
145
|
+
if reflection.deprecated?
|
146
|
+
# If :dependent is set, destroying the record has a side effect that
|
147
|
+
# would no longer happen if the association is removed.
|
148
|
+
model.before_destroy do
|
149
|
+
report_deprecated_association(reflection, context: ":dependent has a side effect here")
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
model.before_destroy(->(o) { o.association(reflection.name).handle_dependency })
|
143
154
|
end
|
144
155
|
|
145
156
|
def self.add_after_commit_jobs_callback(model, dependent)
|
@@ -8,8 +8,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
8
8
|
|
9
9
|
def self.valid_options(options)
|
10
10
|
valid = super + [:polymorphic, :counter_cache, :optional, :default]
|
11
|
-
valid
|
12
|
-
valid
|
11
|
+
valid << :class_name unless options[:polymorphic]
|
12
|
+
valid << :foreign_type if options[:polymorphic]
|
13
|
+
valid << :ensuring_owner_was if options[:dependent] == :destroy_async
|
13
14
|
valid
|
14
15
|
end
|
15
16
|
|
@@ -107,6 +108,14 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
107
108
|
end
|
108
109
|
|
109
110
|
def self.add_destroy_callbacks(model, reflection)
|
111
|
+
if reflection.deprecated?
|
112
|
+
# If :dependent is set, destroying the record has some side effect that
|
113
|
+
# would no longer happen if the association is removed.
|
114
|
+
model.before_destroy do
|
115
|
+
report_deprecated_association(reflection, context: ":dependent has a side effect here")
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
110
119
|
model.after_destroy lambda { |o| o.association(reflection.name).handle_dependency }
|
111
120
|
end
|
112
121
|
|
@@ -144,11 +153,15 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
144
153
|
def self.define_change_tracking_methods(model, reflection)
|
145
154
|
model.generated_association_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
146
155
|
def #{reflection.name}_changed?
|
147
|
-
association(:#{reflection.name})
|
156
|
+
association = association(:#{reflection.name})
|
157
|
+
deprecated_associations_api_guard(association, __method__)
|
158
|
+
association.target_changed?
|
148
159
|
end
|
149
160
|
|
150
161
|
def #{reflection.name}_previously_changed?
|
151
|
-
association(:#{reflection.name})
|
162
|
+
association = association(:#{reflection.name})
|
163
|
+
deprecated_associations_api_guard(association, __method__)
|
164
|
+
association.target_previously_changed?
|
152
165
|
end
|
153
166
|
CODE
|
154
167
|
end
|
@@ -7,7 +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 + [:before_add, :after_add, :before_remove, :after_remove, :extend]
|
10
|
+
super + [:class_name, :before_add, :after_add, :before_remove, :after_remove, :extend]
|
11
11
|
end
|
12
12
|
|
13
13
|
def self.define_callbacks(model, reflection)
|
@@ -60,7 +60,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
60
60
|
|
61
61
|
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
62
62
|
def #{name.to_s.singularize}_ids
|
63
|
-
association(:#{name})
|
63
|
+
association = association(:#{name})
|
64
|
+
deprecated_associations_api_guard(association, __method__)
|
65
|
+
association.ids_reader
|
64
66
|
end
|
65
67
|
CODE
|
66
68
|
end
|
@@ -70,7 +72,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
70
72
|
|
71
73
|
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
72
74
|
def #{name.to_s.singularize}_ids=(ids)
|
73
|
-
association(:#{name})
|
75
|
+
association = association(:#{name})
|
76
|
+
deprecated_associations_api_guard(association, __method__)
|
77
|
+
association.ids_writer(ids)
|
74
78
|
end
|
75
79
|
CODE
|
76
80
|
end
|
@@ -7,7 +7,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.valid_options(options)
|
10
|
-
valid = super + [:as, :through]
|
10
|
+
valid = super + [:class_name, :as, :through]
|
11
11
|
valid += [:foreign_type] if options[:as]
|
12
12
|
valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
|
13
13
|
valid += [:source, :source_type, :disable_joins] if options[:through]
|
@@ -17,11 +17,15 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
17
17
|
|
18
18
|
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
19
19
|
def reload_#{name}
|
20
|
-
association(:#{name})
|
20
|
+
association = association(:#{name})
|
21
|
+
deprecated_associations_api_guard(association, __method__)
|
22
|
+
association.force_reload_reader
|
21
23
|
end
|
22
24
|
|
23
25
|
def reset_#{name}
|
24
|
-
association(:#{name})
|
26
|
+
association = association(:#{name})
|
27
|
+
deprecated_associations_api_guard(association, __method__)
|
28
|
+
association.reset
|
25
29
|
end
|
26
30
|
CODE
|
27
31
|
end
|
@@ -30,19 +34,43 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
30
34
|
def self.define_constructors(mixin, name)
|
31
35
|
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
32
36
|
def build_#{name}(*args, &block)
|
33
|
-
association(:#{name})
|
37
|
+
association = association(:#{name})
|
38
|
+
deprecated_associations_api_guard(association, __method__)
|
39
|
+
association.build(*args, &block)
|
34
40
|
end
|
35
41
|
|
36
42
|
def create_#{name}(*args, &block)
|
37
|
-
association(:#{name})
|
43
|
+
association = association(:#{name})
|
44
|
+
deprecated_associations_api_guard(association, __method__)
|
45
|
+
association.create(*args, &block)
|
38
46
|
end
|
39
47
|
|
40
48
|
def create_#{name}!(*args, &block)
|
41
|
-
association(:#{name})
|
49
|
+
association = association(:#{name})
|
50
|
+
deprecated_associations_api_guard(association, __method__)
|
51
|
+
association.create!(*args, &block)
|
42
52
|
end
|
43
53
|
CODE
|
44
54
|
end
|
45
55
|
|
56
|
+
def self.define_callbacks(model, reflection)
|
57
|
+
super
|
58
|
+
|
59
|
+
# If the record is saved or destroyed and `:touch` is set, the parent
|
60
|
+
# record gets a timestamp updated. We want to know about it, because
|
61
|
+
# deleting the association would change that side-effect and perhaps there
|
62
|
+
# is code relying on it.
|
63
|
+
if reflection.deprecated? && reflection.options[:touch]
|
64
|
+
model.before_save do
|
65
|
+
report_deprecated_association(reflection, context: ":touch has a side effect here")
|
66
|
+
end
|
67
|
+
|
68
|
+
model.before_destroy do
|
69
|
+
report_deprecated_association(reflection, context: ":touch has a side effect here")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
46
74
|
private_class_method :valid_options, :define_accessors, :define_constructors
|
47
75
|
end
|
48
76
|
end
|
@@ -259,10 +259,10 @@ module ActiveRecord
|
|
259
259
|
klass = reflection.klass
|
260
260
|
return false unless record.is_a?(klass)
|
261
261
|
|
262
|
-
if
|
263
|
-
include_in_memory?(record)
|
264
|
-
elsif loaded?
|
262
|
+
if loaded?
|
265
263
|
target.include?(record)
|
264
|
+
elsif record.new_record?
|
265
|
+
include_in_memory?(record)
|
266
266
|
else
|
267
267
|
record_id = klass.composite_primary_key? ? klass.primary_key.zip(record.id).to_h : record.id
|
268
268
|
scope.exists?(record_id)
|
@@ -110,7 +110,7 @@ module ActiveRecord
|
|
110
110
|
# # ]
|
111
111
|
|
112
112
|
# Finds an object in the collection responding to the +id+. Uses the same
|
113
|
-
# rules as ActiveRecord::FinderMethods.find.
|
113
|
+
# rules as ActiveRecord::FinderMethods.find. Raises ActiveRecord::RecordNotFound
|
114
114
|
# error if the object cannot be found.
|
115
115
|
#
|
116
116
|
# class Person < ActiveRecord::Base
|
@@ -1125,14 +1125,32 @@ module ActiveRecord
|
|
1125
1125
|
super
|
1126
1126
|
end
|
1127
1127
|
|
1128
|
+
%w(insert insert_all insert! insert_all! upsert upsert_all).each do |method|
|
1129
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
1130
|
+
def #{method}(...)
|
1131
|
+
if @association&.target&.any? { |r| r.new_record? }
|
1132
|
+
association_name = @association.reflection.name
|
1133
|
+
ActiveRecord.deprecator.warn(<<~MSG)
|
1134
|
+
Using #{method} on association \#{association_name} with unpersisted records
|
1135
|
+
is deprecated and will be removed in Rails 8.2.
|
1136
|
+
The unpersisted records will be lost after this operation.
|
1137
|
+
Please either persist your records first or store them separately before
|
1138
|
+
calling #{method}.
|
1139
|
+
MSG
|
1140
|
+
scope.#{method}(...)
|
1141
|
+
else
|
1142
|
+
scope.#{method}(...).tap { reset }
|
1143
|
+
end
|
1144
|
+
end
|
1145
|
+
RUBY
|
1146
|
+
end
|
1147
|
+
|
1128
1148
|
delegate_methods = [
|
1129
1149
|
QueryMethods,
|
1130
1150
|
SpawnMethods,
|
1131
1151
|
].flat_map { |klass|
|
1132
1152
|
klass.public_instance_methods(false)
|
1133
|
-
} - self.public_instance_methods(false) - [:select] + [
|
1134
|
-
:scoping, :values, :insert, :insert_all, :insert!, :insert_all!, :upsert, :upsert_all, :load_async
|
1135
|
-
]
|
1153
|
+
} - self.public_instance_methods(false) - [ :select ] + [ :scoping, :values, :load_async ]
|
1136
1154
|
|
1137
1155
|
delegate(*delegate_methods, to: :scope)
|
1138
1156
|
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/notifications"
|
4
|
+
require "active_support/core_ext/array/conversions"
|
5
|
+
|
6
|
+
module ActiveRecord::Associations::Deprecation # :nodoc:
|
7
|
+
EVENT = "deprecated_association.active_record"
|
8
|
+
private_constant :EVENT
|
9
|
+
|
10
|
+
MODES = [:warn, :raise, :notify].freeze
|
11
|
+
private_constant :MODES
|
12
|
+
|
13
|
+
class << self
|
14
|
+
attr_reader :mode, :backtrace
|
15
|
+
|
16
|
+
def mode=(value) # private setter
|
17
|
+
unless MODES.include?(value)
|
18
|
+
raise ArgumentError, "invalid deprecated associations mode #{value.inspect} (valid modes are #{MODES.map(&:inspect).to_sentence})"
|
19
|
+
end
|
20
|
+
|
21
|
+
@mode = value
|
22
|
+
end
|
23
|
+
|
24
|
+
def backtrace=(value)
|
25
|
+
@backtrace = !!value
|
26
|
+
end
|
27
|
+
|
28
|
+
def guard(reflection)
|
29
|
+
report(reflection, context: yield) if reflection.deprecated?
|
30
|
+
|
31
|
+
if reflection.through_reflection?
|
32
|
+
reflection.deprecated_nested_reflections.each do |deprecated_nested_reflection|
|
33
|
+
context = "referenced as nested association of the through #{reflection.active_record}##{reflection.name}"
|
34
|
+
report(deprecated_nested_reflection, context: context)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def report(reflection, context:)
|
40
|
+
reflection = user_facing_reflection(reflection)
|
41
|
+
|
42
|
+
message = +"The association #{reflection.active_record}##{reflection.name} is deprecated, #{context}"
|
43
|
+
message << " (#{backtrace_cleaner.first_clean_frame})"
|
44
|
+
|
45
|
+
case @mode
|
46
|
+
when :warn
|
47
|
+
message = [message, *clean_frames].join("\n\t") if @backtrace
|
48
|
+
ActiveRecord::Base.logger&.warn(message)
|
49
|
+
when :raise
|
50
|
+
error = ActiveRecord::DeprecatedAssociationError.new(message)
|
51
|
+
if set_backtrace_supports_array_of_locations?
|
52
|
+
error.set_backtrace(clean_locations)
|
53
|
+
else
|
54
|
+
error.set_backtrace(clean_frames)
|
55
|
+
end
|
56
|
+
raise error
|
57
|
+
else
|
58
|
+
payload = { reflection: reflection, message: message, location: backtrace_cleaner.first_clean_location }
|
59
|
+
payload[:backtrace] = clean_locations if @backtrace
|
60
|
+
ActiveSupport::Notifications.instrument(EVENT, payload)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
def backtrace_cleaner
|
66
|
+
ActiveRecord::LogSubscriber.backtrace_cleaner
|
67
|
+
end
|
68
|
+
|
69
|
+
def clean_frames
|
70
|
+
backtrace_cleaner.clean(caller)
|
71
|
+
end
|
72
|
+
|
73
|
+
def clean_locations
|
74
|
+
backtrace_cleaner.clean_locations(caller_locations)
|
75
|
+
end
|
76
|
+
|
77
|
+
def set_backtrace_supports_array_of_locations?
|
78
|
+
@backtrace_supports_array_of_locations ||= Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0")
|
79
|
+
end
|
80
|
+
|
81
|
+
def user_facing_reflection(reflection)
|
82
|
+
reflection.active_record.reflect_on_association(reflection.name)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
self.mode = :warn
|
87
|
+
self.backtrace = false
|
88
|
+
end
|
@@ -118,6 +118,7 @@ module ActiveRecord
|
|
118
118
|
def loaders
|
119
119
|
@loaders ||=
|
120
120
|
grouped_records.flat_map do |reflection, reflection_records|
|
121
|
+
Deprecation.guard(reflection) { "referenced in query to preload records" }
|
121
122
|
preloaders_for_reflection(reflection, reflection_records)
|
122
123
|
end
|
123
124
|
end
|