# Used for merging results of metadata callbacksrequire"active_support/core_ext/hash/deep_merge"moduleMiddleman# Sitemap namespacemoduleSitemap# The Store class## The Store manages a collection of Resource objects, which represent# individual items in the sitemap. Resources are indexed by "source path",# which is the path relative to the source directory, minus any template# extensions. All "path" parameters used in this class are source paths.classStore# @return [Middleman::Application]attr_accessor:app# Initialize with parent app# @param [Middleman::Application] appdefinitialize(app)@app=app@resources=[]@_cached_metadata={}@_lookup_cache={:path=>{},:destination_path=>{}}@resource_list_manipulators=[]# Register classes which can manipulate the main site map listregister_resource_list_manipulator(:on_disk,Middleman::Sitemap::Extensions::OnDisk.new(self),false)# Proxiesregister_resource_list_manipulator(:proxies,@app.proxy_manager,false)end# Register a klass which can manipulate the main site map list# @param [Symbol] name Name of the manipulator for debugging# @param [Class, Module] inst Abstract namespace which can update the resource list# @param [Boolean] immediately_rebuild Whether the resource list should be immediately recalculated# @return [void]defregister_resource_list_manipulator(name,inst,immediately_rebuild=true)@resource_list_manipulators<<[name,inst]rebuild_resource_list!(:registered_new)ifimmediately_rebuildend# Rebuild the list of resources from scratch, using registed manipulators# @return [void]defrebuild_resource_list!(reason=nil)@resources=@resource_list_manipulators.inject([])do|result,(_,inst)|inst.manipulate_resource_list(result)end# Reset lookup cache@_lookup_cache={:path=>{},:destination_path=>{}}@resources.eachdo|resource|@_lookup_cache[:path][resource.path]=resource@_lookup_cache[:destination_path][resource.destination_path]=resourceendend# Find a resource given its original path# @param [String] request_path The original path of a resource.# @return [Middleman::Sitemap::Resource]deffind_resource_by_path(request_path)request_path=::Middleman::Util.normalize_path(request_path)@_lookup_cache[:path][request_path]end# Find a resource given its destination path# @param [String] request_path The destination (output) path of a resource.# @return [Middleman::Sitemap::Resource]deffind_resource_by_destination_path(request_path)request_path=::Middleman::Util.normalize_path(request_path)@_lookup_cache[:destination_path][request_path]end# Get the array of all resources# @param [Boolean] include_ignored Whether to include ignored resources# @return [Array<Middleman::Sitemap::Resource>]defresources(include_ignored=false)ifinclude_ignored@resourceselse@resources.reject(&:ignored?)endend# Register a handler to provide metadata on a file path# @param [Regexp] matcher# @return [Array<Array<Proc, Regexp>>]defprovides_metadata(matcher=nil,&block)@_provides_metadata||=[]@_provides_metadata<<[block,matcher]ifblock_given?@_provides_metadataend# Get the metadata for a specific file# @param [String] source_file# @return [Hash]defmetadata_for_file(source_file)blank_metadata={:options=>{},:locals=>{},:page=>{},:blocks=>[]}provides_metadata.inject(blank_metadata)do|result,(callback,matcher)|nextresultif!matcher.nil?&&!source_file.match(matcher)metadata=callback.call(source_file)ifmetadata.has_key?(:blocks)result[:blocks]<<metadata[:blocks]metadata.delete(:blocks)endresult.deep_merge(metadata)endend# Register a handler to provide metadata on a url path# @param [Regexp] matcher# @param [Symbol] origin an indicator of where this metadata came from - only one # block per [matcher, origin] pair may exist.# @return [Array<Array<Proc, Regexp>>]defprovides_metadata_for_path(matcher=nil,origin=nil,&block)@_provides_metadata_for_path||=[]ifblock_given?iforiginexisting_provider=@_provides_metadata_for_path.find{|b,m,o|o==origin&&m==matcher}endifexisting_providerexisting_provider[0]=blockelse@_provides_metadata_for_path<<[block,matcher,origin]end@_cached_metadata={}end@_provides_metadata_for_pathend# Get the metadata for a specific URL# @param [String] request_path# @return [Hash]defmetadata_for_path(request_path)return@_cached_metadata[request_path]if@_cached_metadata[request_path]blank_metadata={:options=>{},:locals=>{},:page=>{},:blocks=>[]}@_cached_metadata[request_path]=provides_metadata_for_path.inject(blank_metadata)do|result,(callback,matcher)|casematcherwhenRegexpnextresultunlessrequest_path.match(matcher)whenStringnextresultunlessFile.fnmatch("/"+matcher.sub(%r{^/},''),"/#{request_path}")endmetadata=callback.call(request_path)ifmetadata.has_key?(:blocks)result[:blocks]<<metadata[:blocks]metadata.delete(:blocks)endresult.deep_merge(metadata)endend# Get the URL path for an on-disk file# @param [String] file# @return [String]deffile_to_path(file)file=File.expand_path(file,@app.root)prefix=@app.source_dir.sub(/\/$/,"")+"/"returnfalseunlessfile.include?(prefix)path=file.sub(prefix,"")extensionless_path(path)end# Get a path without templating extensions# @param [String] file# @return [String]defextensionless_path(file)path=file.dupend_of_the_line=falsewhile!end_of_the_lineif!::Tilt[path].nil?path=path.sub(File.extname(path),"")elseend_of_the_line=trueendend# If there is no extension, look for oneifFile.extname(path).empty?input_ext=File.extname(file)if!input_ext.empty?input_ext=input_ext.split(".").last.to_symif@app.template_extensions.has_key?(input_ext)path<<".#{@app.template_extensions[input_ext]}"endendendpathendendendend