module Jekyll::Utils

Constants

SLUGIFY_ASCII_REGEXP
SLUGIFY_DEFAULT_REGEXP
SLUGIFY_MODES

Constants for use in slugify

SLUGIFY_PRETTY_REGEXP
SLUGIFY_RAW_REGEXP

Public Instance Methods

deep_merge_hashes(master_hash, other_hash) click to toggle source

Non-destructive version of deep_merge_hashes! See that method.

Returns the merged hashes.

# File lib/jekyll/utils.rb, line 28
def deep_merge_hashes(master_hash, other_hash)
  deep_merge_hashes!(master_hash.dup, other_hash)
end
deep_merge_hashes!(target, overwrite) click to toggle source

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: gemjack.com/gems/tartan-0.1.1/classes/Hash.html

Thanks to whoever made it.

# File lib/jekyll/utils.rb, line 41
def deep_merge_hashes!(target, overwrite)
  merge_values(target, overwrite)
  merge_default_proc(target, overwrite)
  duplicate_frozen_values(target)

  target
end
duplicable?(obj) click to toggle source
# File lib/jekyll/utils.rb, line 53
def duplicable?(obj)
  case obj
  when nil, false, true, Symbol, Numeric
    false
  else
    true
  end
end
has_liquid_construct?(content) click to toggle source

Determine whether the given content string contains Liquid Tags or Variables

Returns true is the string contains sequences of ‘{%` or `{{`

# File lib/jekyll/utils.rb, line 150
def has_liquid_construct?(content)
  return false if content.nil? || content.empty?

  content.include?("{%") || content.include?("{{")
end
has_yaml_header?(file) click to toggle source

Determines whether a given file has

Returns true if the YAML front matter is present. rubocop: disable Naming/PredicateName

# File lib/jekyll/utils.rb, line 141
def has_yaml_header?(file)
  File.open(file, "rb", &:readline).match? %r!\A---\s*\r?\n!
rescue EOFError
  false
end
mergable?(value) click to toggle source
# File lib/jekyll/utils.rb, line 49
def mergable?(value)
  value.is_a?(Hash) || value.is_a?(Drops::Drop)
end
merged_file_read_opts(site, opts) click to toggle source

Returns merged option hash for File.read of self.site (if exists) and a given param

# File lib/jekyll/utils.rb, line 305
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
parse_date(input, msg = "Input could not be parsed.") click to toggle source

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

# File lib/jekyll/utils.rb, line 130
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
pluralized_array_from_hash(hash, singular_key, plural_key) click to toggle source

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

# File lib/jekyll/utils.rb, line 70
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
safe_glob(dir, patterns, flags = 0) click to toggle source

Work the same way as Dir.glob but separating the input into two parts (‘dir’ + ‘/’ + ‘pattern’) to make sure the first part(‘dir’) does not act as a pattern.

For example, Dir.glob(“path[/*”) always returns an empty array, because the method fails to find the closing pattern to ‘[’ which is ‘]’

Examples:

safe_glob("path[", "*")
# => ["path[/file1", "path[/file2"]

safe_glob("path", "*", File::FNM_DOTMATCH)
# => ["path/.", "path/..", "path/file1"]

safe_glob("path", ["**", "*"])
# => ["path[/file1", "path[/folder/file2"]

dir - the dir where glob will be executed under

(the dir will be included to each result)

patterns - the patterns (or the pattern) which will be applied under the dir flags - the flags which will be applied to the pattern

Returns matched paths

# File lib/jekyll/utils.rb, line 292
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
slugify(string, mode: nil, cased: false) click to toggle source

Slugify a filename or title.

string - the filename or title to slugify mode - how string is slugified cased - whether to replace all uppercase letters with their lowercase counterparts

When mode is “none”, return the given string.

When mode is “raw”, return the given string, 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.

When mode is “ascii”, some everything else except ASCII characters a-z (lowercase), A-Z (uppercase) and 0-9 (numbers) are not replaced with hyphen.

When mode is “latin”, the input string is first preprocessed so that any letters with accents are replaced with the plain letter. Afterwards, it follows the “default” mode of operation.

If cased is true, all uppercase letters in the result string are replaced with their lowercase counterparts.

Examples:

slugify("The _config.yml file")
# => "the-config-yml-file"

slugify("The _config.yml file", "pretty")
# => "the-_config.yml-file"

slugify("The _config.yml file", "pretty", true)
# => "The-_config.yml file"

slugify("The _config.yml file", "ascii")
# => "the-config-yml-file"

slugify("The _config.yml file", "latin")
# => "the-config-yml-file"

Returns the slugified string.

# File lib/jekyll/utils.rb, line 202
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
stringify_hash_keys(hash) click to toggle source

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

# File lib/jekyll/utils.rb, line 119
def stringify_hash_keys(hash)
  transform_keys(hash) { |key| key.to_s rescue key }
end
symbolize_hash_keys(hash) click to toggle source

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

# File lib/jekyll/utils.rb, line 110
def symbolize_hash_keys(hash)
  transform_keys(hash) { |key| key.to_sym rescue key }
end
titleize_slug(slug) click to toggle source

Takes a slug and turns it into a simple title.

# File lib/jekyll/utils.rb, line 21
def titleize_slug(slug)
  slug.split("-").map!(&:capitalize).join(" ")
end
transform_keys(hash) { |key| ... } click to toggle source
# File lib/jekyll/utils.rb, line 97
def transform_keys(hash)
  result = {}
  hash.each_key do |key|
    result[yield(key)] = hash[key]
  end
  result
end
value_from_plural_key(hash, key) click to toggle source
# File lib/jekyll/utils.rb, line 85
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
value_from_singular_key(hash, key) click to toggle source
# File lib/jekyll/utils.rb, line 81
def value_from_singular_key(hash, key)
  hash[key] if hash.key?(key) || (hash.default_proc && hash[key])
end

Private Instance Methods

duplicate_frozen_values(target) click to toggle source
# File lib/jekyll/utils.rb, line 339
def duplicate_frozen_values(target)
  target.each do |key, val|
    target[key] = val.dup if val.frozen? && duplicable?(val)
  end
end
merge_default_proc(target, overwrite) click to toggle source
# File lib/jekyll/utils.rb, line 333
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
merge_values(target, overwrite) click to toggle source
# File lib/jekyll/utils.rb, line 321
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
replace_character_sequence_with_hyphen(string, mode: "default") click to toggle source

Replace each character sequence with a hyphen.

See Utils#slugify for a description of the character sequence specified by each mode.

# File lib/jekyll/utils.rb, line 349
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