# frozen_string_literal: truerequire"action_view/dependency_tracker"moduleActionViewclassDigestor@@digest_mutex=Mutex.newclass<<self# Supported options:## * <tt>name</tt> - Template name# * <tt>format</tt> - Template format# * +finder+ - An instance of ActionView::LookupContext# * <tt>dependencies</tt> - An array of dependent viewsdefdigest(name:,format: nil,finder:,dependencies: nil)ifdependencies.nil?||dependencies.empty?cache_key="#{name}.#{format}"elsedependencies_suffix=dependencies.flatten.tap(&:compact!).join(".")cache_key="#{name}.#{format}.#{dependencies_suffix}"end# this is a correctly done double-checked locking idiom# (Concurrent::Map's lookups have volatile semantics)finder.digest_cache[cache_key]||@@digest_mutex.synchronizedofinder.digest_cache.fetch(cache_key)do# re-check under lockpath=TemplatePath.parse(name)root=tree(path.to_s,finder,path.partial?)dependencies.eachdo|injected_dep|root.children<<Injected.new(injected_dep,nil,nil)endifdependenciesfinder.digest_cache[cache_key]=root.digest(finder)endendenddefloggerActionView::Base.logger||NullLoggerend# Create a dependency tree for template named +name+.deftree(name,finder,partial=false,seen={})logical_name=name.gsub(%r|/_|,"/")interpolated=name.include?("#")path=TemplatePath.parse(name)if!interpolated&&(template=find_template(finder,path.name,[path.prefix],partial,[]))ifnode=seen[template.identifier]# handle cycles in the treenodeelsenode=seen[template.identifier]=Node.create(name,logical_name,template,partial)deps=DependencyTracker.find_dependencies(name,template,finder.view_paths)deps.uniq{|n|n.gsub(%r|/_|,"/")}.eachdo|dep_file|node.children<<tree(dep_file,finder,true,seen)endnodeendelseunlessinterpolated# Dynamic template partial names can never be trackedlogger.error" Couldn't find template for digesting: #{name}"endseen[name]||=Missing.new(name,logical_name,nil)endendprivatedeffind_template(finder,name,prefixes,partial,keys)finder.disable_cachedofinder.find_all(name,prefixes,partial,keys).firstendendendclassNodeattr_reader:name,:logical_name,:template,:childrendefself.create(name,logical_name,template,partial)klass=partial?Partial:Nodeklass.new(name,logical_name,template,[])enddefinitialize(name,logical_name,template,children=[])@name=name@logical_name=logical_name@template=template@children=childrenenddefdigest(finder,stack=[])ActiveSupport::Digest.hexdigest("#{template.source}-#{dependency_digest(finder,stack)}")enddefdependency_digest(finder,stack)children.mapdo|node|ifstack.include?(node)falseelsefinder.digest_cache[node.name]||=beginstack.pushnodenode.digest(finder,stack).tap{stack.pop}endendend.join("-")enddefto_dep_mapchildren.any??{name=>children.map(&:to_dep_map)}:nameendendclassPartial<Node;endclassMissing<Nodedefdigest(finder,_=[])""endendclassInjected<Nodedefdigest(finder,_=[])nameendendclassNullLoggerdefself.debug(_);enddefself.error(_);endendendend