module AfterCommitEverywhere
Module allowing to use ActiveRecord transactional callbacks outside of ActiveRecord models, literally everywhere in your application.
Include it to your classes (e.g. your base service object class or whatever)
Constants
- VERSION
Public Class Methods
Runs callback
after successful commit of outermost transaction for database connection
.
If called outside transaction it will execute callback immediately.
@param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] @param callback [#call] Callback to be executed @return void
# File lib/after_commit_everywhere.rb, line 28 def after_commit(connection: ActiveRecord::Base.connection, &callback) register_callback( connection: connection, name: __method__, callback: callback, no_tx_action: :execute, ) end
Runs callback
after rolling back of transaction or savepoint (if declared in nested transaction) for database connection
.
Caveat: do not raise ActivRecord::Rollback
in nested transaction block! See api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html#module-ActiveRecord::Transactions::ClassMethods-label-Nested+transactions
@param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] @param callback [#call] Callback to be executed @return void @raise [NotInTransaction] if called outside transaction.
# File lib/after_commit_everywhere.rb, line 69 def after_rollback(connection: ActiveRecord::Base.connection, &callback) register_callback( connection: connection, name: __method__, callback: callback, no_tx_action: :exception, ) end
Runs callback
before committing of outermost transaction for connection
.
If called outside transaction it will execute callback immediately.
Available only since Ruby on Rails 5.0. See github.com/rails/rails/pull/18936
@param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] @param callback [#call] Callback to be executed @return void
# File lib/after_commit_everywhere.rb, line 46 def before_commit(connection: ActiveRecord::Base.connection, &callback) if ActiveRecord::VERSION::MAJOR < 5 raise NotImplementedError, "#{__method__} works only with Rails 5.0+" end register_callback( connection: connection, name: __method__, callback: callback, no_tx_action: :warn_and_execute, ) end
Helper method to determine whether we're currently in transaction or not
# File lib/after_commit_everywhere.rb, line 98 def in_transaction?(connection = ActiveRecord::Base.connection) # service transactions (tests and database_cleaner) are not joinable connection.transaction_open? && connection.current_transaction.joinable? end
@api private
# File lib/after_commit_everywhere.rb, line 79 def register_callback(connection:, name:, no_tx_action:, callback:) raise ArgumentError, "Provide callback to #{name}" unless callback unless in_transaction?(connection) case no_tx_action when :warn_and_execute warn "#{name}: No transaction open. Executing callback immediately." return callback.call when :execute return callback.call when :exception raise NotInTransaction, "#{name} is useless outside transaction" end end wrap = Wrap.new(connection: connection, "#{name}": callback) connection.add_transaction_record(wrap) end