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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bc994e44218cf9063af69ad1d01929d86b91fe35b6f30769dd068832fb04fc37
4
- data.tar.gz: b4a078fb25780824cd2a876f5d668196ba2f3a2094ce63a1b9731dfeab1c7aa6
3
+ metadata.gz: c6f6f437fc7f7726b58232820e6f1afd65c5adf674f51c4be07944a43d92a58b
4
+ data.tar.gz: 13046423e606ff6e000b6d557ce468efa94d0f13d8aa36530dc5ae69a688e097
5
5
  SHA512:
6
- metadata.gz: b4bb8dbbe3c9acd66e5c3ed6a6b203408f66c7455544c36de4e60e755a9cfa9a2fa3405a39648e77e5bd42b2593a053d5d517e16ce1cd5236225083bd24891b3
7
- data.tar.gz: c050bd2dc477e19efcebbe6bfa6fe7d46dbfa4dea5367f38c098b6c82750439211d1b4a28224c21fd66e98c8971b9532f341f4e17b4d2e836383470ccf778913
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
- User.create!(name: name, email: email)
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
 
@@ -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
- User.create!(email: email, name: name, role: role)
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
- Mongoid.transaction(&block)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dex
4
- VERSION = "0.4.0"
4
+ VERSION = "0.4.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dexkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jacek Galanciak