Module: AfterCommitEverywhere

Defined in:
lib/after_commit_everywhere.rb,
lib/after_commit_everywhere/wrap.rb,
lib/after_commit_everywhere/version.rb

Overview

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)

Defined Under Namespace

Classes: NotInTransaction, Wrap

Constant Summary collapse

VERSION =
"0.1.5"

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.in_transaction?(connection = ActiveRecord::Base.connection) ⇒ Boolean

Helper method to determine whether we’re currently in transaction or not

Returns:

  • (Boolean)


93
94
95
96
# File 'lib/after_commit_everywhere.rb', line 93

def in_transaction?(connection = ActiveRecord::Base.connection)
  # service transactions (tests and database_cleaner) are not joinable
  connection.transaction_open? && connection.current_transaction.joinable?
end

.register_callback(connection:, name:, no_tx_action:, callback:) ⇒ Object

Raises:

  • (ArgumentError)


74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/after_commit_everywhere.rb', line 74

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

Instance Method Details

#after_commit(connection: ActiveRecord::Base.connection, &callback) ⇒ Object

Runs callback after successful commit of outermost transaction for database connection.

If called outside transaction it will execute callback immediately.

Parameters:

  • connection (ActiveRecord::ConnectionAdapters::AbstractAdapter) (defaults to: ActiveRecord::Base.connection)
  • callback (#call)

    Callback to be executed

Returns:

  • void



23
24
25
26
27
28
29
30
# File 'lib/after_commit_everywhere.rb', line 23

def after_commit(connection: ActiveRecord::Base.connection, &callback)
  AfterCommitEverywhere.register_callback(
    connection: connection,
    name: __method__,
    callback: callback,
    no_tx_action: :execute,
  )
end

#after_rollback(connection: ActiveRecord::Base.connection, &callback) ⇒ Object

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

Parameters:

  • connection (ActiveRecord::ConnectionAdapters::AbstractAdapter) (defaults to: ActiveRecord::Base.connection)
  • callback (#call)

    Callback to be executed

Returns:

  • void

Raises:



64
65
66
67
68
69
70
71
# File 'lib/after_commit_everywhere.rb', line 64

def after_rollback(connection: ActiveRecord::Base.connection, &callback)
  AfterCommitEverywhere.register_callback(
    connection: connection,
    name: __method__,
    callback: callback,
    no_tx_action: :exception,
  )
end

#before_commit(connection: ActiveRecord::Base.connection, &callback) ⇒ Object

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

Parameters:

  • connection (ActiveRecord::ConnectionAdapters::AbstractAdapter) (defaults to: ActiveRecord::Base.connection)
  • callback (#call)

    Callback to be executed

Returns:

  • void



41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/after_commit_everywhere.rb', line 41

def before_commit(connection: ActiveRecord::Base.connection, &callback)
  if ActiveRecord::VERSION::MAJOR < 5
    raise NotImplementedError, "#{__method__} works only with Rails 5.0+"
  end

  AfterCommitEverywhere.register_callback(
    connection: connection,
    name: __method__,
    callback: callback,
    no_tx_action: :warn_and_execute,
  )
end