module Jekyll::Algolia::Utils
def self.compact_empty(hash)
Public: Remove all keys with a nil value or an empty string from a hash
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
def self.diff_keys(alpha, beta)
value. If not, it remember what was the value of beta and return it in
It only checks that all keys of alpha are also in beta, with the same
Public: Get a hash representing the difference between two hashes
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
def self.find_by_key(items, key, value)
value - The value of the key to filter
key - The key to search for
items - The array of hashes to search
Public: Find an item from an array based on the value of one of its key
def self.find_by_key(items, key, value) return nil if items.nil? items.find do |item| item[key] == value end end
def self.html_to_text(html)
Public: Convert an HTML string to its content only
def self.html_to_text(html) return nil if html.nil? text = Nokogiri::HTML(html).text text.tr("\n", ' ').squeeze(' ').strip end
def self.instance_of?(input, classname)
input - the variable to test
Public: Check if a variable is an instance of a specific class
def self.instance_of?(input, classname) input.instance_of? Object.const_get(classname) rescue StandardError # The class might not even exist false end
def self.jsonify(item)
but will try to stringify other objects, excluding the one that contain
It will keep any string, number, boolean,boolean,array or nested object,
item - The object to convert
JSON, to be stored as a record
Public: Convert an object into an object that can easily be converted to
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
def self.keys_to_symbols(hash)
Public: Convert a hash with string keys to a hash with symbol keys
def self.keys_to_symbols(hash) Hash[hash.map { |key, value| [key.to_sym, value] }] end
def self.match?(string, regex)
Newer versions of Ruby have easy ways to test this, but a wrapper is
regex - The regex to match against
string - The string to test
Public: Check if a string matches a regex
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
def self.monkey_patch(instance, method, block)
Solution found on
block - The new block to use for replacing (as a proc)
method - The method symbol to overwrite
instance - The instance to overwrite
Public: Allow redefining an instance method on the fly with a new one
def self.monkey_patch(instance, method, block) metaclass = class << instance; self; end metaclass.send(:define_method, method, block) end
def self.split_lines(input, max_length)
Public: Split a long text into lines of specific length
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