lib/i18n/migrations/migrator.rb in i18n-migrations-0.1.4 vs lib/i18n/migrations/migrator.rb in i18n-migrations-0.2.0

- old
+ new

@@ -6,18 +6,35 @@ $LOAD_PATH.unshift(File.dirname(__FILE__)) require 'google_translate_dictionary' require 'google_spreadsheet' require 'config' +require 'locale' +require 'migration_factory' +# this class knows how to do all the things the cli needs done. +# it mostly delegates to locale to do it, often asking multiple locales to do the same thing module I18n module Migrations class Migrator + def locale_for(name) + Locale.new(name, + locales_dir: config.locales_dir, + main_locale_name: config.main_locale, + migrations: MigrationFactory.new(config.migration_dir), + dictionary: new_dictionary(name)) + end + def config @config ||= Config.new.read! end + # for testing + def config=(config) + @config = config + end + def new_migration(name) name = name.parameterize(separator: '_') file_name = "#{Time.now.strftime('%Y%m%d%H%M')}_#{name.downcase.gsub(' ', '_')}.rb" unless Dir.exist?(config.migration_dir) puts "Creating migration directory #{config.migration_dir} because it didn't exist." @@ -37,301 +54,86 @@ puts "Wrote new migration to #{file_name}" end def migrate(locale_or_all = 'all') each_locale(locale_or_all) do |locale| - update_locale_info(locale) do |data, notes| - migrate_locale(locale, data, notes) + locale.update_info do |data, notes| + locale.migrate(data, notes) end end end def rollback(locale_or_all) each_locale(locale_or_all) do |locale| - update_locale_info(locale) do |data, notes| - rollback_locale(locale, data, notes) + locale.update_info do |data, notes| + locale.rollback(data, notes) end end end def pull(locale_or_all) each_locale(locale_or_all) do |locale| - next if locale == config.main_locale - sheet = get_google_spreadsheet(locale) - pull_locale(locale, sheet) - migrate(locale) + next if locale.main_locale? + sheet = get_google_spreadsheet(locale.name) + locale.pull(sheet) + migrate(locale.name) end end def push(locale_or_all, force = false) each_locale(locale_or_all) do |locale| - next if locale == config.main_locale + next if locale.main_locale? sheet = get_google_spreadsheet(locale) unless force - pull_locale(locale, sheet) - migrate(locale) + locale.pull(sheet) + migrate(locale.name) end - push_locale(locale, sheet) + locale.push(sheet) end end def new_locale(new_locale, limit = nil) - dictionary = new_dictionary(new_locale) - new_data, new_notes = {}, {} - count = 0 - main_data = read_locale_data(config.main_locale) - main_data.each do |key, term| - new_data[key], new_notes[key] = dictionary.lookup(term) - print '.'.green - break if limit && limit < count += 1 - end - new_data['VERSION'] = main_data['VERSION'] - puts - write_locale_data_and_notes(new_locale, new_data, new_notes) + locale_for(new_locale).create end def version each_locale do |locale| - puts "#{locale}: #{locale_versions(read_locale_data(locale)).last}" + puts "#{locale.name}: #{locale.last_version}" end end def validate(locale_or_all) each_locale(locale_or_all) do |locale| - next if locale == config.main_locale - update_locale_info(locale) do |data, notes| - validate_locale(locale, data, notes) + next if locale.main_locale? + locale.update_info do |data, notes| + locale.validate(data, notes) end end end private - def validate_locale(locale, data, notes) - main_data = read_locale_data(config.main_locale) - dict = new_dictionary(locale) - main_data.each do |key, main_term| - old_term = data[key] - new_term, errors = dict.fix(main_term, old_term) - if new_term != old_term - data[key] = new_term - puts "#{"Fix".green} #{key.green}:" - puts "#{config.main_locale}: #{main_term}" - puts "#{locale} (old): #{old_term}" - puts "#{locale} (new): #{new_term}" - puts - end - replace_errors_in_notes(notes, key, errors) - if errors.length > 0 - puts "Error #{errors.join(', ').red} #{key.yellow}" - puts "#{config.main_locale.bold}: #{main_term}" - puts "#{locale.bold}: #{old_term}" - puts - end + def each_locale(name = 'all') + (name == 'all' ? all_locale_names : [name]).each do |l| + yield locale_for(l) end end - def replace_errors_in_notes(all_notes, key, errors) - return if all_notes[key].blank? && errors.empty? - - notes = all_notes[key] - notes = notes.present? ? notes.split("\n") : [] - notes = notes.reject { |n| n.start_with?("[error:") } - all_notes[key] = (errors.map{|e| "[error: #{e}]"} + notes).join("\n") - end - - def update_locale_info(locale) - data, notes = read_locale_data_and_notes(locale) - yield data, notes - write_locale_data_and_notes(locale, data, notes) - end - - def read_locale_data_and_notes(locale) - data = read_locale_data(locale) - notes = locale == config.main_locale ? {} : read_locale_from_file(locale, "../#{locale}_notes.yml") - [data, notes] - end - - def read_locale_data(locale) - read_locale_from_file(locale, "#{locale}.yml") - end - - def write_locale_data_and_notes(locale, data, notes) - write_locale_to_file(locale, "#{locale}.yml", data) - write_locale_to_file(locale, "../#{locale}_notes.yml", notes) unless locale == config.main_locale - end - - def pull_locale(locale, sheet) - puts "Pulling #{locale}" - data = {} - notes = {} - count = 0 - - (2..sheet.num_rows).each do |row| - key, value, note = sheet[row, 1], sheet[row, 3], sheet[row, 4] - if key.present? - assign_complex_key(data, key.split('.'), value.present? ? value : '') - if note.present? - assign_complex_key(notes, key.split('.'), note) - end - count += 1 - print '.' - end - end - - write_locale_data_and_notes(locale, data, notes) - write_locale_remote_version(locale, data) - - puts "\n#{count} keys" - end - - def write_locale_remote_version(locale, data) - write_locale_to_file(locale, - "../#{locale}_remote_version.yml", - { 'VERSION' => locale_versions(data) }) - end - - def push_locale(locale, sheet) - main_data = read_locale_data(config.main_locale) - data, notes = read_locale_data_and_notes(locale) - row = 2 - - puts "Pushing #{locale}" - - main_data.each do |key, value| - sheet[row, 1] = key - sheet[row, 2] = value - sheet[row, 3] = data[key] - sheet[row, 4] = notes[key] - row += 1 - print '.' - end - - sheet.synchronize - write_locale_remote_version(locale, data) - - puts "\n#{main_data.keys.length} keys" - end - - def migrate_locale(locale, data, notes) - missing_versions = (all_versions - locale_versions(data)).sort - if missing_versions.empty? - puts "#{locale}: up-to-date" - return - end - puts "#{locale}: Migrating #{missing_versions.join(', ')}" - missing_versions.each do |version| - migrate_locale_to_version(locale, data, notes, version, :up) - end - end - - def rollback_locale(locale, data, notes) - last_version = locale_versions(data).last - if last_version == nil - puts "#{locale}: no more migrations to roll back" - return - end - puts "#{locale}: Rolling back #{last_version}" - raise "Can't find #{last_version}.rb to rollback" unless all_versions.include?(last_version) - - migrate_locale_to_version(locale, data, notes, last_version, :down) - end - - def migrate_locale_to_version(locale, data, notes, version, direction) - filename = File.join(config.migration_dir, "#{version}.rb") - require filename - migration_class_name = version.gsub(/^\d{12}_/, '').camelcase - dictionary = new_dictionary(locale) - - migration = begin - migration_class_name.constantize.new(locale, data, notes, dictionary, direction) - rescue - raise "Couldn't load migration #{migration_class_name} in #{filename}" - end - - migration.change - - if direction == :up - data['VERSION'] = (locale_versions(data) + [version]).join("\n") - else - data['VERSION'] = (locale_versions(data) - [version]).join("\n") - end - end - - def locale_versions(data) - (data['VERSION'] && data['VERSION'].split("\n")) || [] - end - - def read_locale_from_file(locale, filename) - filename = File.join(config.locales_dir, filename) - begin - hash = {} - add_to_hash(hash, YAML.load(File.read(filename))[locale.to_s]) - hash - rescue - puts "Error loading #{filename}" - raise - end - end - - def write_locale_to_file(locale, filename, hash) - # we have to go from flat keys -> values to a hash that contains other hashes - complex_hash = {} - hash.keys.sort.each do |key| - value = hash[key] - assign_complex_key(complex_hash, key.split('.'), value.present? ? value : '') - end - File.open(File.join(config.locales_dir, filename), 'w') do |file| - file << { locale => complex_hash }.to_yaml - end - end - - def assign_complex_key(hash, key, value) - if key.length == 0 - # should never get here - elsif key.length == 1 - hash[key[0]] = value - else - hash[key[0]] ||= {} - assign_complex_key(hash[key[0]], key[1..-1], value) - end - end - - # flattens new_hash and adds it to hash - def add_to_hash(hash, new_hash, prefix = []) - return unless new_hash - - new_hash.each do |key, value| - if value.is_a?(Hash) - add_to_hash(hash, value, prefix + [key]) - else - hash[(prefix + [key]).join('.')] = value - end - end - end - - def each_locale(locale = 'all') - (locale == 'all' ? all_locales : [locale]).each do |l| - yield l - end - end - - def all_locales + def all_locale_names [config.main_locale] + config.other_locales end - def all_versions - Dir[config.migration_dir + '/*.rb'].map { |name| File.basename(name).gsub('.rb', '') } - end - - def new_dictionary(locale) - GoogleTranslateDictionary.new(config.main_locale, locale, config.google_translate_api_key, config.do_not_translate) - end - def get_google_spreadsheet(locale) GoogleSpreadsheet.new(locale, config.google_spreadsheets[locale], config.google_service_account_key_path).sheet + end + + def new_dictionary(locale) + GoogleTranslateDictionary.new(from_locale: config.main_locale, + to_locale: locale, + key: config.google_translate_api_key, + do_not_translate: config.do_not_translate) end end end end