class RuboCop::Cop::Style::SymbolProc

Use symbols as procs when possible.

If you prefer a style that allows block for method with arguments, please set ‘true` to `AllowMethodsWithArguments`. respond_to , and `define_method?` methods are allowed by default. These are customizable with `AllowedMethods` option.

@safety

This cop is unsafe because there is a difference that a `Proc`
generated from `Symbol#to_proc` behaves as a lambda, while
a `Proc` generated from a block does not.
For example, a lambda will raise an `ArgumentError` if the
number of arguments is wrong, but a non-lambda `Proc` will not.

For example:

[source,ruby]
----
class Foo
  def bar
    :bar
  end
end

def call(options = {}, &block)
  block.call(Foo.new, options)
end

call { |x| x.bar }
#=> :bar
call(&:bar)
# ArgumentError: wrong number of arguments (given 1, expected 0)
----

@example

# bad
something.map { |s| s.upcase }
something.map { _1.upcase }

# good
something.map(&:upcase)

@example AllowMethodsWithArguments: false (default)

# bad
something.do_something(foo) { |o| o.bar }

# good
something.do_something(foo, &:bar)

@example AllowMethodsWithArguments: true

# good
something.do_something(foo) { |o| o.bar }

@example AllowComments: false (default)

# bad
something.do_something do |s| # some comment
  # some comment
  s.upcase # some comment
  # some comment
end

@example AllowComments: true

# good  - if there are comment in either position
something.do_something do |s| # some comment
  # some comment
  s.upcase # some comment
  # some comment
end

@example AllowedMethods: [respond_to, define_method] (default)

# good
respond_to { |foo| foo.bar }
define_method(:foo) { |foo| foo.bar }

@example AllowedPatterns: [] (default)

# bad
something.map { |s| s.upcase }

@example AllowedPatterns: [‘map’] (default)

# good
something.map { |s| s.upcase }

Constants

MSG
SUPER_TYPES

Public Class Methods

autocorrect_incompatible_with() click to toggle source
# File lib/rubocop/cop/style/symbol_proc.rb, line 113
def self.autocorrect_incompatible_with
  [Layout::SpaceBeforeBlockBraces]
end

Public Instance Methods

destructuring_block_argument?(argument_node) click to toggle source
# File lib/rubocop/cop/style/symbol_proc.rb, line 138
def destructuring_block_argument?(argument_node)
  argument_node.one? && argument_node.source.include?(',')
end
on_block(node) click to toggle source

rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity

# File lib/rubocop/cop/style/symbol_proc.rb, line 118
def on_block(node)
  symbol_proc?(node) do |dispatch_node, arguments_node, method_name|
    # TODO: Rails-specific handling that we should probably make
    # configurable - https://github.com/rubocop/rubocop/issues/1485
    # we should allow lambdas & procs
    return if proc_node?(dispatch_node)
    return if unsafe_hash_usage?(dispatch_node)
    return if unsafe_array_usage?(dispatch_node)
    return if %i[lambda proc].include?(dispatch_node.method_name)
    return if allowed_method_name?(dispatch_node.method_name)
    return if allow_if_method_has_argument?(node.send_node)
    return if node.block_type? && destructuring_block_argument?(arguments_node)
    return if allow_comments? && contains_comments?(node)

    register_offense(node, method_name, dispatch_node.method_name)
  end
end
Also aliased as: on_numblock
on_numblock(node)

rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity

Alias for: on_block

Private Instance Methods

allow_comments?() click to toggle source
# File lib/rubocop/cop/style/symbol_proc.rb, line 206
def allow_comments?
  cop_config.fetch('AllowComments', false)
end
allow_if_method_has_argument?(send_node) click to toggle source
# File lib/rubocop/cop/style/symbol_proc.rb, line 202
def allow_if_method_has_argument?(send_node)
  !!cop_config.fetch('AllowMethodsWithArguments', false) && !send_node.arguments.count.zero?
end
allowed_method_name?(name) click to toggle source
# File lib/rubocop/cop/style/symbol_proc.rb, line 153
def allowed_method_name?(name)
  allowed_method?(name) || matches_allowed_pattern?(name)
end
autocorrect(corrector, node) click to toggle source
# File lib/rubocop/cop/style/symbol_proc.rb, line 166
def autocorrect(corrector, node)
  if node.send_node.arguments?
    autocorrect_with_args(corrector, node, node.send_node.arguments, node.body.method_name)
  else
    autocorrect_without_args(corrector, node)
  end
end
autocorrect_with_args(corrector, node, args, method_name) click to toggle source
# File lib/rubocop/cop/style/symbol_proc.rb, line 178
def autocorrect_with_args(corrector, node, args, method_name)
  arg_range = args.last.source_range
  arg_range = range_with_surrounding_comma(arg_range, :right)
  replacement = " &:#{method_name}"
  replacement = ",#{replacement}" unless arg_range.source.end_with?(',')
  corrector.insert_after(arg_range, replacement)
  corrector.remove(block_range_with_space(node))
end
autocorrect_without_args(corrector, node) click to toggle source
# File lib/rubocop/cop/style/symbol_proc.rb, line 174
def autocorrect_without_args(corrector, node)
  corrector.replace(block_range_with_space(node), "(&:#{node.body.method_name})")
end
begin_pos_for_replacement(node) click to toggle source
# File lib/rubocop/cop/style/symbol_proc.rb, line 192
def begin_pos_for_replacement(node)
  expr = node.send_node.source_range

  if (paren_pos = (expr.source =~ /\(\s*\)$/))
    expr.begin_pos + paren_pos
  else
    node.loc.begin.begin_pos
  end
end
block_range_with_space(node) click to toggle source
# File lib/rubocop/cop/style/symbol_proc.rb, line 187
def block_range_with_space(node)
  block_range = range_between(begin_pos_for_replacement(node), node.loc.end.end_pos)
  range_with_surrounding_space(block_range, side: :left)
end
register_offense(node, method_name, block_method_name) click to toggle source
# File lib/rubocop/cop/style/symbol_proc.rb, line 157
def register_offense(node, method_name, block_method_name)
  block_start = node.loc.begin.begin_pos
  block_end = node.loc.end.end_pos
  range = range_between(block_start, block_end)
  message = format(MSG, method: method_name, block_method: block_method_name)

  add_offense(range, message: message) { |corrector| autocorrect(corrector, node) }
end
unsafe_array_usage?(node) click to toggle source
# File lib/rubocop/cop/style/symbol_proc.rb, line 149
def unsafe_array_usage?(node)
  node.receiver&.array_type? && %i[min max].include?(node.method_name)
end
unsafe_hash_usage?(node) click to toggle source

See: github.com/rubocop/rubocop/issues/10864

# File lib/rubocop/cop/style/symbol_proc.rb, line 145
def unsafe_hash_usage?(node)
  node.receiver&.hash_type? && %i[reject select].include?(node.method_name)
end