class RuboCop::Cop::Rails::ActiveRecordCallbacksOrder

This cop checks that Active Record callbacks are declared in the order in which they will be executed.

@example

# bad
class Person < ApplicationRecord
  after_commit :after_commit_callback
  before_validation :before_validation_callback
end

# good
class Person < ApplicationRecord
  before_validation :before_validation_callback
  after_commit :after_commit_callback
end

Constants

CALLBACKS_IN_ORDER
CALLBACKS_ORDER_MAP
MSG

Public Instance Methods

on_class(class_node) click to toggle source
# File lib/rubocop/cop/rails/active_record_callbacks_order.rb, line 51
def on_class(class_node)
  previous_index = -1
  previous_callback = nil

  defined_callbacks(class_node).each do |node|
    callback = node.method_name
    index = CALLBACKS_ORDER_MAP[callback]

    if index < previous_index
      message = format(MSG, current: callback, previous: previous_callback)
      add_offense(node, message: message) do |corrector|
        autocorrect(corrector, node)
      end
    end
    previous_index = index
    previous_callback = callback
  end
end

Private Instance Methods

autocorrect(corrector, node) click to toggle source

Autocorrect by swapping between two nodes autocorrecting them

# File lib/rubocop/cop/rails/active_record_callbacks_order.rb, line 73
def autocorrect(corrector, node)
  previous = node.left_siblings.reverse_each.find do |sibling|
    callback?(sibling)
  end

  current_range = source_range_with_comment(node)
  previous_range = source_range_with_comment(previous)

  corrector.insert_before(previous_range, current_range.source)
  corrector.remove(current_range)
end
begin_pos_with_comment(node) click to toggle source
# File lib/rubocop/cop/rails/active_record_callbacks_order.rb, line 111
def begin_pos_with_comment(node)
  annotation_line = node.first_line - 1
  first_comment = nil

  processed_source.comments_before_line(annotation_line)
                  .reverse_each do |comment|
    if comment.location.line == annotation_line && !inline_comment?(comment)
      first_comment = comment
      annotation_line -= 1
    end
  end

  start_line_position(first_comment || node)
end
buffer() click to toggle source
# File lib/rubocop/cop/rails/active_record_callbacks_order.rb, line 134
def buffer
  processed_source.buffer
end
callback?(node) click to toggle source
# File lib/rubocop/cop/rails/active_record_callbacks_order.rb, line 95
def callback?(node)
  node.send_type? && CALLBACKS_ORDER_MAP.key?(node.method_name)
end
defined_callbacks(class_node) click to toggle source
# File lib/rubocop/cop/rails/active_record_callbacks_order.rb, line 85
def defined_callbacks(class_node)
  class_def = class_node.body

  if class_def
    class_def.each_child_node.select { |c| callback?(c) }
  else
    []
  end
end
end_position_for(node) click to toggle source
# File lib/rubocop/cop/rails/active_record_callbacks_order.rb, line 106
def end_position_for(node)
  end_line = buffer.line_for_position(node.loc.expression.end_pos)
  buffer.line_range(end_line).end_pos
end
inline_comment?(comment) click to toggle source
# File lib/rubocop/cop/rails/active_record_callbacks_order.rb, line 126
def inline_comment?(comment)
  !comment_line?(comment.loc.expression.source_line)
end
source_range_with_comment(node) click to toggle source
# File lib/rubocop/cop/rails/active_record_callbacks_order.rb, line 99
def source_range_with_comment(node)
  begin_pos = begin_pos_with_comment(node)
  end_pos = end_position_for(node)

  Parser::Source::Range.new(buffer, begin_pos, end_pos)
end
start_line_position(node) click to toggle source
# File lib/rubocop/cop/rails/active_record_callbacks_order.rb, line 130
def start_line_position(node)
  buffer.line_range(node.loc.line).begin_pos - 1
end