jekyll/lib/jekyll/utils.rb

212 lines
6.5 KiB
Ruby

module Jekyll
module Utils extend self
autoload :Platforms, 'jekyll/utils/platforms'
# 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
# Non-destructive version of deep_merge_hashes! See that method.
#
# Returns the merged hashes.
def deep_merge_hashes(master_hash, other_hash)
deep_merge_hashes!(master_hash.dup, other_hash)
end
# 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!(target, overwrite)
overwrite.each_key do |key|
if overwrite[key].is_a? Hash and target[key].is_a? Hash
target[key] = Utils.deep_merge_hashes(target[key], overwrite[key])
next
end
target[key] = overwrite[key]
end
if target.default_proc.nil?
target.default_proc = overwrite.default_proc
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