# frozen_string_literal: truemoduleActionViewmoduleRenderParserclassRipperRenderParser<Base# :nodoc:classNode<::Array# :nodoc:attr_reader:typedefinitialize(type,arr,opts={})@type=typesuper(arr)enddefchildrento_aenddefinspecttypeinfo=type&&type!=:list?":"+type.to_s+", ":"""s("+typeinfo+map(&:inspect).join(", ")+")"enddeffcall?type==:command||type==:fcallenddeffcall_named?(name)fcall?&&self[0].type==:@ident&&self[0][0]==nameenddefargument_nodesraiseunlessfcall?return[]ifself[1].nil?ifself[1].last==false||self[1].last.type==:vcallself[1][0...-1]elseself[1][0..-1]endenddefstring?type==:string_literalenddefvariable_reference?type==:var_refenddefvcall?type==:vcallenddefcall?type==:callenddefvariable_nameself[0][0]enddefcall_method_nameself[2].firstenddefto_stringraiseunlessstring?# s(:string_literal, s(:string_content, map))self[0].mapdo|node|casenode.typewhen:@tstring_contentnode[0]when:string_embexpr"*"endend.join("")enddefhash?type==:bare_assoc_hash||type==:hashenddefto_hashiftype==:bare_assoc_hashhash_from_body(self[0])elsiftype==:hash&&self[0]==nil{}elsiftype==:hash&&self[0].type==:assoclist_from_argshash_from_body(self[0][0])endenddefhash_from_body(body)body.to_hdo|hash_node|returnnilifhash_node.type!=:assoc_new[hash_node[0],hash_node[1]]endenddefsymbol?type==:@label||type==:symbol_literalenddefto_symboliftype==:@label&&self[0]=~/\A(.+):\z/$1.to_symelsiftype==:symbol_literal&&self[0].type==:symbol&&self[0][0].type==:@identself[0][0][0].to_symelseraise"not a symbol?: #{self.inspect}"endendendclassNodeParser<::Ripper# :nodoc:PARSER_EVENTS.eachdo|event|arity=PARSER_EVENT_TABLE[event]ifarity==0&&event.to_s.end_with?("_new")module_eval(<<-eof,__FILE__,__LINE__+1)
def on_#{event}(*args)
Node.new(:list, args, lineno: lineno(), column: column())
end
eofelsifevent.to_s.match?(/_add(_.+)?\z/)module_eval(<<-eof,__FILE__,__LINE__+1)
begin; undef on_#{event}; rescue NameError; end
def on_#{event}(list, item)
list.push(item)
list
end
eofelsemodule_eval(<<-eof,__FILE__,__LINE__+1)
begin; undef on_#{event}; rescue NameError; end
def on_#{event}(*args)
Node.new(:#{event}, args, lineno: lineno(), column: column())
end
eofendendSCANNER_EVENTS.eachdo|event|module_eval(<<-End,__FILE__,__LINE__+1)
def on_#{event}(tok)
Node.new(:@#{event}, [tok], lineno: lineno(), column: column())
end
EndendendclassRenderCallExtractor<NodeParser# :nodoc:attr_reader:render_callsMETHODS_TO_PARSE=%w(render render_to_string)definitialize(*args)super@render_calls=[]endprivatedefon_fcall(name,*args)on_render_call(super)enddefon_command(name,*args)on_render_call(super)enddefon_render_call(node)METHODS_TO_PARSE.eachdo|method|ifnode.fcall_named?(method)@render_calls<<[method,node]returnnodeendendnodeenddefon_arg_paren(content)contentenddefon_paren(content)content.size==1?content.first:contentendenddefrender_callsparser=RenderCallExtractor.new(@code)parser.parseparser.render_calls.group_by(&:first).to_hdo|method,nodes|[method.to_sym,nodes.collect{|v|v[1]}]end.mapdo|method,nodes|nodes.map{|n|parse_render(n)}end.flatten.compactendprivatedefresolve_path_directory(path)ifpath.include?("/")pathelse"#{directory}/#{path}"endend# Convert# render("foo", ...)# into either# render(template: "foo", ...)# or# render(partial: "foo", ...)defnormalize_args(string,options_hash)ifoptions_hash{partial: string,locals: options_hash}else{partial: string}endenddefparse_render(node)node=node.argument_nodesif(node.length==1||node.length==2)&&!node[0].hash?ifnode.length==1options=normalize_args(node[0],nil)elsifnode.length==2options=normalize_args(node[0],node[1])endreturnnilunlessoptionsparse_render_from_options(options)elsifnode.length==1&&node[0].hash?options=parse_hash_to_symbols(node[0])returnnilunlessoptionsparse_render_from_options(options)elsenilendenddefparse_hash(node)node.hash?&&node.to_hashenddefparse_hash_to_symbols(node)hash=parse_hash(node)returnunlesshashhash.transform_keysdo|key_node|key=parse_sym(key_node)returnunlesskeykeyendenddefparse_render_from_options(options_hash)renders=[]keys=options_hash.keysif(keys&RENDER_TYPE_KEYS).size<1# Must have at least one of render keysreturnnilendif(keys-ALL_KNOWN_KEYS).any?# de-opt in case of unknown optionreturnnilendrender_type=(keys&RENDER_TYPE_KEYS)[0]node=options_hash[render_type]ifnode.string?template=resolve_path_directory(node.to_string)elseifnode.variable_reference?dependency=node.variable_name.sub(/\A(?:\$|@{1,2})/,"")elsifnode.vcall?dependency=node.variable_nameelsifnode.call?dependency=node.call_method_nameelsereturnendobject_template=truetemplate="#{dependency.pluralize}/#{dependency.singularize}"endreturnunlesstemplateifspacer_template=render_template_with_spacer?(options_hash)virtual_path=partial_to_virtual_path(:partial,spacer_template)renders<<virtual_pathendifoptions_hash.key?(:object)||options_hash.key?(:collection)||object_templatereturnnilifoptions_hash.key?(:object)&&options_hash.key?(:collection)returnnilunlessoptions_hash.key?(:partial)endvirtual_path=partial_to_virtual_path(render_type,template)renders<<virtual_path# Support for rendering multiple templates (i.e. a partial with a layout)iflayout_template=render_template_with_layout?(render_type,options_hash)virtual_path=partial_to_virtual_path(:layout,layout_template)renders<<virtual_pathendrendersenddefparse_str(node)node.string?&&node.to_stringenddefparse_sym(node)node.symbol?&&node.to_symbolenddefrender_template_with_layout?(render_type,options_hash)ifrender_type!=:layout&&options_hash.key?(:layout)parse_str(options_hash[:layout])endenddefrender_template_with_spacer?(options_hash)ifoptions_hash.key?(:spacer_template)parse_str(options_hash[:spacer_template])endendendendend