lib/custom_facts/util/values.rb
# frozen_string_literal: true
module LegacyFacter
module Util
# A util module for custom_facts containing helper methods
module Values
module_function
class DeepFreezeError < StandardError; end
# Duplicate and deeply freeze a given data structure
#
# @param value [Object] The structure to freeze
# @return [void]
def deep_freeze(value)
case value
when Numeric, Symbol, TrueClass, FalseClass, NilClass
# These are immutable values, we can safely ignore them
value
when String
value.dup.freeze
when Array
value.map do |entry|
deep_freeze(entry)
end.freeze
when Hash
value.each_with_object({}) do |(key, val), hash|
hash[deep_freeze(key)] = deep_freeze(val)
end.freeze
else
raise DeepFreezeError, "Cannot deep freeze #{value}:#{value.class}"
end
end
class DeepMergeError < StandardError; end
# Perform a deep merge of two nested data structures.
#
# @param left [Object]
# @param right [Object]
# @param path [Array<String>] The traversal path followed when merging nested hashes
#
# @return [Object] The merged data structure.
def deep_merge(left, right, path = [])
ret = nil
if left.is_a?(Hash) && right.is_a?(Hash)
ret = left.merge(right) do |key, left_val, right_val|
path.push(key)
merged = deep_merge(left_val, right_val, path)
path.pop
merged
end
elsif left.is_a?(Array) && right.is_a?(Array)
ret = left.dup.concat(right)
elsif right.nil?
ret = left
elsif left.nil?
ret = right
elsif left.nil? && right.nil?
ret = nil
else
msg = "Cannot merge #{left.inspect}:#{left.class} and #{right.inspect}:#{right.class}"
unless path.empty?
msg << ' at root'
msg << path.map { |part| "[#{part.inspect}]" }.join
end
raise DeepMergeError, msg
end
ret
end
def convert(value)
value = value.to_s if value.is_a?(Symbol)
value = value.downcase if value.is_a?(String)
value
end
# Flatten the given data structure to something that's suitable to return
# as flat facts.
#
# @param path [String] The fact path to be prefixed to the given value.
# @param structure [Object] The data structure to flatten. Nested hashes
# will be recursively flattened, everything else will be returned as-is.
#
# @return [Hash] The given data structure prefixed with the given path
def flatten_structure(path, structure)
results = {}
if structure.is_a? Hash
structure.each_pair do |name, value|
new_path = "#{path}_#{name}".gsub(%r{\-|/}, '_')
results.merge! flatten_structure(new_path, value)
end
elsif structure.is_a? Array
structure.each_with_index do |value, index|
new_path = "#{path}_#{index}"
results.merge! flatten_structure(new_path, value)
end
else
results[path] = structure
end
results
end
end
end
end