activerecord 7.2.0.beta1 → 7.2.0.beta3
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 +42 -1
- data/lib/active_record/associations/collection_association.rb +3 -3
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations.rb +0 -262
- data/lib/active_record/attribute_methods/dirty.rb +1 -1
- data/lib/active_record/attribute_methods/read.rb +3 -3
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +8 -6
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +29 -13
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/transaction.rb +69 -9
- data/lib/active_record/connection_adapters/abstract_adapter.rb +8 -0
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +1 -1
- data/lib/active_record/database_configurations/database_config.rb +4 -0
- data/lib/active_record/enum.rb +1 -1
- data/lib/active_record/errors.rb +20 -8
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/railtie.rb +3 -4
- data/lib/active_record/railties/databases.rake +1 -1
- data/lib/active_record/relation/calculations.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +22 -10
- data/lib/active_record/signed_id.rb +9 -0
- data/lib/active_record/tasks/database_tasks.rb +2 -2
- data/lib/active_record/test_fixtures.rb +10 -3
- data/lib/active_record/timestamp.rb +1 -1
- data/lib/active_record/transaction.rb +106 -42
- data/lib/active_record/transactions.rb +29 -2
- data/lib/active_record.rb +5 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +11 -10
@@ -77,6 +77,13 @@ module ActiveRecord
|
|
77
77
|
# alias attributes in Active Record are lazily generated
|
78
78
|
end
|
79
79
|
|
80
|
+
def generate_alias_attribute_methods(code_generator, new_name, old_name) # :nodoc:
|
81
|
+
attribute_method_patterns.each do |pattern|
|
82
|
+
alias_attribute_method_definition(code_generator, pattern, new_name, old_name)
|
83
|
+
end
|
84
|
+
attribute_method_patterns_cache.clear
|
85
|
+
end
|
86
|
+
|
80
87
|
def alias_attribute_method_definition(code_generator, pattern, new_name, old_name)
|
81
88
|
old_name = old_name.to_s
|
82
89
|
|
@@ -84,12 +91,7 @@ module ActiveRecord
|
|
84
91
|
raise ArgumentError, "#{self.name} model aliases `#{old_name}`, but `#{old_name}` is not an attribute. " \
|
85
92
|
"Use `alias_method :#{new_name}, :#{old_name}` or define the method manually."
|
86
93
|
else
|
87
|
-
|
88
|
-
parameters = pattern.parameters
|
89
|
-
|
90
|
-
define_proxy_call(code_generator, method_name, pattern.proxy_target, parameters, old_name,
|
91
|
-
namespace: :proxy_alias_attribute
|
92
|
-
)
|
94
|
+
define_attribute_method_pattern(pattern, old_name, owner: code_generator, as: new_name, override: true)
|
93
95
|
end
|
94
96
|
end
|
95
97
|
|
@@ -74,7 +74,7 @@ module ActiveRecord
|
|
74
74
|
# Connections can be obtained and used from a connection pool in several
|
75
75
|
# ways:
|
76
76
|
#
|
77
|
-
# 1. Simply use {ActiveRecord::Base.lease_connection}[rdoc-ref:ConnectionHandling
|
77
|
+
# 1. Simply use {ActiveRecord::Base.lease_connection}[rdoc-ref:ConnectionHandling#lease_connection].
|
78
78
|
# When you're done with the connection(s) and wish it to be returned to the pool, you call
|
79
79
|
# {ActiveRecord::Base.connection_handler.clear_active_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_active_connections!].
|
80
80
|
# This is the default behavior for Active Record when used in conjunction with
|
@@ -253,6 +253,7 @@ module ActiveRecord
|
|
253
253
|
|
254
254
|
@available = ConnectionLeasingQueue.new self
|
255
255
|
@pinned_connection = nil
|
256
|
+
@pinned_connections_depth = 0
|
256
257
|
|
257
258
|
@async_executor = build_async_executor
|
258
259
|
|
@@ -262,6 +263,13 @@ module ActiveRecord
|
|
262
263
|
@reaper.run
|
263
264
|
end
|
264
265
|
|
266
|
+
def inspect # :nodoc:
|
267
|
+
name_field = " name=#{db_config.name.inspect}" unless db_config.name == "primary"
|
268
|
+
shard_field = " shard=#{@shard.inspect}" unless @shard == :default
|
269
|
+
|
270
|
+
"#<#{self.class.name} env_name=#{db_config.env_name.inspect}#{name_field} role=#{role.inspect}#{shard_field}>"
|
271
|
+
end
|
272
|
+
|
265
273
|
def schema_cache
|
266
274
|
@schema_cache ||= BoundSchemaReflection.new(schema_reflection, self)
|
267
275
|
end
|
@@ -305,15 +313,15 @@ module ActiveRecord
|
|
305
313
|
def connection
|
306
314
|
ActiveRecord.deprecator.warn(<<~MSG)
|
307
315
|
ActiveRecord::ConnectionAdapters::ConnectionPool#connection is deprecated
|
308
|
-
and will be removed in Rails
|
316
|
+
and will be removed in Rails 8.0. Use #lease_connection instead.
|
309
317
|
MSG
|
310
318
|
lease_connection
|
311
319
|
end
|
312
320
|
|
313
321
|
def pin_connection!(lock_thread) # :nodoc:
|
314
|
-
|
322
|
+
@pinned_connection ||= (connection_lease&.connection || checkout)
|
323
|
+
@pinned_connections_depth += 1
|
315
324
|
|
316
|
-
@pinned_connection = (connection_lease&.connection || checkout)
|
317
325
|
# Any leased connection must be in @connections otherwise
|
318
326
|
# some methods like #connected? won't behave correctly
|
319
327
|
unless @connections.include?(@pinned_connection)
|
@@ -330,7 +338,10 @@ module ActiveRecord
|
|
330
338
|
|
331
339
|
clean = true
|
332
340
|
@pinned_connection.lock.synchronize do
|
333
|
-
|
341
|
+
@pinned_connections_depth -= 1
|
342
|
+
connection = @pinned_connection
|
343
|
+
@pinned_connection = nil if @pinned_connections_depth.zero?
|
344
|
+
|
334
345
|
if connection.transaction_open?
|
335
346
|
connection.rollback_transaction
|
336
347
|
else
|
@@ -338,8 +349,11 @@ module ActiveRecord
|
|
338
349
|
clean = false
|
339
350
|
connection.reset!
|
340
351
|
end
|
341
|
-
|
342
|
-
|
352
|
+
|
353
|
+
if @pinned_connection.nil?
|
354
|
+
connection.lock_thread = nil
|
355
|
+
checkin(connection)
|
356
|
+
end
|
343
357
|
end
|
344
358
|
|
345
359
|
clean
|
@@ -527,12 +541,14 @@ module ActiveRecord
|
|
527
541
|
# - ActiveRecord::ConnectionTimeoutError no connection can be obtained from the pool.
|
528
542
|
def checkout(checkout_timeout = @checkout_timeout)
|
529
543
|
if @pinned_connection
|
530
|
-
synchronize do
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
@connections
|
544
|
+
@pinned_connection.lock.synchronize do
|
545
|
+
synchronize do
|
546
|
+
@pinned_connection.verify!
|
547
|
+
# Any leased connection must be in @connections otherwise
|
548
|
+
# some methods like #connected? won't behave correctly
|
549
|
+
unless @connections.include?(@pinned_connection)
|
550
|
+
@connections << @pinned_connection
|
551
|
+
end
|
536
552
|
end
|
537
553
|
end
|
538
554
|
@pinned_connection
|
@@ -356,7 +356,7 @@ module ActiveRecord
|
|
356
356
|
if isolation
|
357
357
|
raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
|
358
358
|
end
|
359
|
-
yield current_transaction
|
359
|
+
yield current_transaction.user_transaction
|
360
360
|
else
|
361
361
|
transaction_manager.within_new_transaction(isolation: isolation, joinable: joinable, &block)
|
362
362
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/digest"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module ConnectionAdapters
|
5
7
|
# = Active Record Connection Adapters Transaction State
|
@@ -89,7 +91,9 @@ module ActiveRecord
|
|
89
91
|
raise InstrumentationAlreadyStartedError.new("Called start on an already started transaction") if @started
|
90
92
|
@started = true
|
91
93
|
|
92
|
-
|
94
|
+
ActiveSupport::Notifications.instrument("start_transaction.active_record", @base_payload)
|
95
|
+
|
96
|
+
@payload = @base_payload.dup # We dup because the payload for a given event is mutated later to add the outcome.
|
93
97
|
@handle = ActiveSupport::Notifications.instrumenter.build_handle("transaction.active_record", @payload)
|
94
98
|
@handle.start
|
95
99
|
end
|
@@ -104,7 +108,6 @@ module ActiveRecord
|
|
104
108
|
end
|
105
109
|
|
106
110
|
class NullTransaction # :nodoc:
|
107
|
-
def initialize; end
|
108
111
|
def state; end
|
109
112
|
def closed?; true; end
|
110
113
|
def open?; false; end
|
@@ -118,11 +121,31 @@ module ActiveRecord
|
|
118
121
|
def materialized?; false; end
|
119
122
|
def before_commit; yield; end
|
120
123
|
def after_commit; yield; end
|
121
|
-
def after_rollback; end
|
124
|
+
def after_rollback; end
|
125
|
+
def user_transaction; ActiveRecord::Transaction::NULL_TRANSACTION; end
|
122
126
|
end
|
123
127
|
|
124
|
-
class Transaction
|
125
|
-
|
128
|
+
class Transaction # :nodoc:
|
129
|
+
class Callback # :nodoc:
|
130
|
+
def initialize(event, callback)
|
131
|
+
@event = event
|
132
|
+
@callback = callback
|
133
|
+
end
|
134
|
+
|
135
|
+
def before_commit
|
136
|
+
@callback.call if @event == :before_commit
|
137
|
+
end
|
138
|
+
|
139
|
+
def after_commit
|
140
|
+
@callback.call if @event == :after_commit
|
141
|
+
end
|
142
|
+
|
143
|
+
def after_rollback
|
144
|
+
@callback.call if @event == :after_rollback
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
attr_reader :connection, :state, :savepoint_name, :isolation_level, :user_transaction
|
126
149
|
attr_accessor :written
|
127
150
|
|
128
151
|
delegate :invalidate!, :invalidated?, to: :@state
|
@@ -131,6 +154,7 @@ module ActiveRecord
|
|
131
154
|
super()
|
132
155
|
@connection = connection
|
133
156
|
@state = TransactionState.new
|
157
|
+
@callbacks = nil
|
134
158
|
@records = nil
|
135
159
|
@isolation_level = isolation
|
136
160
|
@materialized = false
|
@@ -138,7 +162,8 @@ module ActiveRecord
|
|
138
162
|
@run_commit_callbacks = run_commit_callbacks
|
139
163
|
@lazy_enrollment_records = nil
|
140
164
|
@dirty = false
|
141
|
-
@
|
165
|
+
@user_transaction = joinable ? ActiveRecord::Transaction.new(self) : ActiveRecord::Transaction::NULL_TRANSACTION
|
166
|
+
@instrumenter = TransactionInstrumenter.new(connection: connection, transaction: @user_transaction)
|
142
167
|
end
|
143
168
|
|
144
169
|
def dirty!
|
@@ -149,6 +174,14 @@ module ActiveRecord
|
|
149
174
|
@dirty
|
150
175
|
end
|
151
176
|
|
177
|
+
def open?
|
178
|
+
true
|
179
|
+
end
|
180
|
+
|
181
|
+
def closed?
|
182
|
+
false
|
183
|
+
end
|
184
|
+
|
152
185
|
def add_record(record, ensure_finalize = true)
|
153
186
|
@records ||= []
|
154
187
|
if ensure_finalize
|
@@ -159,6 +192,30 @@ module ActiveRecord
|
|
159
192
|
end
|
160
193
|
end
|
161
194
|
|
195
|
+
def before_commit(&block)
|
196
|
+
if @state.finalized?
|
197
|
+
raise ActiveRecordError, "Cannot register callbacks on a finalized transaction"
|
198
|
+
end
|
199
|
+
|
200
|
+
(@callbacks ||= []) << Callback.new(:before_commit, block)
|
201
|
+
end
|
202
|
+
|
203
|
+
def after_commit(&block)
|
204
|
+
if @state.finalized?
|
205
|
+
raise ActiveRecordError, "Cannot register callbacks on a finalized transaction"
|
206
|
+
end
|
207
|
+
|
208
|
+
(@callbacks ||= []) << Callback.new(:after_commit, block)
|
209
|
+
end
|
210
|
+
|
211
|
+
def after_rollback(&block)
|
212
|
+
if @state.finalized?
|
213
|
+
raise ActiveRecordError, "Cannot register callbacks on a finalized transaction"
|
214
|
+
end
|
215
|
+
|
216
|
+
(@callbacks ||= []) << Callback.new(:after_rollback, block)
|
217
|
+
end
|
218
|
+
|
162
219
|
def records
|
163
220
|
if @lazy_enrollment_records
|
164
221
|
@records.concat @lazy_enrollment_records.values
|
@@ -269,8 +326,11 @@ module ActiveRecord
|
|
269
326
|
|
270
327
|
def full_rollback?; true; end
|
271
328
|
def joinable?; @joinable; end
|
272
|
-
|
273
|
-
|
329
|
+
|
330
|
+
protected
|
331
|
+
def append_callbacks(callbacks) # :nodoc:
|
332
|
+
(@callbacks ||= []).concat(callbacks)
|
333
|
+
end
|
274
334
|
|
275
335
|
private
|
276
336
|
def unique_records
|
@@ -553,7 +613,7 @@ module ActiveRecord
|
|
553
613
|
@connection.lock.synchronize do
|
554
614
|
transaction = begin_transaction(isolation: isolation, joinable: joinable)
|
555
615
|
begin
|
556
|
-
yield transaction
|
616
|
+
yield transaction.user_transaction
|
557
617
|
rescue Exception => error
|
558
618
|
rollback_transaction
|
559
619
|
after_failure_actions(transaction, error)
|
@@ -172,6 +172,13 @@ module ActiveRecord
|
|
172
172
|
@verified = false
|
173
173
|
end
|
174
174
|
|
175
|
+
def inspect # :nodoc:
|
176
|
+
name_field = " name=#{pool.db_config.name.inspect}" unless pool.db_config.name == "primary"
|
177
|
+
shard_field = " shard=#{shard.inspect}" unless shard == :default
|
178
|
+
|
179
|
+
"#<#{self.class.name}:#{'%#016x' % (object_id << 1)} env_name=#{pool.db_config.env_name.inspect}#{name_field} role=#{role.inspect}#{shard_field}>"
|
180
|
+
end
|
181
|
+
|
175
182
|
def lock_thread=(lock_thread) # :nodoc:
|
176
183
|
@lock =
|
177
184
|
case lock_thread
|
@@ -1118,6 +1125,7 @@ module ActiveRecord
|
|
1118
1125
|
statement_name: statement_name,
|
1119
1126
|
async: async,
|
1120
1127
|
connection: self,
|
1128
|
+
transaction: current_transaction.user_transaction.presence,
|
1121
1129
|
row_count: 0,
|
1122
1130
|
&block
|
1123
1131
|
)
|
@@ -644,7 +644,7 @@ module ActiveRecord
|
|
644
644
|
# MySQL 8.0.19 replaces `VALUES(<expression>)` clauses with row and column alias names, see https://dev.mysql.com/worklog/task/?id=6312 .
|
645
645
|
# then MySQL 8.0.20 deprecates the `VALUES(<expression>)` see https://dev.mysql.com/worklog/task/?id=13325 .
|
646
646
|
if supports_insert_raw_alias_syntax?
|
647
|
-
values_alias = quote_table_name("#{insert.model.table_name}_values")
|
647
|
+
values_alias = quote_table_name("#{insert.model.table_name.parameterize}_values")
|
648
648
|
sql = +"INSERT #{insert.into} #{insert.values_list} AS #{values_alias}"
|
649
649
|
|
650
650
|
if insert.skip_duplicates?
|
@@ -18,6 +18,10 @@ module ActiveRecord
|
|
18
18
|
@adapter_class ||= ActiveRecord::ConnectionAdapters.resolve(adapter)
|
19
19
|
end
|
20
20
|
|
21
|
+
def inspect # :nodoc:
|
22
|
+
"#<#{self.class.name} env_name=#{@env_name} name=#{@name} adapter_class=#{adapter_class}>"
|
23
|
+
end
|
24
|
+
|
21
25
|
def new_connection
|
22
26
|
adapter_class.new(configuration_hash)
|
23
27
|
end
|
data/lib/active_record/enum.rb
CHANGED
@@ -226,7 +226,7 @@ module ActiveRecord
|
|
226
226
|
|
227
227
|
ActiveRecord.deprecator.warn(<<~MSG)
|
228
228
|
Defining enums with keyword arguments is deprecated and will be removed
|
229
|
-
in Rails
|
229
|
+
in Rails 8.0. Positional arguments should be used instead:
|
230
230
|
|
231
231
|
#{definitions.map { |name, values| "enum :#{name}, #{values}" }.join("\n")}
|
232
232
|
MSG
|
data/lib/active_record/errors.rb
CHANGED
@@ -130,9 +130,17 @@ module ActiveRecord
|
|
130
130
|
|
131
131
|
# Raised by {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!] and
|
132
132
|
# {ActiveRecord::Base.update_attribute!}[rdoc-ref:Persistence#update_attribute!]
|
133
|
-
# methods when a record
|
133
|
+
# methods when a record failed to validate or cannot be saved due to any of the
|
134
134
|
# <tt>before_*</tt> callbacks throwing +:abort+. See
|
135
|
-
# ActiveRecord::Callbacks for further details
|
135
|
+
# ActiveRecord::Callbacks for further details.
|
136
|
+
#
|
137
|
+
# class Product < ActiveRecord::Base
|
138
|
+
# before_save do
|
139
|
+
# throw :abort if price < 0
|
140
|
+
# end
|
141
|
+
# end
|
142
|
+
#
|
143
|
+
# Product.create! # => raises an ActiveRecord::RecordNotSaved
|
136
144
|
class RecordNotSaved < ActiveRecordError
|
137
145
|
attr_reader :record
|
138
146
|
|
@@ -143,15 +151,17 @@ module ActiveRecord
|
|
143
151
|
end
|
144
152
|
|
145
153
|
# Raised by {ActiveRecord::Base#destroy!}[rdoc-ref:Persistence#destroy!]
|
146
|
-
# when a
|
147
|
-
#
|
154
|
+
# when a record cannot be destroyed due to any of the
|
155
|
+
# <tt>before_destroy</tt> callbacks throwing +:abort+. See
|
156
|
+
# ActiveRecord::Callbacks for further details.
|
148
157
|
#
|
149
|
-
#
|
150
|
-
#
|
151
|
-
#
|
152
|
-
#
|
158
|
+
# class User < ActiveRecord::Base
|
159
|
+
# before_destroy do
|
160
|
+
# throw :abort if still_active?
|
161
|
+
# end
|
153
162
|
# end
|
154
163
|
#
|
164
|
+
# User.first.destroy! # => raises an ActiveRecord::RecordNotDestroyed
|
155
165
|
class RecordNotDestroyed < ActiveRecordError
|
156
166
|
attr_reader :record
|
157
167
|
|
@@ -583,3 +593,5 @@ module ActiveRecord
|
|
583
593
|
class DatabaseVersionError < ActiveRecordError
|
584
594
|
end
|
585
595
|
end
|
596
|
+
|
597
|
+
require "active_record/associations/errors"
|
@@ -187,7 +187,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|
187
187
|
if config.active_record.warn_on_records_fetched_greater_than
|
188
188
|
ActiveRecord.deprecator.warn <<~MSG.squish
|
189
189
|
`config.active_record.warn_on_records_fetched_greater_than` is deprecated and will be
|
190
|
-
removed in Rails
|
190
|
+
removed in Rails 8.0.
|
191
191
|
Please subscribe to `sql.active_record` notifications and access the row count field to
|
192
192
|
detect large result set sizes.
|
193
193
|
MSG
|
@@ -241,8 +241,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|
241
241
|
end
|
242
242
|
|
243
243
|
ActiveSupport.on_load(:active_record) do
|
244
|
-
|
245
|
-
configs = configs.except(
|
244
|
+
configs_used_in_other_initializers = configs.except(
|
246
245
|
:migration_error,
|
247
246
|
:database_selector,
|
248
247
|
:database_resolver,
|
@@ -259,7 +258,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|
259
258
|
:postgresql_adapter_decode_dates,
|
260
259
|
)
|
261
260
|
|
262
|
-
|
261
|
+
configs_used_in_other_initializers.each do |k, v|
|
263
262
|
next if k == :encryption
|
264
263
|
setter = "#{k}="
|
265
264
|
# Some existing initializers might rely on Active Record configuration
|
@@ -89,7 +89,7 @@ db_namespace = namespace :db do
|
|
89
89
|
task migrate: :load_config do
|
90
90
|
db_configs = ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env)
|
91
91
|
|
92
|
-
if db_configs.size == 1
|
92
|
+
if db_configs.size == 1 && db_configs.first.primary?
|
93
93
|
ActiveRecord::Tasks::DatabaseTasks.migrate
|
94
94
|
else
|
95
95
|
mapped_versions = ActiveRecord::Tasks::DatabaseTasks.db_configs_with_versions
|
@@ -604,7 +604,7 @@ module ActiveRecord
|
|
604
604
|
klass.attribute_types.fetch(name = result.columns[i]) do
|
605
605
|
join_dependencies ||= build_join_dependencies
|
606
606
|
lookup_cast_type_from_join_dependencies(name, join_dependencies) ||
|
607
|
-
result.column_types[
|
607
|
+
result.column_types[i] || Type.default_value
|
608
608
|
end
|
609
609
|
end
|
610
610
|
end
|
@@ -746,7 +746,7 @@ module ActiveRecord
|
|
746
746
|
VALID_UNSCOPING_VALUES = Set.new([:where, :select, :group, :order, :lock,
|
747
747
|
:limit, :offset, :joins, :left_outer_joins, :annotate,
|
748
748
|
:includes, :eager_load, :preload, :from, :readonly,
|
749
|
-
:having, :optimizer_hints])
|
749
|
+
:having, :optimizer_hints, :with])
|
750
750
|
|
751
751
|
# Removes an unwanted relation that is already defined on a chain of relations.
|
752
752
|
# This is useful when passing around chains of relations and would like to
|
@@ -2063,17 +2063,29 @@ module ActiveRecord
|
|
2063
2063
|
order_args.flat_map do |arg|
|
2064
2064
|
case arg
|
2065
2065
|
when String, Symbol
|
2066
|
-
arg
|
2066
|
+
extract_table_name_from(arg)
|
2067
2067
|
when Hash
|
2068
|
-
arg
|
2068
|
+
arg
|
2069
|
+
.map do |key, value|
|
2070
|
+
case value
|
2071
|
+
when Hash
|
2072
|
+
key.to_s
|
2073
|
+
else
|
2074
|
+
extract_table_name_from(key) if key.is_a?(String) || key.is_a?(Symbol)
|
2075
|
+
end
|
2076
|
+
end
|
2077
|
+
when Arel::Attribute
|
2078
|
+
arg.relation.name
|
2079
|
+
when Arel::Nodes::Ordering
|
2080
|
+
if arg.expr.is_a?(Arel::Attribute)
|
2081
|
+
arg.expr.relation.name
|
2082
|
+
end
|
2069
2083
|
end
|
2070
|
-
end.
|
2071
|
-
|
2072
|
-
|
2073
|
-
|
2074
|
-
|
2075
|
-
.flat_map { |e| e.map { |k, v| k if v.is_a?(Hash) } }
|
2076
|
-
.compact
|
2084
|
+
end.compact
|
2085
|
+
end
|
2086
|
+
|
2087
|
+
def extract_table_name_from(string)
|
2088
|
+
string.match(/^\W?(\w+)\W?\./) && $1
|
2077
2089
|
end
|
2078
2090
|
|
2079
2091
|
def order_column(field)
|
@@ -106,7 +106,16 @@ module ActiveRecord
|
|
106
106
|
|
107
107
|
|
108
108
|
# Returns a signed id that's generated using a preconfigured +ActiveSupport::MessageVerifier+ instance.
|
109
|
+
#
|
109
110
|
# This signed id is tamper proof, so it's safe to send in an email or otherwise share with the outside world.
|
111
|
+
# However, as with any message signed with a +ActiveSupport::MessageVerifier+,
|
112
|
+
# {the signed id is not encrypted}[link:classes/ActiveSupport/MessageVerifier.html#class-ActiveSupport::MessageVerifier-label-Signing+is+not+encryption].
|
113
|
+
# It's just encoded and protected against tampering.
|
114
|
+
#
|
115
|
+
# This means that the ID can be decoded by anyone; however, if tampered with (so to point to a different ID),
|
116
|
+
# the cryptographic signature will no longer match, and the signed id will be considered invalid and return nil
|
117
|
+
# when passed to +find_signed+ (or raise with +find_signed!+).
|
118
|
+
#
|
110
119
|
# It can furthermore be set to expire (the default is not to expire), and scoped down with a specific purpose.
|
111
120
|
# If the expiration date has been exceeded before +find_signed+ is called, the id won't find the designated
|
112
121
|
# record. If a purpose is set, this too must match.
|
@@ -446,7 +446,7 @@ module ActiveRecord
|
|
446
446
|
db_config_or_name.default_schema_cache_path(ActiveRecord::Tasks::DatabaseTasks.db_dir)
|
447
447
|
else
|
448
448
|
ActiveRecord.deprecator.warn(<<~MSG.squish)
|
449
|
-
Passing a database name to `cache_dump_filename` is deprecated and will be removed in Rails
|
449
|
+
Passing a database name to `cache_dump_filename` is deprecated and will be removed in Rails 8.0. Pass a
|
450
450
|
`ActiveRecord::DatabaseConfigurations::DatabaseConfig` object instead.
|
451
451
|
MSG
|
452
452
|
|
@@ -531,7 +531,7 @@ module ActiveRecord
|
|
531
531
|
def schema_cache_env
|
532
532
|
if ENV["SCHEMA_CACHE"]
|
533
533
|
ActiveRecord.deprecator.warn(<<~MSG.squish)
|
534
|
-
Setting `ENV["SCHEMA_CACHE"]` is deprecated and will be removed in Rails
|
534
|
+
Setting `ENV["SCHEMA_CACHE"]` is deprecated and will be removed in Rails 8.0.
|
535
535
|
Configure the `:schema_cache_path` in the database configuration instead.
|
536
536
|
MSG
|
537
537
|
|
@@ -96,6 +96,14 @@ module ActiveRecord
|
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
99
|
+
# Generic fixture accessor for fixture names that may conflict with other methods.
|
100
|
+
#
|
101
|
+
# assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
|
102
|
+
# assert_equal "Ruby on Rails", fixture(:web_sites, :rubyonrails).name
|
103
|
+
def fixture(fixture_set_name, *fixture_names)
|
104
|
+
active_record_fixture(fixture_set_name, *fixture_names)
|
105
|
+
end
|
106
|
+
|
99
107
|
private
|
100
108
|
def run_in_transaction?
|
101
109
|
use_transactional_tests &&
|
@@ -255,7 +263,7 @@ module ActiveRecord
|
|
255
263
|
|
256
264
|
def method_missing(method, ...)
|
257
265
|
if fixture_sets.key?(method.name)
|
258
|
-
|
266
|
+
active_record_fixture(method, ...)
|
259
267
|
else
|
260
268
|
super
|
261
269
|
end
|
@@ -269,14 +277,13 @@ module ActiveRecord
|
|
269
277
|
end
|
270
278
|
end
|
271
279
|
|
272
|
-
def
|
280
|
+
def active_record_fixture(fixture_set_name, *fixture_names)
|
273
281
|
if fs_name = fixture_sets[fixture_set_name.name]
|
274
282
|
access_fixture(fs_name, *fixture_names)
|
275
283
|
else
|
276
284
|
raise StandardError, "No fixture set named '#{fixture_set_name.inspect}'"
|
277
285
|
end
|
278
286
|
end
|
279
|
-
alias_method :fixture, :_active_record_fixture
|
280
287
|
|
281
288
|
def access_fixture(fs_name, *fixture_names)
|
282
289
|
force_reload = fixture_names.pop if fixture_names.last == true || fixture_names.last == :reload
|
@@ -162,7 +162,7 @@ module ActiveRecord
|
|
162
162
|
|
163
163
|
def max_updated_column_timestamp
|
164
164
|
timestamp_attributes_for_update_in_model
|
165
|
-
.filter_map { |attr| self[attr]
|
165
|
+
.filter_map { |attr| (v = self[attr]) && (v.is_a?(::Time) ? v : v.to_time) }
|
166
166
|
.max
|
167
167
|
end
|
168
168
|
|