Locale/localeapp

View on GitHub
lib/localeapp/updater.rb

Summary

Maintainability
A
2 hrs
Test Coverage
require 'fileutils'

module Localeapp
  class Updater

    def update(data)
      data['locales'].each do |short_code|
        filename = File.join(Localeapp.configuration.translation_data_directory, "#{short_code}.yml")

        if File.exist?(filename)
          translations = Localeapp.load_yaml_file(filename)
          if data['translations'] && data['translations'][short_code]
            new_data = { short_code => data['translations'][short_code] }
            translations = deep_merge translations, new_data
          end
        else
          translations = { short_code => data['translations'][short_code] }
        end

        if data['deleted']
          data['deleted'].each do |key|
            remove_flattened_key!(translations, short_code, key)
          end
        end

        if translations[short_code]
          atomic_write(filename) do |file|
            file.write generate_yaml(translations)
          end
        end
      end
    end

    def dump(data)
      data.each do |locale, translations|
        filename = File.join(Localeapp.configuration.translation_data_directory, "#{locale}.yml")
        atomic_write(filename) do |file|
          file.write generate_yaml({locale => translations})
        end
      end
    end

    private

    def deep_merge(original, other)
      original.merge other do |_, a, b|
        if Hash === a && Hash === b
          deep_merge a, b
        else
          b
        end
      end
    end

    def generate_yaml(translations)
      YAML.dump(translations, :line_width => -1)[4..-1]
    end

    def remove_flattened_key!(hash, locale, key)
      keys = I18n.normalize_keys(locale, key, '').map(&:to_s)
      current_key = keys.shift
      remove_child_keys!(hash[current_key], keys)
      hash
    end

    def remove_child_keys!(sub_hash, keys)
      return if sub_hash.nil?
      current_key = keys.shift
      if keys.empty?
        # delete key except if key is now used as a namespace for a child_hash
        unless sub_hash[current_key].is_a?(Hash)
          sub_hash.delete(current_key)
        end
      else
        child_hash = sub_hash[current_key]
        unless child_hash.nil?
          remove_child_keys!(child_hash, keys)
          if child_hash.empty?
            sub_hash.delete(current_key)
          end
        end
      end
    end

    # originally from ActiveSupport
    def atomic_write(file_name, temp_dir = Dir.tmpdir)
      target_dir = File.dirname(file_name)
      unless File.directory?(target_dir)
        raise "Could not write locale file, please make sure that #{target_dir} exists and is writable"
      end

      permissions = File.stat(file_name).mode if File.exist?(file_name)

      temp_file = Tempfile.new(File.basename(file_name), temp_dir)
      yield temp_file
      temp_file.close
      # heroku has /tmp on a different fs
      # so move first to sure they're on the same fs
      # so rename will work
      FileUtils.mv(temp_file.path, "#{file_name}.tmp")
      File.rename("#{file_name}.tmp", file_name)

      # chmod the file to its previous permissions
      # or set default permissions to 644
      File.chmod(permissions ? permissions : 0644 , file_name)
    end
  end
end