dexkit 0.4.0 → 0.4.1
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/README.md +6 -1
- data/guides/llm/OPERATION.md +25 -1
- data/lib/dex/operation/transaction_adapter.rb +47 -1
- data/lib/dex/operation/transaction_wrapper.rb +11 -0
- data/lib/dex/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c6f6f437fc7f7726b58232820e6f1afd65c5adf674f51c4be07944a43d92a58b
|
|
4
|
+
data.tar.gz: 13046423e606ff6e000b6d557ce468efa94d0f13d8aa36530dc5ae69a688e097
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d48748c521d0e0c298ab78230797f7a22e45ae194fee6669f0f99669ea0ac1333b59158a8068211319694cbd073660f298babdb0a784b251444b579b898ba1c0
|
|
7
|
+
data.tar.gz: 4a574717aa7d2f5f5de9d192b86d7a6151d5b1a8cd2bba04c79e933cb2768e5c4d7cd05d91e22cf3ca9973cfca59467cd1a6972df8626ce90971ca9fec2a365d
|
data/README.md
CHANGED
|
@@ -18,7 +18,12 @@ class CreateUser < Dex::Operation
|
|
|
18
18
|
|
|
19
19
|
def perform
|
|
20
20
|
error!(:email_taken) if User.exists?(email: email)
|
|
21
|
-
|
|
21
|
+
|
|
22
|
+
user = User.create!(name: name, email: email)
|
|
23
|
+
|
|
24
|
+
after_commit { WelcomeMailer.with(user: user).deliver_later }
|
|
25
|
+
|
|
26
|
+
user
|
|
22
27
|
end
|
|
23
28
|
end
|
|
24
29
|
|
data/guides/llm/OPERATION.md
CHANGED
|
@@ -20,7 +20,12 @@ class CreateUser < Dex::Operation
|
|
|
20
20
|
def perform
|
|
21
21
|
error!(:invalid_email) unless email.include?("@")
|
|
22
22
|
error!(:email_taken) if User.exists?(email: email)
|
|
23
|
-
|
|
23
|
+
|
|
24
|
+
user = User.create!(email: email, name: name, role: role)
|
|
25
|
+
|
|
26
|
+
after_commit { WelcomeMailer.with(user: user).deliver_later }
|
|
27
|
+
|
|
28
|
+
user
|
|
24
29
|
end
|
|
25
30
|
end
|
|
26
31
|
```
|
|
@@ -255,6 +260,25 @@ transaction :mongoid # adapter override (default: auto-detect AR → Mongoid
|
|
|
255
260
|
|
|
256
261
|
Child classes can re-enable: `transaction true`.
|
|
257
262
|
|
|
263
|
+
### after_commit
|
|
264
|
+
|
|
265
|
+
Register blocks to run after the transaction commits. Use for side effects that should only happen on success (emails, webhooks, cache invalidation):
|
|
266
|
+
|
|
267
|
+
```ruby
|
|
268
|
+
def perform
|
|
269
|
+
user = User.create!(name: name, email: email)
|
|
270
|
+
after_commit { WelcomeMailer.with(user: user).deliver_later }
|
|
271
|
+
after_commit { Analytics.track(:user_created, user_id: user.id) }
|
|
272
|
+
user
|
|
273
|
+
end
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
On rollback (`error!` or exception), callbacks are discarded. When no transaction is open anywhere, executes immediately. Multiple blocks run in registration order.
|
|
277
|
+
|
|
278
|
+
**ActiveRecord:** fully nesting-aware — callbacks are deferred until the outermost transaction commits, even across nested operations or ambient `ActiveRecord::Base.transaction` blocks. Requires Rails 7.2+.
|
|
279
|
+
|
|
280
|
+
**Mongoid:** callbacks are deferred across nested Dex operations. Ambient `Mongoid.transaction` blocks opened outside Dex are not detected — callbacks will fire immediately in that case.
|
|
281
|
+
|
|
258
282
|
---
|
|
259
283
|
|
|
260
284
|
## Advisory Locking
|
|
@@ -32,17 +32,63 @@ module Dex
|
|
|
32
32
|
ActiveRecord::Base.transaction(&block)
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
+
def self.after_commit(&block)
|
|
36
|
+
unless defined?(ActiveRecord) && ActiveRecord.respond_to?(:after_all_transactions_commit)
|
|
37
|
+
raise LoadError, "after_commit requires Rails 7.2+"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
ActiveRecord.after_all_transactions_commit(&block)
|
|
41
|
+
end
|
|
42
|
+
|
|
35
43
|
def self.rollback_exception_class
|
|
36
44
|
ActiveRecord::Rollback
|
|
37
45
|
end
|
|
38
46
|
end
|
|
39
47
|
|
|
40
48
|
module MongoidAdapter
|
|
49
|
+
AFTER_COMMIT_KEY = :_dex_mongoid_after_commit
|
|
50
|
+
|
|
41
51
|
def self.wrap(&block)
|
|
42
52
|
unless defined?(Mongoid)
|
|
43
53
|
raise LoadError, "Mongoid is required for transactions"
|
|
44
54
|
end
|
|
45
|
-
|
|
55
|
+
|
|
56
|
+
outermost = !Thread.current[AFTER_COMMIT_KEY]
|
|
57
|
+
Thread.current[AFTER_COMMIT_KEY] ||= []
|
|
58
|
+
snapshot = Thread.current[AFTER_COMMIT_KEY].length
|
|
59
|
+
|
|
60
|
+
block_completed = false
|
|
61
|
+
result = Mongoid.transaction do
|
|
62
|
+
value = block.call
|
|
63
|
+
block_completed = true
|
|
64
|
+
value
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
if outermost && block_completed
|
|
68
|
+
Thread.current[AFTER_COMMIT_KEY].each(&:call)
|
|
69
|
+
elsif !block_completed
|
|
70
|
+
# Mongoid swallowed a Rollback exception — discard callbacks from this level
|
|
71
|
+
Thread.current[AFTER_COMMIT_KEY].slice!(snapshot..)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
result
|
|
75
|
+
rescue StandardError # rubocop:disable Style/RescueStandardError
|
|
76
|
+
Thread.current[AFTER_COMMIT_KEY]&.slice!(snapshot..) unless outermost
|
|
77
|
+
raise
|
|
78
|
+
ensure
|
|
79
|
+
Thread.current[AFTER_COMMIT_KEY] = nil if outermost
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# NOTE: Only detects transactions opened via MongoidAdapter.wrap (i.e. Dex operations).
|
|
83
|
+
# Ambient Mongoid.transaction blocks opened outside Dex are invisible here —
|
|
84
|
+
# the callback will fire immediately instead of deferring to the outer commit.
|
|
85
|
+
def self.after_commit(&block)
|
|
86
|
+
callbacks = Thread.current[AFTER_COMMIT_KEY]
|
|
87
|
+
if callbacks
|
|
88
|
+
callbacks << block
|
|
89
|
+
else
|
|
90
|
+
block.call
|
|
91
|
+
end
|
|
46
92
|
end
|
|
47
93
|
|
|
48
94
|
def self.rollback_exception_class
|
|
@@ -18,6 +18,17 @@ module Dex
|
|
|
18
18
|
result
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
def after_commit(&block)
|
|
22
|
+
raise ArgumentError, "after_commit requires a block" unless block
|
|
23
|
+
|
|
24
|
+
adapter = _transaction_adapter
|
|
25
|
+
if adapter
|
|
26
|
+
adapter.after_commit(&block)
|
|
27
|
+
else
|
|
28
|
+
block.call
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
21
32
|
TRANSACTION_KNOWN_ADAPTERS = %i[active_record mongoid].freeze
|
|
22
33
|
|
|
23
34
|
module ClassMethods
|
data/lib/dex/version.rb
CHANGED