require'concurrent/map'require'action_view/path_set'moduleActionViewclassDependencyTracker# :nodoc:@trackers=Concurrent::Map.newdefself.find_dependencies(name,template,view_paths=nil)tracker=@trackers[template.handler]return[]unlesstrackertracker.call(name,template,view_paths)enddefself.register_tracker(extension,tracker)handler=Template.handler_for_extension(extension)iftracker.respond_to?(:supports_view_paths?)@trackers[handler]=trackerelse@trackers[handler]=lambda{|name,template,_|tracker.call(name,template)}endenddefself.remove_tracker(handler)@trackers.delete(handler)endclassERBTracker# :nodoc:EXPLICIT_DEPENDENCY=/# Template Dependency: (\S+)/# A valid ruby identifier - suitable for class, method and specially variable namesIDENTIFIER=/
[[:alpha:]_] # at least one uppercase letter, lowercase letter or underscore
[[:word:]]* # followed by optional letters, numbers or underscores
/x# Any kind of variable name. e.g. @instance, @@class, $global or local.# Possibly following a method call chainVARIABLE_OR_METHOD_CHAIN=/
(?:\$|@{1,2})? # optional global, instance or class variable indicator
(?:#{IDENTIFIER}\.)* # followed by an optional chain of zero-argument method calls
(?<dynamic>#{IDENTIFIER}) # and a final valid identifier, captured as DYNAMIC
/x# A simple string literal. e.g. "School's out!"STRING=/
(?<quote>['"]) # an opening quote
(?<static>.*?) # with anything inside, captured as STATIC
\k<quote> # and a matching closing quote
/x# Part of any hash containing the :partial keyPARTIAL_HASH_KEY=/
(?:\bpartial:|:partial\s*=>) # partial key in either old or new style hash syntax
\s* # followed by optional spaces
/x# Part of any hash containing the :layout keyLAYOUT_HASH_KEY=/
(?:\blayout:|:layout\s*=>) # layout key in either old or new style hash syntax
\s* # followed by optional spaces
/x# Matches:# partial: "comments/comment", collection: @all_comments => "comments/comment"# (object: @single_comment, partial: "comments/comment") => "comments/comment"## "comments/comments"# 'comments/comments'# ('comments/comments')## (@topic) => "topics/topic"# topics => "topics/topic"# (message.topics) => "topics/topic"RENDER_ARGUMENTS=/\A
(?:\s*\(?\s*) # optional opening paren surrounded by spaces
(?:.*?#{PARTIAL_HASH_KEY}|#{LAYOUT_HASH_KEY})? # optional hash, up to the partial or layout key declaration
(?:#{STRING}|#{VARIABLE_OR_METHOD_CHAIN}) # finally, the dependency name of interest
/xmLAYOUT_DEPENDENCY=/\A
(?:\s*\(?\s*) # optional opening paren surrounded by spaces
(?:.*?#{LAYOUT_HASH_KEY}) # check if the line has layout key declaration
(?:#{STRING}|#{VARIABLE_OR_METHOD_CHAIN}) # finally, the dependency name of interest
/xmdefself.supports_view_paths?# :nodoc:trueenddefself.call(name,template,view_paths=nil)new(name,template,view_paths).dependenciesenddefinitialize(name,template,view_paths=nil)@name,@template,@view_paths=name,template,view_pathsenddefdependenciesrender_dependencies+explicit_dependenciesendattr_reader:name,:templateprivate:name,:templateprivatedefsourcetemplate.sourceenddefdirectoryname.split("/")[0..-2].join("/")enddefrender_dependenciesrender_dependencies=[]render_calls=source.split(/\brender\b/).drop(1)render_calls.eachdo|arguments|add_dependencies(render_dependencies,arguments,LAYOUT_DEPENDENCY)add_dependencies(render_dependencies,arguments,RENDER_ARGUMENTS)endrender_dependencies.uniqenddefadd_dependencies(render_dependencies,arguments,pattern)arguments.scan(pattern)doadd_dynamic_dependency(render_dependencies,Regexp.last_match[:dynamic])add_static_dependency(render_dependencies,Regexp.last_match[:static])endenddefadd_dynamic_dependency(dependencies,dependency)ifdependencydependencies<<"#{dependency.pluralize}/#{dependency.singularize}"endenddefadd_static_dependency(dependencies,dependency)ifdependencyifdependency.include?('/')dependencies<<dependencyelsedependencies<<"#{directory}/#{dependency}"endendenddefresolve_directories(wildcard_dependencies)return[]unless@view_pathswildcard_dependencies.flat_map{|query,templates|@view_paths.find_all_with_query(query).mapdo|template|"#{File.dirname(query)}/#{File.basename(template).split('.').first}"end}.sortenddefexplicit_dependenciesdependencies=source.scan(EXPLICIT_DEPENDENCY).flatten.uniqwildcards,explicits=dependencies.partition{|dependency|dependency[-1]=='*'}(explicits+resolve_directories(wildcards)).uniqendendregister_tracker:erb,ERBTrackerendend