Class: RuboCop::Cop::DevDoc::Rails::BangSaveInTransaction

Inherits:
Base
  • Object
show all
Extended by:
AutoCorrector
Defined in:
lib/rubocop/cop/dev_doc/rails/bang_save_in_transaction.rb

Overview

Flag bare (return-value-discarded) ‘save`/`update`/`create` calls inside a `transaction` block.

## Rationale Inside a transaction a non-bang ‘save` / `update` / `create` whose return value is discarded is almost always a bug. The transaction does not roll back on a `false` return — execution continues as if the write succeeded, silently producing inconsistent data.

Checking the return value (as a condition or assignment) is allowed, as is the bang form. Only the bare-statement form is flagged.

❌ silent failure — transaction does not roll back
ApplicationRecord.transaction do
  @order.save
  create_child_records(...)
end

✔️ return value gated — ok
ApplicationRecord.transaction do
  if @order.save
    create_child_records(...)
  end
end

✔️ bang method — raises on failure, rolls back
ApplicationRecord.transaction do
  @order.save!
  create_child_records(...)
end

Examples:

# bad
ApplicationRecord.transaction do
  @order.save
  create_child_records(params)
end

# good
ApplicationRecord.transaction do
  if @order.save
    create_child_records(params)
  end
end

# good
ApplicationRecord.transaction do
  result = @order.save
  create_child_records(params) if result
end

# good
ApplicationRecord.transaction do
  @order.save!
  create_child_records(params)
end

Constant Summary collapse

MSG =
'Use `%<bang>s` inside a `transaction` block, or check its return value. ' \
'A non-bang call whose return value is discarded does not roll back the transaction on failure.'.freeze
FLAGGED_METHODS =
%i[save update create].freeze
CONSUMING_PARENT_TYPES =

Node types whose parent always means the return value is consumed.

%i[
  and or return
  send csend
  lvasgn ivasgn cvasgn gvasgn casgn masgn
  array hash pair
].freeze

Instance Method Summary collapse

Instance Method Details

#on_send(node) ⇒ Object



77
78
79
80
81
82
83
84
85
86
# File 'lib/rubocop/cop/dev_doc/rails/bang_save_in_transaction.rb', line 77

def on_send(node)
  return unless FLAGGED_METHODS.include?(node.method_name)
  return unless inside_transaction?(node)
  return if return_value_used?(node)

  bang = :"#{node.method_name}!"
  add_offense(node.loc.selector, message: format(MSG, bang: bang)) do |corrector|
    corrector.replace(node.loc.selector, bang.to_s)
  end
end