class RuboCop::Cop::Performance::CompareWithBlock

This cop identifies places where `sort { |a, b| a.foo <=> b.foo }` can be replaced by `sort_by(&:foo)`. This cop also checks `max` and `min` methods.

@example

# bad
array.sort { |a, b| a.foo <=> b.foo }
array.max { |a, b| a.foo <=> b.foo }
array.min { |a, b| a.foo <=> b.foo }
array.sort { |a, b| a[:foo] <=> b[:foo] }

# good
array.sort_by(&:foo)
array.sort_by { |v| v.foo }
array.sort_by do |var|
  var.foo
end
array.max_by(&:foo)
array.min_by(&:foo)
array.sort_by { |a| a[:foo] }

Constants

MSG

Public Instance Methods

autocorrect(node) click to toggle source
# File lib/rubocop/cop/performance/compare_with_block.rb, line 63
def autocorrect(node)
  lambda do |corrector|
    send, var_a, var_b, body = compare?(node)
    method, arg, = replaceable_body?(body, var_a, var_b)
    replacement =
      if method == :[]
        "#{send.method_name}_by { |a| a[#{arg.first.source}] }"
      else
        "#{send.method_name}_by(&:#{method})"
      end
    corrector.replace(compare_range(send, node),
                      replacement)
  end
end
on_block(node) click to toggle source
# File lib/rubocop/cop/performance/compare_with_block.rb, line 47
def on_block(node)
  compare?(node) do |send, var_a, var_b, body|
    replaceable_body?(body, var_a, var_b) do |method, args_a, args_b|
      return unless slow_compare?(method, args_a, args_b)

      range = compare_range(send, node)

      add_offense(
        node,
        location: range,
        message: message(send, method, var_a, var_b, args_a)
      )
    end
  end
end

Private Instance Methods

compare_range(send, node) click to toggle source

rubocop:enable Metrics/MethodLength

# File lib/rubocop/cop/performance/compare_with_block.rb, line 116
def compare_range(send, node)
  range_between(send.loc.selector.begin_pos, node.loc.end.end_pos)
end
message(send, method, var_a, var_b, args) click to toggle source

rubocop:disable Metrics/MethodLength

# File lib/rubocop/cop/performance/compare_with_block.rb, line 95
def message(send, method, var_a, var_b, args)
  compare_method = send.method_name
  if method == :[]
    key = args.first
    instead = " { |a| a[#{key.source}] }"
    str_a = "#{var_a}[#{key.source}]"
    str_b = "#{var_b}[#{key.source}]"
  else
    instead = "(&:#{method})"
    str_a = "#{var_a}.#{method}"
    str_b = "#{var_b}.#{method}"
  end
  format(MSG, compare_method: compare_method,
              instead: instead,
              var_a: var_a,
              var_b: var_b,
              str_a: str_a,
              str_b: str_b)
end
slow_compare?(method, args_a, args_b) click to toggle source
# File lib/rubocop/cop/performance/compare_with_block.rb, line 80
def slow_compare?(method, args_a, args_b)
  return false unless args_a == args_b

  if method == :[]
    return false unless args_a.size == 1

    key = args_a.first
    return false unless %i[sym str int].include?(key.type)
  else
    return false unless args_a.empty?
  end
  true
end