activerecord 7.2.0.beta1 → 7.2.0.beta3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|