Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support prepending callbacks #30

Merged
merged 3 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ Will be executed right after transaction in which it have been declared was roll

If called outside transaction will raise an exception!

Please keep in mind ActiveRecord's [limitations for rolling back nested transactions](http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html#module-ActiveRecord::Transactions::ClassMethods-label-Nested+transactions). See [`in_transaction`](#in_transaction) for a workaround to this limitation.
Please keep in mind ActiveRecord's [limitations for rolling back nested transactions](http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html#module-ActiveRecord::Transactions::ClassMethods-label-Nested+transactions). See [`in_transaction`](#in_transaction) for a workaround to this limitation.

### Available helper methods

Expand Down Expand Up @@ -139,7 +139,7 @@ class ServiceObjectBtw
end
```

Our service object can run its database operations safely when run in isolation.
Our service object can run its database operations safely when run in isolation.

```rb
ServiceObjectBtw.new.call # This opens a new #transaction block
Expand Down Expand Up @@ -195,6 +195,8 @@ end
- `:warn_and_execute` to print warning and execute immediately
- `:raise` to raise an exception instead of executing

- `prepend` puts the callback at the head of the callback chain, instead of at the end.

### FAQ

#### Does it works with transactional_test or DatabaseCleaner
Expand Down
16 changes: 13 additions & 3 deletions lib/after_commit_everywhere.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@ class << self
# @param callback [#call] Callback to be executed
# @return void
def after_commit(
prepend: false,
connection: nil,
without_tx: EXECUTE,
&callback
)
register_callback(
prepend: prepend,
connection: connection,
name: __method__,
callback: callback,
Expand All @@ -64,6 +66,7 @@ def after_commit(
# @param callback [#call] Callback to be executed
# @return void
def before_commit(
prepend: false,
connection: nil,
without_tx: WARN_AND_EXECUTE,
&callback
Expand All @@ -73,6 +76,7 @@ def before_commit(
end

register_callback(
prepend: prepend,
connection: connection,
name: __method__,
callback: callback,
Expand All @@ -90,8 +94,9 @@ def before_commit(
# @param callback [#call] Callback to be executed
# @return void
# @raise [NotInTransaction] if called outside transaction.
def after_rollback(connection: nil, &callback)
def after_rollback(prepend: false, connection: nil, &callback)
register_callback(
prepend: prepend,
connection: connection,
name: __method__,
callback: callback,
Expand All @@ -100,7 +105,7 @@ def after_rollback(connection: nil, &callback)
end

# @api private
def register_callback(connection: nil, name:, without_tx:, callback:)
def register_callback(prepend:, connection: nil, name:, without_tx:, callback:)
raise ArgumentError, "Provide callback to #{name}" unless callback

unless in_transaction?(connection)
Expand All @@ -119,7 +124,12 @@ def register_callback(connection: nil, name:, without_tx:, callback:)

connection ||= default_connection
wrap = Wrap.new(connection: connection, "#{name}": callback)
connection.add_transaction_record(wrap)

if prepend
connection.current_transaction.instance_variable_get(:@records).unshift(wrap)
else
connection.add_transaction_record(wrap)
end
end

# Helper method to determine whether we're currently in transaction or not
Expand Down
28 changes: 28 additions & 0 deletions spec/after_commit_everywhere_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,34 @@
end

context "within transaction" do
context 'when prepend is true' do
let(:handler_1) { spy("handler_1") }
let(:handler_2) { spy("handler_2") }

it 'executes prepended callback first' do
ActiveRecord::Base.transaction do
example_class.new.after_commit { handler_1.call }
example_class.new.after_commit(prepend: true) { handler_2.call }
end
expect(handler_2).to have_received(:call).ordered
expect(handler_1).to have_received(:call).ordered
end
end

context 'when prepend is not specified' do
let(:handler_1) { spy("handler_1") }
let(:handler_2) { spy("handler_2") }

it 'executes callbacks in the order they were defined' do
ActiveRecord::Base.transaction do
example_class.new.after_commit { handler_1.call }
example_class.new.after_commit { handler_2.call }
end
expect(handler_1).to have_received(:call).ordered
expect(handler_2).to have_received(:call).ordered
end
end

it "executes code only after commit" do
ActiveRecord::Base.transaction do
subject
Expand Down
Loading