Class: RuboCop::Cop::Rails::HttpPositionalArguments

Inherits:
Cop
  • Object
show all
Extended by:
TargetRailsVersion
Defined in:
lib/rubocop/cop/rails/http_positional_arguments.rb

Overview

This cop is used to identify usages of http methods like get, post, put, patch without the usage of keyword arguments in your tests and change them to use keyword args. This cop only applies to Rails >= 5 . If you are not running Rails < 5 you should disable # the Rails/HttpPositionalArguments cop or set your TargetRailsVersion in your .rubocop.yml file to 4.0, etc.

Examples:

# bad
get :new, { user_id: 1}

# good
get :new, params: { user_id: 1 }

Constant Summary collapse

MSG =
'Use keyword arguments instead of ' \
'positional arguments for http call: `%s`.'.freeze
KEYWORD_ARGS =
i[
  headers env params body flash as xhr session method
].freeze
HTTP_METHODS =
i[get post put patch delete head].freeze

Constants included from Util

Util::ASGN_NODES, Util::BYTE_ORDER_MARK, Util::CONDITIONAL_NODES, Util::EQUALS_ASGN_NODES, Util::LITERAL_REGEX, Util::LOGICAL_OPERATOR_NODES, Util::MODIFIER_NODES, Util::OPERATOR_METHODS, Util::SHORTHAND_ASGN_NODES

Instance Attribute Summary

Attributes inherited from Cop

#config, #corrections, #offenses, #processed_source

Instance Method Summary collapse

Methods included from TargetRailsVersion

minimum_target_rails_version, support_target_rails_version?

Methods inherited from Cop

#add_offense, all, autocorrect_incompatible_with, badge, #config_to_allow_offenses, #config_to_allow_offenses=, #cop_config, cop_name, #cop_name, #correct, department, #duplicate_location?, #excluded_file?, #find_location, #highlights, inherited, #initialize, #join_force?, lint?, match?, #message, #messages, non_rails, #parse, qualified_cop_name, #relevant_file?, #target_rails_version, #target_ruby_version

Methods included from AST::Sexp

#s

Methods included from NodePattern::Macros

#def_node_matcher, #def_node_search, #node_search, #node_search_all, #node_search_body, #node_search_first

Methods included from AutocorrectLogic

#autocorrect?, #autocorrect_enabled?, #autocorrect_requested?, #support_autocorrect?

Methods included from IgnoredNode

#ignore_node, #ignored_node?, #part_of_ignored_node?

Methods included from Util

begins_its_line?, block_length, comment_line?, compatible_external_encoding_for?, directions, double_quotes_required?, effective_column, ends_its_line?, escape_string, first_part_of_call_chain, interpret_string_escapes, line_distance, line_range, move_pos, needs_escaping?, on_node, operator?, parentheses?, parenthesized_call?, preceed?, range_between, range_by_whole_lines, range_with_surrounding_comma, range_with_surrounding_space, same_line?, source_range, strip_quotes, stripped_source_upto, symbol_without_quote?, to_string_literal, to_supported_styles, to_symbol_literal, within_node?

Methods included from PathUtil

absolute?, match_path?, relative_path, smart_path

Constructor Details

This class inherits a constructor from RuboCop::Cop::Cop

Instance Method Details

#autocorrect(node) ⇒ Object

given a pre Rails 5 method: get :new, user_id: @user.id, {}

the result should look like:

get :new, params: { user_id: @user.id }, headers: {}

the http_method is the method use to call the controller the controller node can be a symbol, method, object or string that represents the path/action on the Rails controller the data is the http parameters and environment sent in the Rails 5 http call

Returns:

  • lambda of auto correct procedure



89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/rubocop/cop/rails/http_positional_arguments.rb', line 89

def autocorrect(node)
  http_path, *data = *node.arguments

  controller_action = http_path.source
  params = convert_hash_data(data.first, 'params')
  headers = convert_hash_data(data.last, 'headers') if data.size > 1
  # the range of the text to replace, which is the whole line
  code_to_replace = node.loc.expression
  # what to replace with
  format = parentheses?(node) ? '%s(%s%s%s)' : '%s %s%s%s'
  new_code = format(format, node.method_name, controller_action,
                    params, headers)
  ->(corrector) { corrector.replace(code_to_replace, new_code) }
end

#convert_hash_data(data, type) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/rubocop/cop/rails/http_positional_arguments.rb', line 66

def convert_hash_data(data, type)
  # empty hash or no hash return empty string
  return '' if data.nil? || data.children.empty?
  hash_data = if data.hash_type?
                format('{ %s }', data.pairs.map(&:source).join(', '))
              else
                # user supplies an object,
                # no need to surround with braces
                data.source
              end
  format(', %s: %s', type, hash_data)
end

#format_arg?(node) ⇒ Boolean

Returns:

  • (Boolean)


62
63
64
# File 'lib/rubocop/cop/rails/http_positional_arguments.rb', line 62

def format_arg?(node)
  node.children.first == :format if node.sym_type?
end

#needs_conversion?(data) ⇒ Boolean

Returns true if the line needs to be converted.

Returns:

  • (Boolean)

    true if the line needs to be converted



46
47
48
49
50
51
52
53
54
55
56
# File 'lib/rubocop/cop/rails/http_positional_arguments.rb', line 46

def needs_conversion?(data)
  return true unless data.hash_type?
  children = data.child_nodes

  value = children.find do |d|
    special_keyword_arg?(d.children.first) ||
      (format_arg?(d.children.first) && children.size == 1)
  end

  value.nil?
end

#on_send(node) ⇒ Object



35
36
37
38
39
40
41
42
43
# File 'lib/rubocop/cop/rails/http_positional_arguments.rb', line 35

def on_send(node)
  data = http_request?(node)
  # if the data is nil then we don't need to add keyword arguments
  # because there is no data to put in params or headers, so skip
  return if data.nil?
  return unless needs_conversion?(data)

  add_offense(node, node.loc.selector, format(MSG, node.method_name))
end

#special_keyword_arg?(node) ⇒ Boolean

Returns:

  • (Boolean)


58
59
60
# File 'lib/rubocop/cop/rails/http_positional_arguments.rb', line 58

def special_keyword_arg?(node)
  KEYWORD_ARGS.include?(node.children.first) if node.sym_type?
end