lib/agnostic_backend/utilities.rb
module AgnosticBackend
module Utilities
def self.included(base)
base.send :include, InstanceMethods
base.send :extend, ClassMethods
end
module ClassMethods
end
module InstanceMethods
def with_exponential_backoff(error, &block)
attempts = 0
begin
block.call
rescue error => e
if attempts < exponential_backoff_max_attempts
sleep(exponential_backoff_sleep_time(exponential_backoff_max_time, exponential_backoff_base, attempts))
attempts += 1
retry
else
raise e
end
end
end
def exponential_backoff_max_time
@exponential_backoff_max_time ||= 4
end
def exponential_backoff_max_attempts
@exponential_backoff_max_time ||= 10
end
def exponential_backoff_base
@exponential_backoff_max_time ||= 0.2
end
def exponential_backoff_sleep_time(max, base, attempts)
temp = [max, base * 2 ** attempts].min.to_f
temp/2 + rand(0.0...temp/2)
end
def flatten(document, delimiter = '__')
def flat_hash(document, delimiter)
flatten_doc = {}
document.each do |k, v|
if v.is_a? Hash
# if we have a nested hash, traverse the hash until we find a value
# When a value is found, we return it and merge the keys with the key of the previous recursion iteration
flat_hash(v, delimiter).map do |doc_k, doc_v|
flatten_doc["#{k}"+ delimiter + "#{doc_k}"] = doc_v
end
else
flatten_doc[k.to_s] = v
end
end
return flatten_doc
end
flat_hash(document, delimiter)
end
def unflatten(document, delimiter='__')
unflatten = {}
def unflatten_hash(hash, delimiter)
key, value = hash.keys.first, hash.values.first
components = key.split(delimiter)
new_key = components.shift
if components.empty?
return {new_key => value}
else
unflat_hash = {}
unflat_hash[new_key] = unflatten_hash({components.join(delimiter) => value}, delimiter)
end
unflat_hash
end
document.each do |k, v|
unflatten.deep_merge!(unflatten_hash({k => v}, delimiter))
end
unflatten
end
def transform_nested_values(hash, proc)
hash.keys.each do |key|
if hash[key].kind_of? Hash
transform_nested_values(hash[key], proc)
else
hash[key] = proc.call(hash[key])
end
end
hash
end
def value_for_key(document, key)
keys = key.split('.')
key = keys.shift
if document.is_a?(Hash)
if document.has_key? key
value_for_key(document[key], keys.join('.'))
else
return nil
end
else
if document.present? && key.nil?
return document
else
return nil
end
end
end
def reject_blank_values_from(flat_document)
flat_document.reject { |_, v| (v.is_a?(FalseClass) ? false : v.blank?) }
end
def convert_bool_values_to_string_in(document)
document.each do |k, v|
if v.is_a?(TrueClass)
document[k] = 'true'
elsif v.is_a?(FalseClass)
document[k] = 'false'
end
end
end
def convert_to(type, value)
case type
when :integer
if value.is_a?(Fixnum)
value
elsif is_integer?(value)
value.to_i
else
value
end
when :date, :date_array
if value.is_a?(Time)
value
elsif is_date?(value)
value.to_time.utc
else
value
end
when :double
if value.is_a?(Float)
value
elsif is_float?(value)
value.to_f
else
value
end
when :boolean
if value.is_a?(TrueClass) || value.is_a?(FalseClass)
value
elsif is_boolean?(value)
convert_to_boolean(value)
else
value
end
when :string,:string_array,:text,:text_array
if value.is_a?(String)
value
else
value.to_s
end
else
value
end
end
def is_integer?(value)
(/^[-+]?\d+$/ =~ value.to_s).present?
end
def is_float?(value)
(/^[-+]?(\d*[.])?\d+$/ =~ value.to_s).present?
end
def is_boolean?(value)
value == 'true' || value == 'false'
end
def is_date?(value)
value.to_time.present? rescue false
end
def convert_to_boolean(value)
case value
when 'true'
true
when 'false'
false
end
end
end
end
end