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
@@ -1,68 +1,132 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/digest"
|
4
|
+
|
3
5
|
module ActiveRecord
|
6
|
+
# Class specifies the interface to interact with the current transaction state.
|
7
|
+
#
|
8
|
+
# It can either map to an actual transaction/savepoint, or represent the
|
9
|
+
# absence of a transaction.
|
10
|
+
#
|
11
|
+
# == State
|
12
|
+
#
|
13
|
+
# We say that a transaction is _finalized_ when it wraps a real transaction
|
14
|
+
# that has been either committed or rolled back.
|
15
|
+
#
|
16
|
+
# A transaction is _open_ if it wraps a real transaction that is not finalized.
|
17
|
+
#
|
18
|
+
# On the other hand, a transaction is _closed_ when it is not open. That is,
|
19
|
+
# when it represents absence of transaction, or it wraps a real but finalized
|
20
|
+
# one.
|
21
|
+
#
|
22
|
+
# You can check whether a transaction is open or closed with the +open?+ and
|
23
|
+
# +closed?+ predicates:
|
24
|
+
#
|
25
|
+
# if Article.current_transaction.open?
|
26
|
+
# # We are inside a real and not finalized transaction.
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# Closed transactions are `blank?` too.
|
30
|
+
#
|
31
|
+
# == Callbacks
|
32
|
+
#
|
33
|
+
# After updating the database state, you may sometimes need to perform some extra work, or reflect these
|
34
|
+
# changes in a remote system like clearing or updating a cache:
|
35
|
+
#
|
36
|
+
# def publish_article(article)
|
37
|
+
# article.update!(published: true)
|
38
|
+
# NotificationService.article_published(article)
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# The above code works but has one important flaw, which is that it no longer works properly if called inside
|
42
|
+
# a transaction, as it will interact with the remote system before the changes are persisted:
|
43
|
+
#
|
44
|
+
# Article.transaction do
|
45
|
+
# article = create_article(article)
|
46
|
+
# publish_article(article)
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# The callbacks offered by ActiveRecord::Transaction allow to rewriting this method in a way that is compatible
|
50
|
+
# with transactions:
|
51
|
+
#
|
52
|
+
# def publish_article(article)
|
53
|
+
# article.update!(published: true)
|
54
|
+
# Article.current_transaction.after_commit do
|
55
|
+
# NotificationService.article_published(article)
|
56
|
+
# end
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# In the above example, if +publish_article+ is called inside a transaction, the callback will be invoked
|
60
|
+
# after the transaction is successfully committed, and if called outside a transaction, the callback will be invoked
|
61
|
+
# immediately.
|
62
|
+
#
|
63
|
+
# == Caveats
|
64
|
+
#
|
65
|
+
# When using after_commit callbacks, it is important to note that if the callback raises an error, the transaction
|
66
|
+
# won't be rolled back as it was already committed. Relying solely on these to synchronize state between multiple
|
67
|
+
# systems may lead to consistency issues.
|
4
68
|
class Transaction
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
@callback = callback
|
9
|
-
end
|
10
|
-
|
11
|
-
def before_commit
|
12
|
-
@callback.call if @event == :before_commit
|
13
|
-
end
|
14
|
-
|
15
|
-
def after_commit
|
16
|
-
@callback.call if @event == :after_commit
|
17
|
-
end
|
18
|
-
|
19
|
-
def after_rollback
|
20
|
-
@callback.call if @event == :after_rollback
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def initialize # :nodoc:
|
25
|
-
@callbacks = nil
|
69
|
+
def initialize(internal_transaction) # :nodoc:
|
70
|
+
@internal_transaction = internal_transaction
|
71
|
+
@uuid = nil
|
26
72
|
end
|
27
73
|
|
28
|
-
# Registers a block to be called
|
74
|
+
# Registers a block to be called after the transaction is fully committed.
|
29
75
|
#
|
30
|
-
# If there is no currently open transactions, the block is called
|
76
|
+
# If there is no currently open transactions, the block is called
|
77
|
+
# immediately, unless the transaction is finalized, in which case attempting
|
78
|
+
# to register the callback raises ActiveRecord::ActiveRecordError.
|
31
79
|
#
|
32
|
-
# If the
|
80
|
+
# If the transaction has a parent transaction, the callback is transferred to
|
33
81
|
# the parent when the current transaction commits, or dropped when the current transaction
|
34
82
|
# is rolled back. This operation is repeated until the outermost transaction is reached.
|
35
|
-
def before_commit(&block)
|
36
|
-
(@callbacks ||= []) << Callback.new(:before_commit, block)
|
37
|
-
end
|
38
|
-
|
39
|
-
# Registers a block to be called after the current transaction is fully committed.
|
40
83
|
#
|
41
|
-
# If
|
42
|
-
#
|
43
|
-
# If the current transaction has a parent transaction, the callback is transferred to
|
44
|
-
# the parent when the current transaction commits, or dropped when the current transaction
|
45
|
-
# is rolled back. This operation is repeated until the outermost transaction is reached.
|
84
|
+
# If the callback raises an error, the transaction remains committed.
|
46
85
|
def after_commit(&block)
|
47
|
-
|
86
|
+
if @internal_transaction.nil?
|
87
|
+
yield
|
88
|
+
else
|
89
|
+
@internal_transaction.after_commit(&block)
|
90
|
+
end
|
48
91
|
end
|
49
92
|
|
50
|
-
# Registers a block to be called after the
|
93
|
+
# Registers a block to be called after the transaction is rolled back.
|
51
94
|
#
|
52
|
-
# If there is no currently open transactions, the block is
|
95
|
+
# If there is no currently open transactions, the block is not called. But
|
96
|
+
# if the transaction is finalized, attempting to register the callback
|
97
|
+
# raises ActiveRecord::ActiveRecordError.
|
53
98
|
#
|
54
|
-
# If the
|
99
|
+
# If the transaction is successfully committed but has a parent
|
55
100
|
# transaction, the callback is automatically added to the parent transaction.
|
56
101
|
#
|
57
102
|
# If the entire chain of nested transactions are all successfully committed,
|
58
103
|
# the block is never called.
|
104
|
+
#
|
105
|
+
# If the transaction is already finalized, attempting to register a callback
|
106
|
+
# will raise ActiveRecord::ActiveRecordError.
|
59
107
|
def after_rollback(&block)
|
60
|
-
|
108
|
+
@internal_transaction&.after_rollback(&block)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Returns true if the transaction exists and isn't finalized yet.
|
112
|
+
def open?
|
113
|
+
!closed?
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returns true if the transaction doesn't exist or is finalized.
|
117
|
+
def closed?
|
118
|
+
@internal_transaction.nil? || @internal_transaction.state.finalized?
|
61
119
|
end
|
62
120
|
|
63
|
-
|
64
|
-
|
65
|
-
|
121
|
+
alias_method :blank?, :closed?
|
122
|
+
|
123
|
+
# Returns a UUID for this transaction or +nil+ if no transaction is open.
|
124
|
+
def uuid
|
125
|
+
if @internal_transaction
|
126
|
+
@uuid ||= Digest::UUID.uuid_v4
|
66
127
|
end
|
128
|
+
end
|
129
|
+
|
130
|
+
NULL_TRANSACTION = new(nil).freeze
|
67
131
|
end
|
68
132
|
end
|
@@ -188,6 +188,27 @@ module ActiveRecord
|
|
188
188
|
# #after_commit is a good spot to put in a hook to clearing a cache since clearing it from
|
189
189
|
# within a transaction could trigger the cache to be regenerated before the database is updated.
|
190
190
|
#
|
191
|
+
# ==== NOTE: Callbacks are deduplicated per callback by filter.
|
192
|
+
#
|
193
|
+
# Trying to define multiple callbacks with the same filter will result in a single callback being run.
|
194
|
+
#
|
195
|
+
# For example:
|
196
|
+
#
|
197
|
+
# after_commit :do_something
|
198
|
+
# after_commit :do_something # only the last one will be called
|
199
|
+
#
|
200
|
+
# This applies to all variations of <tt>after_*_commit</tt> callbacks as well.
|
201
|
+
#
|
202
|
+
# after_commit :do_something
|
203
|
+
# after_create_commit :do_something
|
204
|
+
# after_save_commit :do_something
|
205
|
+
#
|
206
|
+
# It is recommended to use the +on:+ option to specify when the callback should be run.
|
207
|
+
#
|
208
|
+
# after_commit :do_something, on: [:create, :update]
|
209
|
+
#
|
210
|
+
# This is equivalent to using +after_create_commit+ and +after_update_commit+, but will not be deduplicated.
|
211
|
+
#
|
191
212
|
# === Caveats
|
192
213
|
#
|
193
214
|
# If you're on MySQL, then do not use Data Definition Language (DDL) operations in nested
|
@@ -214,9 +235,15 @@ module ActiveRecord
|
|
214
235
|
end
|
215
236
|
end
|
216
237
|
|
217
|
-
# Returns the current transaction
|
238
|
+
# Returns a representation of the current transaction state,
|
239
|
+
# which can be a top level transaction, a savepoint, or the absence of a transaction.
|
240
|
+
#
|
241
|
+
# An object is always returned, whether or not a transaction is currently active.
|
242
|
+
# To check if a transaction was opened, use <tt>current_transaction.open?</tt>.
|
243
|
+
#
|
244
|
+
# See the ActiveRecord::Transaction documentation for detailed behavior.
|
218
245
|
def current_transaction
|
219
|
-
connection_pool.active_connection&.current_transaction ||
|
246
|
+
connection_pool.active_connection&.current_transaction&.user_transaction || Transaction::NULL_TRANSACTION
|
220
247
|
end
|
221
248
|
|
222
249
|
def before_commit(*args, &block) # :nodoc:
|
data/lib/active_record.rb
CHANGED
@@ -290,7 +290,7 @@ module ActiveRecord
|
|
290
290
|
# with the global thread pool async query executor.
|
291
291
|
def self.global_executor_concurrency=(global_executor_concurrency)
|
292
292
|
if self.async_query_executor.nil? || self.async_query_executor == :multi_thread_pool
|
293
|
-
raise ArgumentError, "`global_executor_concurrency` cannot be set when
|
293
|
+
raise ArgumentError, "`global_executor_concurrency` cannot be set when the executor is nil or set to `:multi_thread_pool`. For multiple thread pools, please set the concurrency in your database configuration."
|
294
294
|
end
|
295
295
|
|
296
296
|
@global_executor_concurrency = global_executor_concurrency
|
@@ -347,14 +347,14 @@ module ActiveRecord
|
|
347
347
|
def self.commit_transaction_on_non_local_return
|
348
348
|
ActiveRecord.deprecator.warn <<-WARNING.squish
|
349
349
|
`Rails.application.config.active_record.commit_transaction_on_non_local_return`
|
350
|
-
is deprecated and will be removed in Rails
|
350
|
+
is deprecated and will be removed in Rails 8.0.
|
351
351
|
WARNING
|
352
352
|
end
|
353
353
|
|
354
354
|
def self.commit_transaction_on_non_local_return=(value)
|
355
355
|
ActiveRecord.deprecator.warn <<-WARNING.squish
|
356
356
|
`Rails.application.config.active_record.commit_transaction_on_non_local_return`
|
357
|
-
is deprecated and will be removed in Rails
|
357
|
+
is deprecated and will be removed in Rails 8.0.
|
358
358
|
WARNING
|
359
359
|
end
|
360
360
|
|
@@ -447,14 +447,14 @@ module ActiveRecord
|
|
447
447
|
def self.allow_deprecated_singular_associations_name
|
448
448
|
ActiveRecord.deprecator.warn <<-WARNING.squish
|
449
449
|
`Rails.application.config.active_record.allow_deprecated_singular_associations_name`
|
450
|
-
is deprecated and will be removed in Rails
|
450
|
+
is deprecated and will be removed in Rails 8.0.
|
451
451
|
WARNING
|
452
452
|
end
|
453
453
|
|
454
454
|
def self.allow_deprecated_singular_associations_name=(value)
|
455
455
|
ActiveRecord.deprecator.warn <<-WARNING.squish
|
456
456
|
`Rails.application.config.active_record.allow_deprecated_singular_associations_name`
|
457
|
-
is deprecated and will be removed in Rails
|
457
|
+
is deprecated and will be removed in Rails 8.0.
|
458
458
|
WARNING
|
459
459
|
end
|
460
460
|
|
@@ -12,7 +12,10 @@ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Mi
|
|
12
12
|
t.<%= attribute.type %> :<%= attribute.name %><%= attribute.inject_options %>
|
13
13
|
<% end -%>
|
14
14
|
<% end -%>
|
15
|
-
<%
|
15
|
+
<% unless attributes.empty? -%>
|
16
|
+
|
17
|
+
<% end -%>
|
18
|
+
<% if options[:timestamps] -%>
|
16
19
|
t.timestamps
|
17
20
|
<% end -%>
|
18
21
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 7.2.0.
|
4
|
+
version: 7.2.0.beta3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Heinemeier Hansson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-07-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 7.2.0.
|
19
|
+
version: 7.2.0.beta3
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 7.2.0.
|
26
|
+
version: 7.2.0.beta3
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activemodel
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - '='
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 7.2.0.
|
33
|
+
version: 7.2.0.beta3
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - '='
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 7.2.0.
|
40
|
+
version: 7.2.0.beta3
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: timeout
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -85,6 +85,7 @@ files:
|
|
85
85
|
- lib/active_record/associations/collection_association.rb
|
86
86
|
- lib/active_record/associations/collection_proxy.rb
|
87
87
|
- lib/active_record/associations/disable_joins_association_scope.rb
|
88
|
+
- lib/active_record/associations/errors.rb
|
88
89
|
- lib/active_record/associations/foreign_association.rb
|
89
90
|
- lib/active_record/associations/has_many_association.rb
|
90
91
|
- lib/active_record/associations/has_many_through_association.rb
|
@@ -475,10 +476,10 @@ licenses:
|
|
475
476
|
- MIT
|
476
477
|
metadata:
|
477
478
|
bug_tracker_uri: https://github.com/rails/rails/issues
|
478
|
-
changelog_uri: https://github.com/rails/rails/blob/v7.2.0.
|
479
|
-
documentation_uri: https://api.rubyonrails.org/v7.2.0.
|
479
|
+
changelog_uri: https://github.com/rails/rails/blob/v7.2.0.beta3/activerecord/CHANGELOG.md
|
480
|
+
documentation_uri: https://api.rubyonrails.org/v7.2.0.beta3/
|
480
481
|
mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
|
481
|
-
source_code_uri: https://github.com/rails/rails/tree/v7.2.0.
|
482
|
+
source_code_uri: https://github.com/rails/rails/tree/v7.2.0.beta3/activerecord
|
482
483
|
rubygems_mfa_required: 'true'
|
483
484
|
post_install_message:
|
484
485
|
rdoc_options:
|
@@ -497,7 +498,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
497
498
|
- !ruby/object:Gem::Version
|
498
499
|
version: '0'
|
499
500
|
requirements: []
|
500
|
-
rubygems_version: 3.5.
|
501
|
+
rubygems_version: 3.5.11
|
501
502
|
signing_key:
|
502
503
|
specification_version: 4
|
503
504
|
summary: Object-relational mapper framework (part of Rails).
|