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
@@ -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).
|