module Jekyll::Algolia::Utils

Generic language-wide utils

Public Class Methods

compact_empty(hash) click to toggle source

Public: Remove all keys with a nil value or an empty string from a hash

hash - The input hash

# File lib/jekyll/algolia/utils.rb, line 53
def self.compact_empty(hash)
  new_hash = {}
  hash.each do |key, value|
    next if value.nil?
    next if value.is_a?(String) && value.empty?

    new_hash[key] = value
  end
  new_hash
end
diff_keys(alpha, beta) click to toggle source

Public: Get a hash representing the difference between two hashes

It only checks that all keys of alpha are also in beta, with the same value. If not, it remember what was the value of beta and return it in the output

# File lib/jekyll/algolia/utils.rb, line 139
def self.diff_keys(alpha, beta)
  diff = {}
  alpha.each do |key, value|
    diff[key] = beta[key] if beta[key] != value
  end

  return nil if diff.empty?

  diff
end
find_by_key(items, key, value) click to toggle source

Public: Find an item from an array based on the value of one of its key

items - The array of hashes to search key - The key to search for value - The value of the key to filter

It is basically a wrapper around [].find, handling more edge-cases

# File lib/jekyll/algolia/utils.rb, line 87
def self.find_by_key(items, key, value)
  return nil if items.nil?

  items.find do |item|
    item[key] == value
  end
end
html_to_text(html) click to toggle source

Public: Convert an HTML string to its content only

html - String representation of the HTML node

# File lib/jekyll/algolia/utils.rb, line 43
def self.html_to_text(html)
  return nil if html.nil?

  text = Nokogiri::HTML(html).text
  text.tr("\n", ' ').squeeze(' ').strip
end
instance_of?(input, classname) click to toggle source

Public: Check if a variable is an instance of a specific class

input - the variable to test classname - the string representation of the class

# File lib/jekyll/algolia/utils.rb, line 33
def self.instance_of?(input, classname)
  input.instance_of? Object.const_get(classname)
rescue StandardError
  # The class might not even exist
  false
end
jsonify(item) click to toggle source

Public: Convert an object into an object that can easily be converted to JSON, to be stored as a record

item - The object to convert

It will keep any string, number, boolean,boolean,array or nested object, but will try to stringify other objects, excluding the one that contain a unique identifier once serialized.

# File lib/jekyll/algolia/utils.rb, line 103
def self.jsonify(item)
  simple_types = [
    NilClass,
    TrueClass, FalseClass,
    Integer, Float,
    String
  ]
  # Integer arrived in Ruby 2.4. Before that it was Fixnum and Bignum
  if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.4.0')
    # rubocop:disable Lint/UnifiedInteger
    simple_types += [Fixnum, Bignum]
    # rubocop:enable Lint/UnifiedInteger
  end
  return item if simple_types.member?(item.class)

  # Recursive types
  return item.map { |value| jsonify(value) } if item.is_a?(Array)
  if item.is_a?(Hash)
    return item.map { |key, value| [key, jsonify(value)] }.to_h
  end

  # Can't be stringified, discard it
  return nil unless item.respond_to?(:to_s)

  # Discard also if is a serialized version with unique identifier
  stringified = item.to_s
  return nil if match?(stringified, /#<[^ ].*@[0-9]* .*>/)

  stringified
end
keys_to_symbols(hash) click to toggle source

Public: Convert a hash with string keys to a hash with symbol keys

hash - The input hash, with string keys

# File lib/jekyll/algolia/utils.rb, line 25
def self.keys_to_symbols(hash)
  Hash[hash.map { |key, value| [key.to_sym, value] }]
end
match?(string, regex) click to toggle source

Public: Check if a string matches a regex

string - The string to test regex - The regex to match against

Newer versions of Ruby have easy ways to test this, but a wrapper is needed for older versions.

# File lib/jekyll/algolia/utils.rb, line 71
def self.match?(string, regex)
  # Ruby 2.4 introduces .match?
  return regex.match?(string) if regex.respond_to?(:match?)

  # Older versions of Ruby have to deal with =~ returning nil if no match
  # is found
  !(string =~ regex).nil?
end
monkey_patch(instance, method, block) click to toggle source

Public: Allow redefining an instance method on the fly with a new one

instance - The instance to overwrite method - The method symbol to overwrite block - The new block to use for replacing (as a proc)

Solution found on stackoverflow.com/questions/803020/redefining-a-single-ruby-method-on-a-single-instance-with-a-lambda/16631789

# File lib/jekyll/algolia/utils.rb, line 17
def self.monkey_patch(instance, method, block)
  metaclass = class << instance; self; end
  metaclass.send(:define_method, method, block)
end
split_lines(input, max_length) click to toggle source

Public: Split a long text into lines of specific length

It takes care to not cut words

# File lib/jekyll/algolia/utils.rb, line 153
def self.split_lines(input, max_length)
  # Force splitting on actual new lines first
  if input.include?("\n")
    output = []
    input.split("\n").each do |line|
      output += split_lines(line, max_length)
    end
    return output
  end

  output = []
  words = input.split(' ')
  current_line = words.shift || ''
  test_line = '' # must be defined outside of the loop

  words.each do |word|
    test_line = "#{current_line} #{word}"
    if test_line.length > max_length
      output << current_line
      current_line = word
      next
    end
    current_line = test_line
  end
  output << current_line

  # Making sure all lines are the same length
  output.map { |line| line.ljust(max_length, ' ') }
end