class RuboCop::Cop::Rails::MatchRoute

This cop identifies places where defining routes with `match` can be replaced with a specific HTTP method.

Don't use `match` to define any routes unless there is a need to map multiple request types among [:get, :post, :patch, :put, :delete] to a single action using the `:via` option.

@example

# bad
match ':controller/:action/:id'
match 'photos/:id', to: 'photos#show', via: :get

# good
get ':controller/:action/:id'
get 'photos/:id', to: 'photos#show'
match 'photos/:id', to: 'photos#show', via: [:get, :post]
match 'photos/:id', to: 'photos#show', via: :all

Constants

HTTP_METHODS
MSG
RESTRICT_ON_SEND

Public Instance Methods

on_send(node) click to toggle source
# File lib/rubocop/cop/rails/match_route.rb, line 34
def on_send(node)
  match_method_call?(node) do |path_node, options_node|
    return unless within_routes?(node)

    options_node = path_node.hash_type? ? path_node : options_node.first

    if options_node.nil?
      register_offense(node, 'get')
    else
      via = extract_via(options_node)
      return unless via.size == 1 && http_method?(via.first)

      register_offense(node, via.first)
    end
  end
end

Private Instance Methods

extract_via(node) click to toggle source
# File lib/rubocop/cop/rails/match_route.rb, line 71
def extract_via(node)
  via_pair = via_pair(node)
  return %i[get] unless via_pair

  _, via = *via_pair

  if via.basic_literal?
    [via.value]
  elsif via.array_type?
    via.values.map(&:value)
  else
    []
  end
end
http_method?(method) click to toggle source
# File lib/rubocop/cop/rails/match_route.rb, line 90
def http_method?(method)
  HTTP_METHODS.include?(method.to_sym)
end
http_method_and_options(node) click to toggle source
# File lib/rubocop/cop/rails/match_route.rb, line 111
def http_method_and_options(node)
  via_pair = via_pair(node)
  http_method = extract_via(node).first
  rest_pairs = node.pairs - [via_pair]
  [http_method, rest_pairs]
end
register_offense(node, http_method) click to toggle source
# File lib/rubocop/cop/rails/match_route.rb, line 53
def register_offense(node, http_method)
  add_offense(node, message: format(MSG, http_method: http_method)) do |corrector|
    match_method_call?(node) do |path_node, options_node|
      options_node = options_node.first

      corrector.replace(node, replacement(path_node, options_node))
    end
  end
end
replacement(path_node, options_node) click to toggle source
# File lib/rubocop/cop/rails/match_route.rb, line 94
def replacement(path_node, options_node)
  if path_node.hash_type?
    http_method, options = *http_method_and_options(path_node)
    "#{http_method} #{options.map(&:source).join(', ')}"
  elsif options_node.nil?
    "get #{path_node.source}"
  else
    http_method, options = *http_method_and_options(options_node)

    if options.any?
      "#{http_method} #{path_node.source}, #{options.map(&:source).join(', ')}"
    else
      "#{http_method} #{path_node.source}"
    end
  end
end
via_pair(node) click to toggle source
# File lib/rubocop/cop/rails/match_route.rb, line 86
def via_pair(node)
  node.pairs.find { |p| p.key.value == :via }
end
within_routes?(node) click to toggle source
# File lib/rubocop/cop/rails/match_route.rb, line 67
def within_routes?(node)
  node.each_ancestor(:block).any? { |a| routes_draw?(a.send_node) }
end