lib/translation_io/flat_hash.rb
module TranslationIO
module FlatHash
class << self
def to_flat_hash(hash)
hash = brackets_to_joker!(hash)
hash = remove_reserved_keys!(hash)
get_flat_hash_for_level(hash)
end
def to_hash(flat_hash, remove_empty_keys = false)
hash = {}
if remove_empty_keys
flat_hash = flat_hash.reject { |k, v| v.blank? && !k.end_with?(']') }
end
flat_hash.each_pair do |key, value|
build_hash_with_flat(hash, key, value)
end
joker_to_brackets!(hash)
end
private
def build_hash_with_flat(hash, key_string, value)
current_object = hash
splitted = key_string.split(/\.|\[/, 2)
current_key = splitted[0] # first is always a hash
key_string = splitted[1]
if key_string.blank? # if only one key like { 'en' => 'salut' }
current_object[current_key] = value
else
# Put back '[' if needed
if key_string.count(']') > key_string.count('[')
key_string = '[' + key_string
end
while key_string != '' && !current_object.is_a?(String) && !current_object.nil?
# Next is array
if key_string[0] == '['
array_pos = key_string.split(']', 2)[0]
array_pos = array_pos.split('[', 2)[1].to_i
key_string = key_string.split(']', 2).count == 2 ? key_string.split(']', 2)[1] : ""
key_string = key_string[1..-1] if key_string[0] == '.'
if no_key?(current_object, current_key)
current_object[current_key] = []
end
current_object = current_object[current_key]
current_key = array_pos
# array is terminal
if key_string == ''
current_object[array_pos] = value if current_object.is_a?(Array)
end
# next is hash
elsif key_string[0] != '[' && (key_string.include?('.') || key_string.include?('['))
splitted = key_string.split(/\.|\[/, 2)
new_key = splitted[0]
key_string = splitted[1]
# Put back '[' if needed
if key_string.count(']') > key_string.count('[')
key_string = '[' + key_string
end
if no_key?(current_object, current_key)
current_object[current_key] = {}
end
current_object = current_object[current_key]
current_key = new_key
# next (last) is value
else
new_key = key_string
if no_key?(current_object, current_key)
current_object[current_key] = {}
end
current_object = current_object[current_key]
current_object[new_key] = value if current_object.is_a?(Hash)
key_string = ''
end
end
end
end
def no_key?(array_or_hash, key)
is_a_hash_without_key = array_or_hash.is_a?(Hash) && (!array_or_hash.has_key?(key) || array_or_hash[key] == nil)
is_an_array_without_key = array_or_hash.is_a?(Array) && array_or_hash[key] == nil
is_a_hash_without_key || is_an_array_without_key
end
def get_flat_hash_for_level(value, parent_key = nil)
flat_hash = {}
if value.is_a? Hash
value.each_pair do |key, value|
current_level_key = [ parent_key, key ].reject(&:blank?).join('.')
flat_hash.merge!(get_flat_hash_for_level(value, current_level_key))
end
elsif value.is_a? Array
value.each_with_index do |item, index|
flat_hash.merge!(get_flat_hash_for_level(item, "#{parent_key}[#{index}]"))
end
else
flat_hash[parent_key] = value
end
flat_hash
end
def brackets_to_joker!(h)
gsub_keys!(h, '[', ']', '<@~<', '>@~>')
end
def joker_to_brackets!(h)
gsub_keys!(h, '<@~<', '>@~>', '[', ']')
end
def gsub_keys!(h, from_1, from_2, to_1, to_2)
if h.is_a?(Hash)
h.keys.each do |key|
if key.to_s.include?(from_1) || key.to_s.include?(from_2)
new_key = key.to_s.gsub(from_1, to_1).gsub(from_2, to_2)
else
new_key = key
end
h[new_key] = h.delete(key)
gsub_keys!(h[new_key], from_1, from_2, to_1, to_2)
end
elsif h.respond_to?(:each)
h.each { |e| gsub_keys!(e, from_1, from_2, to_1, to_2) }
end
h
end
def remove_reserved_keys!(h)
if h.is_a?(Hash)
h.keys.each do |key|
if [TrueClass, FalseClass].include?(key.class)
# # This warning is commented because Rails admin uses bad keys: https://github.com/sferik/rails_admin/blob/master/config/locales/rails_admin.en.yml
# TranslationIO.info("Warning - We found some YAML protected keys in your project, they will not be synchronized: 'yes', 'no', 'y', 'n', 'on', 'off', 'true', 'false'")
# TranslationIO.info(h.inspect)
h.delete(key)
else
remove_reserved_keys!(h[key])
end
end
elsif h.respond_to?(:each)
h.each { |e| remove_reserved_keys!(e) }
end
h
end
end
end
end