mirror of
https://github.com/jekyll/jekyll.git
synced 2026-02-14 08:35:02 -05:00
This updates the default permalink style for pages and collections to match the site-wide 'permalink' setting. If the permalink setting contains a trailing slash, either explicitly or by being set to ':pretty', then pages and collections permalinks will contain trailing slashes by default as well. Similarly, if the permalink setting contains a trailing ':output_ext', so will pages and collections. If the permalink setting contains neither a trailing slash or extension, neither will pages or collections. This impacts only the default permalink structure for pages and collections. Permalinks set in the frontmatter of an individual page take precedence, as does the permalink setting for a specific collection. Fixes #2691
203 lines
6.2 KiB
Ruby
203 lines
6.2 KiB
Ruby
module Jekyll
|
|
module Utils
|
|
extend self
|
|
|
|
# Constants for use in #slugify
|
|
SLUGIFY_MODES = %w{raw default pretty}
|
|
SLUGIFY_RAW_REGEXP = Regexp.new('\\s+').freeze
|
|
SLUGIFY_DEFAULT_REGEXP = Regexp.new('[^[:alnum:]]+').freeze
|
|
SLUGIFY_PRETTY_REGEXP = Regexp.new("[^[:alnum:]._~!$&'()+,;=@]+").freeze
|
|
|
|
# Merges a master hash with another hash, recursively.
|
|
#
|
|
# master_hash - the "parent" hash whose values will be overridden
|
|
# other_hash - the other hash whose values will be persisted after the merge
|
|
#
|
|
# This code was lovingly stolen from some random gem:
|
|
# http://gemjack.com/gems/tartan-0.1.1/classes/Hash.html
|
|
#
|
|
# Thanks to whoever made it.
|
|
def deep_merge_hashes(master_hash, other_hash)
|
|
target = master_hash.dup
|
|
|
|
other_hash.each_key do |key|
|
|
if other_hash[key].is_a? Hash and target[key].is_a? Hash
|
|
target[key] = Utils.deep_merge_hashes(target[key], other_hash[key])
|
|
next
|
|
end
|
|
|
|
target[key] = other_hash[key]
|
|
end
|
|
|
|
target
|
|
end
|
|
|
|
# Read array from the supplied hash favouring the singular key
|
|
# and then the plural key, and handling any nil entries.
|
|
#
|
|
# hash - the hash to read from
|
|
# singular_key - the singular key
|
|
# plural_key - the plural key
|
|
#
|
|
# Returns an array
|
|
def pluralized_array_from_hash(hash, singular_key, plural_key)
|
|
[].tap do |array|
|
|
array << (value_from_singular_key(hash, singular_key) || value_from_plural_key(hash, plural_key))
|
|
end.flatten.compact
|
|
end
|
|
|
|
def value_from_singular_key(hash, key)
|
|
hash[key] if (hash.key?(key) || (hash.default_proc && hash[key]))
|
|
end
|
|
|
|
def value_from_plural_key(hash, key)
|
|
if hash.key?(key) || (hash.default_proc && hash[key])
|
|
val = hash[key]
|
|
case val
|
|
when String
|
|
val.split
|
|
when Array
|
|
val.compact
|
|
end
|
|
end
|
|
end
|
|
|
|
def transform_keys(hash)
|
|
result = {}
|
|
hash.each_key do |key|
|
|
result[yield(key)] = hash[key]
|
|
end
|
|
result
|
|
end
|
|
|
|
# Apply #to_sym to all keys in the hash
|
|
#
|
|
# hash - the hash to which to apply this transformation
|
|
#
|
|
# Returns a new hash with symbolized keys
|
|
def symbolize_hash_keys(hash)
|
|
transform_keys(hash) { |key| key.to_sym rescue key }
|
|
end
|
|
|
|
# Apply #to_s to all keys in the Hash
|
|
#
|
|
# hash - the hash to which to apply this transformation
|
|
#
|
|
# Returns a new hash with stringified keys
|
|
def stringify_hash_keys(hash)
|
|
transform_keys(hash) { |key| key.to_s rescue key }
|
|
end
|
|
|
|
# Parse a date/time and throw an error if invalid
|
|
#
|
|
# input - the date/time to parse
|
|
# msg - (optional) the error message to show the user
|
|
#
|
|
# Returns the parsed date if successful, throws a FatalException
|
|
# if not
|
|
def parse_date(input, msg = "Input could not be parsed.")
|
|
Time.parse(input).localtime
|
|
rescue ArgumentError
|
|
raise Errors::FatalException.new("Invalid date '#{input}': " + msg)
|
|
end
|
|
|
|
# Determines whether a given file has
|
|
#
|
|
# Returns true if the YAML front matter is present.
|
|
def has_yaml_header?(file)
|
|
!!(File.open(file, 'rb') { |f| f.read(5) } =~ /\A---\r?\n/)
|
|
end
|
|
|
|
# Slugify a filename or title.
|
|
#
|
|
# string - the filename or title to slugify
|
|
# mode - how string is slugified
|
|
#
|
|
# When mode is "none", return the given string in lowercase.
|
|
#
|
|
# When mode is "raw", return the given string in lowercase,
|
|
# with every sequence of spaces characters replaced with a hyphen.
|
|
#
|
|
# When mode is "default" or nil, non-alphabetic characters are
|
|
# replaced with a hyphen too.
|
|
#
|
|
# When mode is "pretty", some non-alphabetic characters (._~!$&'()+,;=@)
|
|
# are not replaced with hyphen.
|
|
#
|
|
# Examples:
|
|
# slugify("The _config.yml file")
|
|
# # => "the-config-yml-file"
|
|
#
|
|
# slugify("The _config.yml file", "pretty")
|
|
# # => "the-_config.yml-file"
|
|
#
|
|
# Returns the slugified string.
|
|
def slugify(string, mode=nil)
|
|
mode ||= 'default'
|
|
return nil if string.nil?
|
|
return string.downcase unless SLUGIFY_MODES.include?(mode)
|
|
|
|
# Replace each character sequence with a hyphen
|
|
re = case mode
|
|
when 'raw'
|
|
SLUGIFY_RAW_REGEXP
|
|
when 'default'
|
|
SLUGIFY_DEFAULT_REGEXP
|
|
when 'pretty'
|
|
# "._~!$&'()+,;=@" is human readable (not URI-escaped) in URL
|
|
# and is allowed in both extN and NTFS.
|
|
SLUGIFY_PRETTY_REGEXP
|
|
end
|
|
|
|
string.
|
|
# Strip according to the mode
|
|
gsub(re, '-').
|
|
# Remove leading/trailing hyphen
|
|
gsub(/^\-|\-$/i, '').
|
|
# Downcase
|
|
downcase
|
|
end
|
|
|
|
# Add an appropriate suffix to template so that it matches the specified
|
|
# permalink style.
|
|
#
|
|
# template - permalink template without trailing slash or file extension
|
|
# permalink_style - permalink style, either built-in or custom
|
|
#
|
|
# The returned permalink template will use the same ending style as
|
|
# specified in permalink_style. For example, if permalink_style contains a
|
|
# trailing slash (or is :pretty, which indirectly has a trailing slash),
|
|
# then so will the returned template. If permalink_style has a trailing
|
|
# ":output_ext" (or is :none, :date, or :ordinal) then so will the returned
|
|
# template. Otherwise, template will be returned without modification.
|
|
#
|
|
# Examples:
|
|
# add_permalink_suffix("/:basename", :pretty)
|
|
# # => "/:basename/"
|
|
#
|
|
# add_permalink_suffix("/:basename", :date)
|
|
# # => "/:basename:output_ext"
|
|
#
|
|
# add_permalink_suffix("/:basename", "/:year/:month/:title/")
|
|
# # => "/:basename/"
|
|
#
|
|
# add_permalink_suffix("/:basename", "/:year/:month/:title")
|
|
# # => "/:basename"
|
|
#
|
|
# Returns the updated permalink template
|
|
def add_permalink_suffix(template, permalink_style)
|
|
case permalink_style
|
|
when :pretty
|
|
template << "/"
|
|
when :date, :ordinal, :none
|
|
template << ":output_ext"
|
|
else
|
|
template << "/" if permalink_style.to_s.end_with?("/")
|
|
template << ":output_ext" if permalink_style.to_s.end_with?(":output_ext")
|
|
end
|
|
template
|
|
end
|
|
|
|
end
|
|
end
|