# frozen_string_literal: truemoduleRuboCopmoduleCopmoduleRails# 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 arguments.## @example# # bad# get :new, { user_id: 1}## # good# get :new, params: { user_id: 1 }classHttpPositionalArguments<CopMSG='Use keyword arguments instead of '\'positional arguments for http call: `%s`.'.freezeKEYWORD_ARGS=[:headers,:env,:params,:body,:flash,:as,:xhr,:session,:method].freezeHTTP_METHODS=[:get,:post,:put,:patch,:delete,:head].freezedef_node_matcher:http_request?,<<-END
(send nil {#{HTTP_METHODS.map(&:inspect).join(' ')}} !nil $_data ...)
ENDdefon_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 skipreturnifdata.nil?returnunlessneeds_conversion?(data)add_offense(node,node.loc.selector,format(MSG,node.method_name))end# @return [Boolean] true if the line needs to be converteddefneeds_conversion?(data)returntrueunlessdata.hash_type?children=data.child_nodesvalue=children.finddo|d|special_keyword_arg?(d.children.first)||(format_arg?(d.children.first)&&children.size==1)endvalue.nil?enddefspecial_keyword_arg?(node)KEYWORD_ARGS.include?(node.children.first)ifnode.type==:symenddefformat_arg?(node)node.children.first==:formatifnode.type==:symenddefconvert_hash_data(data,type)# empty hash or no hash return empty stringreturn''ifdata.nil?||data.children.empty?hash_data=ifdata.hash_type?format('{ %s }',data.pairs.map(&:source).join(', '))else# user supplies an object,# no need to surround with bracesdata.sourceendformat(', %s: %s',type,hash_data)end# given a pre Rails 5 method: get :new, user_id: @user.id, {}## @return lambda of auto correct procedure# 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 calldefautocorrect(node)_receiver,http_method,http_path,*data=*nodecontroller_action=http_path.sourceparams=convert_hash_data(data.first,'params')headers=convert_hash_data(data.last,'headers')ifdata.size>1# the range of the text to replace, which is the whole linecode_to_replace=node.loc.expression# what to replace withformat=parentheses?(node)?'%s(%s%s%s)':'%s %s%s%s'new_code=format(format,http_method,controller_action,params,headers)->(corrector){corrector.replace(code_to_replace,new_code)}endendendendend