# frozen_string_literal: truemoduleJekyll# This class handles custom defaults for YAML frontmatter settings.# These are set in _config.yml and apply both to internal use (e.g. layout)# and the data available to liquid.## It is exposed via the frontmatter_defaults method on the site class.classFrontmatterDefaults# Initializes a new instance.definitialize(site)@site=siteenddefreset@glob_cache={}if@glob_cacheenddefupdate_deprecated_types(set)returnsetunlessset.key?("scope")&&set["scope"].key?("type")set["scope"]["type"]=caseset["scope"]["type"]when"page"Deprecator.defaults_deprecate_type("page","pages")"pages"when"post"Deprecator.defaults_deprecate_type("post","posts")"posts"when"draft"Deprecator.defaults_deprecate_type("draft","drafts")"drafts"elseset["scope"]["type"]endsetenddefensure_time!(set)returnsetunlessset.key?("values")&&set["values"].key?("date")returnsetifset["values"]["date"].is_a?(Time)set["values"]["date"]=Utils.parse_date(set["values"]["date"],"An invalid date format was found in a front-matter default set: #{set}")setend# Finds a default value for a given setting, filtered by path and type## path - the path (relative to the source) of the page,# post or :draft the default is used in# type - a symbol indicating whether a :page,# a :post or a :draft calls this method## Returns the default value or nil if none was founddeffind(path,type,setting)value=nilold_scope=nilmatching_sets(path,type).eachdo|set|ifset["values"].key?(setting)&&has_precedence?(old_scope,set["scope"])value=set["values"][setting]old_scope=set["scope"]endendvalueend# Collects a hash with all default values for a page or post## path - the relative path of the page or post# type - a symbol indicating the type (:post, :page or :draft)## Returns a hash with all default values (an empty hash if there are none)defall(path,type)defaults={}old_scope=nilmatching_sets(path,type).eachdo|set|ifhas_precedence?(old_scope,set["scope"])defaults=Utils.deep_merge_hashes(defaults,set["values"])old_scope=set["scope"]elsedefaults=Utils.deep_merge_hashes(set["values"],defaults)endenddefaultsendprivate# Checks if a given default setting scope matches the given path and type## scope - the hash indicating the scope, as defined in _config.yml# path - the path to check for# type - the type (:post, :page or :draft) to check for## Returns true if the scope applies to the given type and pathdefapplies?(scope,path,type)applies_type?(scope,type)&&applies_path?(scope,path)enddefapplies_path?(scope,path)rel_scope_path=scope["path"]returntrueif!rel_scope_path.is_a?(String)||rel_scope_path.empty?sanitized_path=sanitize_path(path)ifrel_scope_path.include?("*")glob_scope(sanitized_path,rel_scope_path)elsepath_is_subpath?(sanitized_path,strip_collections_dir(rel_scope_path))endenddefglob_scope(sanitized_path,rel_scope_path)site_source=Pathname.new(@site.source)abs_scope_path=site_source.join(rel_scope_path).to_sglob_cache(abs_scope_path).eachdo|scope_path|scope_path=Pathname.new(scope_path).relative_path_from(site_source).to_sscope_path=strip_collections_dir(scope_path)Jekyll.logger.debug"Globbed Scope Path:",scope_pathreturntrueifpath_is_subpath?(sanitized_path,scope_path)endfalseenddefglob_cache(path)@glob_cache||={}@glob_cache[path]||=Dir.glob(path)enddefpath_is_subpath?(path,parent_path)path.start_with?(parent_path)enddefstrip_collections_dir(path)collections_dir=@site.config["collections_dir"]slashed_coll_dir=collections_dir.empty??"/":"#{collections_dir}/"returnpathifcollections_dir.empty?||!path.to_s.start_with?(slashed_coll_dir)path.sub(slashed_coll_dir,"")end# Determines whether the scope applies to type.# The scope applies to the type if:# 1. no 'type' is specified# 2. the 'type' in the scope is the same as the type asked about## scope - the Hash defaults set being asked about application# type - the type of the document being processed / asked about# its defaults.## Returns true if either of the above conditions are satisfied,# otherwise returns falsedefapplies_type?(scope,type)!scope.key?("type")||type&.to_sym.eql?(scope["type"].to_sym)end# Checks if a given set of default values is valid## set - the default value hash, as defined in _config.yml## Returns true if the set is valid and can be used in this classdefvalid?(set)set.is_a?(Hash)&&set["values"].is_a?(Hash)end# Determines if a new scope has precedence over an old one## old_scope - the old scope hash, or nil if there's none# new_scope - the new scope hash## Returns true if the new scope has precedence over the older# rubocop: disable Naming/PredicateNamedefhas_precedence?(old_scope,new_scope)returntrueifold_scope.nil?new_path=sanitize_path(new_scope["path"])old_path=sanitize_path(old_scope["path"])ifnew_path.length!=old_path.lengthnew_path.length>=old_path.lengthelsifnew_scope.key?("type")trueelse!old_scope.key?"type"endend# rubocop: enable Naming/PredicateName# Collects a list of sets that match the given path and type## Returns an array of hashesdefmatching_sets(path,type)@matched_set_cache||={}@matched_set_cache[path]||={}@matched_set_cache[path][type]||=valid_sets.selectdo|set|!set.key?("scope")||applies?(set["scope"],path,type)endend# Returns a list of valid sets## This is not cached to allow plugins to modify the configuration# and have their changes take effect## Returns an array of hashesdefvalid_setssets=@site.config["defaults"]return[]unlesssets.is_a?(Array)sets.mapdo|set|ifvalid?(set)ensure_time!(update_deprecated_types(set))elseJekyll.logger.warn"Defaults:","An invalid front-matter default set was found:"Jekyll.logger.warnset.to_snilendend.tap(&:compact!)end# Sanitizes the given path by removing a leading slashdefsanitize_path(path)ifpath.nil?||path.empty?""elsifpath.start_with?("/")path.gsub(%r!\A/|(?<=[^/])\z!,"")elsepathendendendend