module Jekyll::Utils
def add_permalink_suffix(template, permalink_style)
# => "/:basename"
add_permalink_suffix("/:basename", "/:year/:month/:title")
# => "/:basename/"
add_permalink_suffix("/:basename", "/:year/:month/:title/")
# => "/:basename:output_ext"
add_permalink_suffix("/:basename", :date)
# => "/:basename/"
add_permalink_suffix("/:basename", :pretty)
Examples:
template. Otherwise, template will be returned without modification.
":output_ext" (or is :none, :date, or :ordinal) then so will the returned
then so will the returned template. If permalink_style has a trailing
trailing slash (or is :pretty, which indirectly has a trailing slash),
specified in permalink_style. For example, if permalink_style contains a
The returned permalink template will use the same ending style as
permalink_style - permalink style, either built-in or custom
template - permalink template without trailing slash or file extension
permalink style.
Add an appropriate suffix to template so that it matches the specified
def add_permalink_suffix(template, permalink_style) template = template.dup 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
def deep_merge_hashes(master_hash, other_hash)
Non-destructive version of deep_merge_hashes! See that method.
def deep_merge_hashes(master_hash, other_hash) deep_merge_hashes!(master_hash.dup, other_hash) end
def deep_merge_hashes!(target, overwrite)
http://gemjack.com/gems/tartan-0.1.1/classes/Hash.html
This code was lovingly stolen from some random gem:
other_hash - the other hash whose values will be persisted after the merge
master_hash - the "parent" hash whose values will be overridden
Merges a master hash with another hash, recursively.
def deep_merge_hashes!(target, overwrite) merge_values(target, overwrite) merge_default_proc(target, overwrite) duplicate_frozen_values(target) target end
def duplicable?(obj)
def duplicable?(obj) case obj when nil, false, true, Symbol, Numeric false else true end end
def duplicate_frozen_values(target)
def duplicate_frozen_values(target) target.each do |key, val| target[key] = val.dup if val.frozen? && duplicable?(val) end end
def has_liquid_construct?(content)
Determine whether the given content string contains Liquid Tags or Variables
def has_liquid_construct?(content) return false if content.nil? || content.empty? content.include?("{%") || content.include?("{{") end
def has_yaml_header?(file)
Returns true if the YAML front matter is present.
Determines whether a given file has
def has_yaml_header?(file) File.open(file, "rb", &:readline).match? %r!\A---\s*\r?\n! rescue EOFError false end
def mergable?(value)
def mergable?(value) value.is_a?(Hash) || value.is_a?(Drops::Drop) end
def merge_default_proc(target, overwrite)
def merge_default_proc(target, overwrite) if target.is_a?(Hash) && overwrite.is_a?(Hash) && target.default_proc.nil? target.default_proc = overwrite.default_proc end end
def merge_values(target, overwrite)
def merge_values(target, overwrite) target.merge!(overwrite) do |_key, old_val, new_val| if new_val.nil? old_val elsif mergable?(old_val) && mergable?(new_val) deep_merge_hashes(old_val, new_val) else new_val end end end
def merged_file_read_opts(site, opts)
Returns merged option hash for File.read of self.site (if exists)
def merged_file_read_opts(site, opts) merged = (site ? site.file_read_opts : {}).merge(opts) # always use BOM when reading UTF-encoded files if merged[:encoding]&.downcase&.start_with?("utf-") merged[:encoding] = "bom|#{merged[:encoding]}" end if merged["encoding"]&.downcase&.start_with?("utf-") merged["encoding"] = "bom|#{merged["encoding"]}" end merged end
def parse_date(input, msg = "Input could not be parsed.")
Returns the parsed date if successful, throws a FatalException
msg - (optional) the error message to show the user
input - the date/time to parse
Parse a date/time and throw an error if invalid
def parse_date(input, msg = "Input could not be parsed.") @parse_date_cache ||= {} @parse_date_cache[input] ||= Time.parse(input).localtime rescue ArgumentError raise Errors::InvalidDateError, "Invalid date '#{input}': #{msg}" end
def pluralized_array_from_hash(hash, singular_key, plural_key)
plural_key - the plural key
singular_key - the singular key
hash - the hash to read from
and then the plural key, and handling any nil entries.
Read array from the supplied hash favouring the singular key
def pluralized_array_from_hash(hash, singular_key, plural_key) array = [] value = value_from_singular_key(hash, singular_key) value ||= value_from_plural_key(hash, plural_key) array << value array.flatten! array.compact! array end
def replace_character_sequence_with_hyphen(string, mode: "default")
See Utils#slugify for a description of the character sequence specified
Replace each character sequence with a hyphen.
def replace_character_sequence_with_hyphen(string, mode: "default") replaceable_char = case mode when "raw" SLUGIFY_RAW_REGEXP when "pretty" # "._~!$&'()+,;=@" is human readable (not URI-escaped) in URL # and is allowed in both extN and NTFS. SLUGIFY_PRETTY_REGEXP when "ascii" # For web servers not being able to handle Unicode, the safe # method is to ditch anything else but latin letters and numeric # digits. SLUGIFY_ASCII_REGEXP else SLUGIFY_DEFAULT_REGEXP end # Strip according to the mode string.gsub(replaceable_char, "-") end
def safe_glob(dir, patterns, flags = 0)
flags - the flags which will be applied to the pattern
patterns - the patterns (or the pattern) which will be applied under the dir
(the dir will be included to each result)
dir - the dir where glob will be executed under
# => ["path[/file1", "path[/folder/file2"]
safe_glob("path", ["**", "*"])
# => ["path/.", "path/..", "path/file1"]
safe_glob("path", "*", File::FNM_DOTMATCH)
# => ["path[/file1", "path[/file2"]
safe_glob("path[", "*")
Examples:
because the method fails to find the closing pattern to '[' which is ']'
For example, Dir.glob("path[/*") always returns an empty array,
as a pattern.
('dir' + '/' + 'pattern') to make sure the first part('dir') does not act
Work the same way as Dir.glob but separating the input into two parts
def safe_glob(dir, patterns, flags = 0) return [] unless Dir.exist?(dir) pattern = File.join(Array(patterns)) return [dir] if pattern.empty? Dir.chdir(dir) do Dir.glob(pattern, flags).map { |f| File.join(dir, f) } end end
def slugify(string, mode: nil, cased: false)
# => "the-config-yml-file"
slugify("The _config.yml file", "latin")
# => "the-config-yml-file"
slugify("The _config.yml file", "ascii")
# => "The-_config.yml file"
slugify("The _config.yml file", "pretty", true)
# => "the-_config.yml-file"
slugify("The _config.yml file", "pretty")
# => "the-config-yml-file"
slugify("The _config.yml file")
Examples:
replaced with their lowercase counterparts.
If cased is true, all uppercase letters in the result string are
it follows the "default" mode of operation.
any letters with accents are replaced with the plain letter. Afterwards,
When mode is "latin", the input string is first preprocessed so that
a-z (lowercase), A-Z (uppercase) and 0-9 (numbers) are not replaced with hyphen.
When mode is "ascii", some everything else except ASCII characters
are not replaced with hyphen.
When mode is "pretty", some non-alphabetic characters (._~!$&'()+,;=@)
replaced with a hyphen too.
When mode is "default" or nil, non-alphabetic characters are
with every sequence of spaces characters replaced with a hyphen.
When mode is "raw", return the given string,
When mode is "none", return the given string.
lowercase counterparts
cased - whether to replace all uppercase letters with their
mode - how string is slugified
string - the filename or title to slugify
Slugify a filename or title.
def slugify(string, mode: nil, cased: false) mode ||= "default" return nil if string.nil? unless SLUGIFY_MODES.include?(mode) return cased ? string : string.downcase end # Drop accent marks from latin characters. Everything else turns to ? if mode == "latin" I18n.config.available_locales = :en if I18n.config.available_locales.empty? string = I18n.transliterate(string) end slug = replace_character_sequence_with_hyphen(string, :mode => mode) # Remove leading/trailing hyphen slug.gsub!(%r!^-|-$!i, "") slug.downcase! unless cased Jekyll.logger.warn("Warning:", "Empty `slug` generated for '#{string}'.") if slug.empty? slug end
def stringify_hash_keys(hash)
hash - the hash to which to apply this transformation
Apply #to_s to all keys in the Hash
def stringify_hash_keys(hash) transform_keys(hash) { |key| key.to_s rescue key } end
def symbolize_hash_keys(hash)
hash - the hash to which to apply this transformation
Apply #to_sym to all keys in the hash
def symbolize_hash_keys(hash) transform_keys(hash) { |key| key.to_sym rescue key } end
def titleize_slug(slug)
def titleize_slug(slug) slug.split("-").map!(&:capitalize).join(" ") end
def transform_keys(hash)
def transform_keys(hash) result = {} hash.each_key do |key| result[yield(key)] = hash[key] end result end
def value_from_plural_key(hash, key)
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 value_from_singular_key(hash, key)
def value_from_singular_key(hash, key) hash[key] if hash.key?(key) || (hash.default_proc && hash[key]) end