Class: RuboCop::Cop::Rails::TransactionExitStatement

Inherits:
Base
  • Object
show all
Defined in:
lib/rubocop/cop/rails/transaction_exit_statement.rb

Overview

Checks for the use of exit statements (namely ‘return`, `break` and `throw`) in transactions. This is due to the eventual unexpected behavior when using ActiveRecord >= 7, where transactions exited using these statements are being rollbacked rather than committed (pre ActiveRecord 7 behavior).

As alternatives, it would be more intuitive to explicitly raise an error when rollback is desired, and to use ‘next` when commit is desired.

If you are defining custom transaction methods, you can configure it with ‘TransactionMethods`.

Examples:

# bad
ApplicationRecord.transaction do
  return if user.active?
end

# bad
ApplicationRecord.transaction do
  break if user.active?
end

# bad
ApplicationRecord.transaction do
  throw if user.active?
end

# bad, as `with_lock` implicitly opens a transaction too
user.with_lock do
  throw if user.active?
end

# bad, as `with_lock` implicitly opens a transaction too
ApplicationRecord.with_lock do
  break if user.active?
end

# good
ApplicationRecord.transaction do
  # Rollback
  raise "User is active" if user.active?
end

# good
ApplicationRecord.transaction do
  # Commit
  next if user.active?
end

TransactionMethods: [“custom_transaction”]

# bad
CustomModel.custom_transaction do
  return if user.active?
end

Constant Summary collapse

MSG =
'Exit statement `%<statement>s` is not allowed. Use `raise` (rollback) or `next` (commit).'
BUILT_IN_TRANSACTION_METHODS =
%i[transaction with_lock].freeze

Instance Method Summary collapse

Instance Method Details

#on_send(node) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/rubocop/cop/rails/transaction_exit_statement.rb', line 78

def on_send(node)
  return unless in_transaction_block?(node)

  exit_statements(node.parent.body).each do |statement_node|
    next if statement_node.break_type? && nested_block?(statement_node)

    statement = statement(statement_node)
    message = format(MSG, statement: statement)

    add_offense(statement_node, message: message)
  end
end