i18n_flow 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/Gemfile.lock +1 -1
- data/README.md +5 -1
- data/lib/i18n_flow/cli.rb +9 -7
- data/lib/i18n_flow/cli/copy_command.rb +3 -24
- data/lib/i18n_flow/cli/format_command.rb +57 -0
- data/lib/i18n_flow/cli/help_command.rb +1 -0
- data/lib/i18n_flow/cli/lint_command/ascii.erb +6 -0
- data/lib/i18n_flow/cli/lint_command/markdown.erb +8 -2
- data/lib/i18n_flow/corrector.rb +40 -0
- data/lib/i18n_flow/formatter.rb +20 -0
- data/lib/i18n_flow/validator/errors.rb +27 -1
- data/lib/i18n_flow/validator/symmetry.rb +20 -13
- data/lib/i18n_flow/version.rb +1 -1
- data/lib/i18n_flow/yaml_ast_proxy.rb +36 -0
- data/lib/i18n_flow/yaml_ast_proxy/mapping.rb +14 -0
- data/lib/i18n_flow/yaml_ast_proxy/node.rb +8 -1
- data/lib/i18n_flow/yaml_ast_proxy/sequence.rb +1 -1
- data/spec/lib/i18n_flow/corrector_spec.rb +197 -0
- data/spec/lib/i18n_flow/formatter_spec.rb +137 -0
- data/spec/lib/i18n_flow/validator/symmetry_spec.rb +23 -2
- metadata +9 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: '08d2cf8d25372c57651808089a31dedc34a52f81'
         | 
| 4 | 
            +
              data.tar.gz: 2a96774f923502e0dd534c8b632acf4f7e5d232f
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: '03463815dc69ad05dfb2e4b048201441e212a165947a546f4b8e1828b72f32cda5e1f16ab0d335fe9881bc949cfad7a5fa0eb6acf6ebc50a312cfa5d2080b17b'
         | 
| 7 | 
            +
              data.tar.gz: 4bd655b11d659a008101bd28f113c65d8d03ea59ab7fe969c1c702e2dabacca9b52c20da33f4e2b4c3981f9b74af73cc87dbc11ae96fa269f2bcfd00fdc7f591
         | 
    
        data/Gemfile.lock
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | @@ -6,7 +6,7 @@ i18n_flow (beta) | |
| 6 6 | 
             
            [](https://travis-ci.org/creasty/i18n_flow)
         | 
| 7 7 | 
             
            [](./LICENSE)
         | 
| 8 8 |  | 
| 9 | 
            -
            **Manage translation status in YAML file | 
| 9 | 
            +
            **Manage translation status in YAML file.**<br>
         | 
| 10 10 | 
             
            With an official [tag](http://www.yaml.org/spec/1.2/spec.html#id2784064) feature, `i18n_flow` enables you to annotate status information directly in YAML file.
         | 
| 11 11 |  | 
| 12 12 | 
             
            
         | 
| @@ -23,6 +23,9 @@ Setup | |
| 23 23 | 
             
            Add this line to your Gemfile:
         | 
| 24 24 |  | 
| 25 25 | 
             
            ```ruby
         | 
| 26 | 
            +
            gem 'i18n_flow'
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            # To use the latest version:
         | 
| 26 29 | 
             
            gem 'i18n_flow', github: 'creasty/i18n_flow'
         | 
| 27 30 | 
             
            ```
         | 
| 28 31 |  | 
| @@ -61,6 +64,7 @@ Options: | |
| 61 64 |  | 
| 62 65 | 
             
            Commands:
         | 
| 63 66 | 
             
                lint       Validate files
         | 
| 67 | 
            +
                format     Format and correct errors
         | 
| 64 68 | 
             
                search     Search contents and keys
         | 
| 65 69 | 
             
                copy       Copy translations and mark as todo
         | 
| 66 70 | 
             
                split      Split a file into proper-sized files
         | 
    
        data/lib/i18n_flow/cli.rb
    CHANGED
    
    | @@ -1,22 +1,24 @@ | |
| 1 1 | 
             
            require_relative 'util'
         | 
| 2 2 |  | 
| 3 3 | 
             
            class I18nFlow::CLI
         | 
| 4 | 
            -
              require_relative 'cli/lint_command'
         | 
| 5 | 
            -
              require_relative 'cli/split_command'
         | 
| 6 4 | 
             
              require_relative 'cli/copy_command'
         | 
| 7 | 
            -
              require_relative 'cli/ | 
| 8 | 
            -
              require_relative 'cli/version_command'
         | 
| 5 | 
            +
              require_relative 'cli/format_command'
         | 
| 9 6 | 
             
              require_relative 'cli/help_command'
         | 
| 7 | 
            +
              require_relative 'cli/lint_command'
         | 
| 10 8 | 
             
              require_relative 'cli/read_config_command'
         | 
| 9 | 
            +
              require_relative 'cli/search_command'
         | 
| 10 | 
            +
              require_relative 'cli/split_command'
         | 
| 11 | 
            +
              require_relative 'cli/version_command'
         | 
| 11 12 |  | 
| 12 13 | 
             
              COMMANDS = {
         | 
| 14 | 
            +
                'copy'        => CopyCommand,
         | 
| 15 | 
            +
                'format'      => FormatCommand,
         | 
| 16 | 
            +
                'help'        => HelpCommand,
         | 
| 13 17 | 
             
                'lint'        => LintCommand,
         | 
| 18 | 
            +
                'read_config' => ReadConfigCommand,
         | 
| 14 19 | 
             
                'search'      => SearchCommand,
         | 
| 15 20 | 
             
                'split'       => SplitCommand,
         | 
| 16 | 
            -
                'copy'        => CopyCommand,
         | 
| 17 21 | 
             
                'version'     => VersionCommand,
         | 
| 18 | 
            -
                'help'        => HelpCommand,
         | 
| 19 | 
            -
                'read_config' => ReadConfigCommand,
         | 
| 20 22 | 
             
              }
         | 
| 21 23 |  | 
| 22 24 | 
             
              attr_reader :args
         | 
| @@ -2,6 +2,7 @@ require 'psych' | |
| 2 2 | 
             
            require_relative 'command_base'
         | 
| 3 3 | 
             
            require_relative '../util'
         | 
| 4 4 | 
             
            require_relative '../parser'
         | 
| 5 | 
            +
            require_relative '../yaml_ast_proxy'
         | 
| 5 6 |  | 
| 6 7 | 
             
            class I18nFlow::CLI
         | 
| 7 8 | 
             
              class CopyCommand < CommandBase
         | 
| @@ -12,7 +13,7 @@ class I18nFlow::CLI | |
| 12 13 |  | 
| 13 14 | 
             
                  parser.parse!
         | 
| 14 15 |  | 
| 15 | 
            -
                  mark_as_todo(parser.root_proxy)
         | 
| 16 | 
            +
                  I18nFlow::YamlAstProxy.mark_as_todo(parser.root_proxy)
         | 
| 16 17 |  | 
| 17 18 | 
             
                  if locale && first_key_node
         | 
| 18 19 | 
             
                    first_key_node.value = locale
         | 
| @@ -35,11 +36,7 @@ class I18nFlow::CLI | |
| 35 36 |  | 
| 36 37 | 
             
                def first_key_node
         | 
| 37 38 | 
             
                  return @first_key_node if defined?(@first_key_node)
         | 
| 38 | 
            -
                  @first_key_node = parser.root_proxy
         | 
| 39 | 
            -
                    .send(:indexed_object)
         | 
| 40 | 
            -
                    .node
         | 
| 41 | 
            -
                    .tap { |n| break unless n.is_a?(Psych::Nodes::Mapping) }
         | 
| 42 | 
            -
                    &.tap { |n| break n.children.first }
         | 
| 39 | 
            +
                  @first_key_node = I18nFlow::YamlAstProxy.first_key_node_of(parser.root_proxy)
         | 
| 43 40 | 
             
                end
         | 
| 44 41 |  | 
| 45 42 | 
             
              private
         | 
| @@ -47,23 +44,5 @@ class I18nFlow::CLI | |
| 47 44 | 
             
                def parser
         | 
| 48 45 | 
             
                  @parser ||= I18nFlow::Parser.new(File.read(src_file), file_path: src_file)
         | 
| 49 46 | 
             
                end
         | 
| 50 | 
            -
             | 
| 51 | 
            -
                def mark_as_todo(ast)
         | 
| 52 | 
            -
                  if ast.alias?
         | 
| 53 | 
            -
                    return
         | 
| 54 | 
            -
                  end
         | 
| 55 | 
            -
                  if ast.scalar?
         | 
| 56 | 
            -
                    ast.node.tag = '!todo'
         | 
| 57 | 
            -
             | 
| 58 | 
            -
                    # https://github.com/ruby/psych/blob/f30b65befa4f0a5a8548d482424a84a2383b0284/ext/psych/yaml/emitter.c#L1187
         | 
| 59 | 
            -
                    ast.node.plain = ast.node.quoted = false
         | 
| 60 | 
            -
             | 
| 61 | 
            -
                    return
         | 
| 62 | 
            -
                  end
         | 
| 63 | 
            -
             | 
| 64 | 
            -
                  ast.each do |k, v|
         | 
| 65 | 
            -
                    mark_as_todo(v)
         | 
| 66 | 
            -
                  end
         | 
| 67 | 
            -
                end
         | 
| 68 47 | 
             
              end
         | 
| 69 48 | 
             
            end
         | 
| @@ -0,0 +1,57 @@ | |
| 1 | 
            +
            require_relative 'command_base'
         | 
| 2 | 
            +
            require_relative 'color'
         | 
| 3 | 
            +
            require_relative '../repository'
         | 
| 4 | 
            +
            require_relative '../formatter'
         | 
| 5 | 
            +
            require_relative '../corrector'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            class I18nFlow::CLI
         | 
| 8 | 
            +
              class FormatCommand < CommandBase
         | 
| 9 | 
            +
                include I18nFlow::CLI::Color
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def invoke!
         | 
| 12 | 
            +
                  puts color('==> Correcting', :yellow)
         | 
| 13 | 
            +
                  repository.asts_by_scope.each do |scope, locale_trees|
         | 
| 14 | 
            +
                    locale_pairs.each do |(master, slave)|
         | 
| 15 | 
            +
                      master_tree = locale_trees[master]
         | 
| 16 | 
            +
                      slave_tree = locale_trees[slave]
         | 
| 17 | 
            +
                      next unless master_tree && slave_tree
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                      puts slave_tree.file_path
         | 
| 20 | 
            +
                      correct(slave_tree, master_tree)
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  puts
         | 
| 25 | 
            +
                  puts color('==> Formatting', :yellow)
         | 
| 26 | 
            +
                  repository.asts_by_path.each do |path, tree|
         | 
| 27 | 
            +
                    puts path
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                    format(tree)
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                    output_path = I18nFlow.config.base_path.join(tree.file_path)
         | 
| 32 | 
            +
                    File.write(output_path, tree.to_yaml)
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
              private
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                def locale_pairs
         | 
| 39 | 
            +
                  I18nFlow.config.locale_pairs
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                def repository
         | 
| 43 | 
            +
                  @repository ||= I18nFlow::Repository.new(
         | 
| 44 | 
            +
                    base_path:     I18nFlow.config.base_path,
         | 
| 45 | 
            +
                    glob_patterns: I18nFlow.config.glob_patterns,
         | 
| 46 | 
            +
                  )
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                def correct(slave_tree, master_tree)
         | 
| 50 | 
            +
                  I18nFlow::Corrector.new(slave_tree, master_tree).correct!
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                def format(tree)
         | 
| 54 | 
            +
                  I18nFlow::Formatter.new(tree).format!
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
              end
         | 
| 57 | 
            +
            end
         | 
| @@ -27,9 +27,15 @@ | |
| 27 27 | 
             
                <%- when I18nFlow::Validator::InvalidTodoError -%>
         | 
| 28 28 | 
             
                    Todo cannot be annotated on a mapping/sequence
         | 
| 29 29 | 
             
                <%- when I18nFlow::Validator::TodoContentError -%>
         | 
| 30 | 
            +
                  <%- if err.inverse -%>
         | 
| 31 | 
            +
                    It has "!todo" but the content diverges from the foreign file
         | 
| 32 | 
            +
                    foreign: <%= err.expect %>
         | 
| 33 | 
            +
                    master:  <%= err.actual %>
         | 
| 34 | 
            +
                  <%- else -%>
         | 
| 30 35 | 
             
                    It has "!todo" but the content diverges from the master file
         | 
| 31 36 | 
             
                    master:  <%= err.expect %>
         | 
| 32 37 | 
             
                    foreign: <%= err.actual %>
         | 
| 38 | 
            +
                  <%- end -%>
         | 
| 33 39 | 
             
                <%- when I18nFlow::Validator::InvalidLocaleError -%>
         | 
| 34 40 | 
             
                    It has "!only" but the locale is invalid
         | 
| 35 41 | 
             
                    valid: [<%= err.expect.join(', ') %>]
         | 
| @@ -32,9 +32,15 @@ An extra key found | |
| 32 32 | 
             
                <%- when I18nFlow::Validator::InvalidTodoError -%>
         | 
| 33 33 | 
             
            Todo cannot be annotated on a mapping/sequence
         | 
| 34 34 | 
             
                <%- when I18nFlow::Validator::TodoContentError -%>
         | 
| 35 | 
            +
                  <%- if err.inverse -%>
         | 
| 36 | 
            +
            It has "!todo" but the content diverges from the foreign file
         | 
| 37 | 
            +
            foreign: <%= err.expect %>
         | 
| 38 | 
            +
            master:  <%= err.actual %>
         | 
| 39 | 
            +
                  <%- else -%>
         | 
| 35 40 | 
             
            It has "!todo" but the content diverges from the master file
         | 
| 36 | 
            -
            master:  | 
| 37 | 
            -
            foreign:  | 
| 41 | 
            +
            master:  <%= err.expect %>
         | 
| 42 | 
            +
            foreign: <%= err.actual %>
         | 
| 43 | 
            +
                  <%- end -%>
         | 
| 38 44 | 
             
                <%- when I18nFlow::Validator::InvalidLocaleError -%>
         | 
| 39 45 | 
             
            It has "!only" but the locale is invalid
         | 
| 40 46 | 
             
            valid: `[<%= err.expect.join(', ') %>]`
         | 
| @@ -0,0 +1,40 @@ | |
| 1 | 
            +
            require_relative 'validator/symmetry'
         | 
| 2 | 
            +
            require_relative 'validator/errors'
         | 
| 3 | 
            +
            require_relative 'yaml_ast_proxy'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            class I18nFlow::Corrector
         | 
| 6 | 
            +
              attr_reader :ast_1
         | 
| 7 | 
            +
              attr_reader :ast_2
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              def initialize(ast_1, ast_2)
         | 
| 10 | 
            +
                @ast_1 = ast_1
         | 
| 11 | 
            +
                @ast_2 = ast_2
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              def correct!
         | 
| 15 | 
            +
                n1, n2 = [ast_1, ast_2]
         | 
| 16 | 
            +
                  .map { |n| I18nFlow::YamlAstProxy.first_value_node_of(n) }
         | 
| 17 | 
            +
                  .map { |n| I18nFlow::YamlAstProxy.create(n) }
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                errors = I18nFlow::Validator::Symmetry.new(n2, n1)
         | 
| 20 | 
            +
                  .tap(&:validate!)
         | 
| 21 | 
            +
                  .errors
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                errors.each do |error|
         | 
| 24 | 
            +
                  case error
         | 
| 25 | 
            +
                  when I18nFlow::Validator::MissingKeyError
         | 
| 26 | 
            +
                    src_node = error.src_node.clone
         | 
| 27 | 
            +
                    I18nFlow::YamlAstProxy.mark_as_todo(src_node)
         | 
| 28 | 
            +
                    error.dest_node[error.dest_key] = src_node.node
         | 
| 29 | 
            +
                  when I18nFlow::Validator::ExtraKeyError
         | 
| 30 | 
            +
                    if error.dest_node.mapping?
         | 
| 31 | 
            +
                      error.dest_node.delete(error.dest_key)
         | 
| 32 | 
            +
                    elsif error.dest_node.sequence?
         | 
| 33 | 
            +
                      error.dest_node.delete_at(error.dest_key)
         | 
| 34 | 
            +
                    end
         | 
| 35 | 
            +
                  when I18nFlow::Validator::TodoContentError
         | 
| 36 | 
            +
                    error.dest_node.node.value = error.src_node.node.value
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
            end
         | 
| @@ -0,0 +1,20 @@ | |
| 1 | 
            +
            class I18nFlow::Formatter
         | 
| 2 | 
            +
              attr_reader :ast
         | 
| 3 | 
            +
             | 
| 4 | 
            +
              def initialize(ast)
         | 
| 5 | 
            +
                @ast = ast
         | 
| 6 | 
            +
              end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              def format!
         | 
| 9 | 
            +
                sort_keys(ast)
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            private
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              def sort_keys(node)
         | 
| 15 | 
            +
                node.sort_keys! if node.mapping?
         | 
| 16 | 
            +
                node.each do |_, val|
         | 
| 17 | 
            +
                  sort_keys(val) if val.mapping?
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
            end
         | 
| @@ -36,17 +36,34 @@ module I18nFlow::Validator | |
| 36 36 | 
             
              end
         | 
| 37 37 |  | 
| 38 38 | 
             
              class MissingKeyError < Error
         | 
| 39 | 
            +
                attr_reader :dest_node
         | 
| 40 | 
            +
                attr_reader :dest_key
         | 
| 41 | 
            +
                attr_reader :src_node
         | 
| 42 | 
            +
             | 
| 39 43 | 
             
                def initialize(key, single: false)
         | 
| 40 44 | 
             
                  super(key)
         | 
| 41 45 | 
             
                  @single = single
         | 
| 42 46 | 
             
                end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                def set_correction_context(dest_node:, dest_key:, src_node:)
         | 
| 49 | 
            +
                  @dest_node, @dest_key, @src_node = dest_node, dest_key, src_node
         | 
| 50 | 
            +
                  self
         | 
| 51 | 
            +
                end
         | 
| 43 52 | 
             
              end
         | 
| 44 53 |  | 
| 45 54 | 
             
              class ExtraKeyError < Error
         | 
| 55 | 
            +
                attr_reader :dest_node
         | 
| 56 | 
            +
                attr_reader :dest_key
         | 
| 57 | 
            +
             | 
| 46 58 | 
             
                def initialize(key, single: false)
         | 
| 47 59 | 
             
                  super(key)
         | 
| 48 60 | 
             
                  @single = single
         | 
| 49 61 | 
             
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                def set_correction_context(dest_node:, dest_key:)
         | 
| 64 | 
            +
                  @dest_node, @dest_key = dest_node, dest_key
         | 
| 65 | 
            +
                  self
         | 
| 66 | 
            +
                end
         | 
| 50 67 | 
             
              end
         | 
| 51 68 |  | 
| 52 69 | 
             
              class InvalidTodoError < Error
         | 
| @@ -55,16 +72,25 @@ module I18nFlow::Validator | |
| 55 72 | 
             
              class TodoContentError < Error
         | 
| 56 73 | 
             
                attr_reader :expect
         | 
| 57 74 | 
             
                attr_reader :actual
         | 
| 75 | 
            +
                attr_reader :dest_node
         | 
| 76 | 
            +
                attr_reader :src_node
         | 
| 77 | 
            +
                attr_reader :inverse
         | 
| 58 78 |  | 
| 59 | 
            -
                def initialize(key, expect:, actual:)
         | 
| 79 | 
            +
                def initialize(key, expect:, actual:, inverse:)
         | 
| 60 80 | 
             
                  super(key)
         | 
| 61 81 | 
             
                  @expect = expect
         | 
| 62 82 | 
             
                  @actual = actual
         | 
| 83 | 
            +
                  @inverse = inverse
         | 
| 63 84 | 
             
                end
         | 
| 64 85 |  | 
| 65 86 | 
             
                def data
         | 
| 66 87 | 
             
                  super + [expect, actual]
         | 
| 67 88 | 
             
                end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                def set_correction_context(dest_node:, src_node:)
         | 
| 91 | 
            +
                  @dest_node, @src_node = dest_node, src_node
         | 
| 92 | 
            +
                  self
         | 
| 93 | 
            +
                end
         | 
| 68 94 | 
             
              end
         | 
| 69 95 |  | 
| 70 96 | 
             
              class InvalidLocaleError < Error
         | 
| @@ -39,7 +39,7 @@ module I18nFlow::Validator | |
| 39 39 | 
             
                    return
         | 
| 40 40 | 
             
                  end
         | 
| 41 41 |  | 
| 42 | 
            -
                  check_asymmetric_key(n1, n2, t2)&.tap do |err|
         | 
| 42 | 
            +
                  check_asymmetric_key(n1, n2, t2, key)&.tap do |err|
         | 
| 43 43 | 
             
                    errors << err if err
         | 
| 44 44 | 
             
                    return
         | 
| 45 45 | 
             
                  end
         | 
| @@ -112,28 +112,35 @@ module I18nFlow::Validator | |
| 112 112 | 
             
                  InvalidTypeError.new(n2.full_key).set_location(n2)
         | 
| 113 113 | 
             
                end
         | 
| 114 114 |  | 
| 115 | 
            -
                def check_asymmetric_key(n1, n2, t2)
         | 
| 115 | 
            +
                def check_asymmetric_key(n1, n2, t2, key)
         | 
| 116 116 | 
             
                  return false if n1&.ignored_violation == :key || n2&.ignored_violation == :key
         | 
| 117 117 | 
             
                  return if n1 && n2
         | 
| 118 118 |  | 
| 119 119 | 
             
                  if n1
         | 
| 120 120 | 
             
                    full_key = [t2.locale, *n1.scopes.drop(1)].join('.')
         | 
| 121 | 
            -
                    MissingKeyError.new(full_key) | 
| 121 | 
            +
                    MissingKeyError.new(full_key)
         | 
| 122 | 
            +
                      .set_location(t2)
         | 
| 123 | 
            +
                      .set_correction_context(dest_node: t2, dest_key: key, src_node: n1)
         | 
| 122 124 | 
             
                  else
         | 
| 123 | 
            -
                    ExtraKeyError.new(n2.full_key) | 
| 125 | 
            +
                    ExtraKeyError.new(n2.full_key)
         | 
| 126 | 
            +
                      .set_location(n2)
         | 
| 127 | 
            +
                      .set_correction_context(dest_node: t2, dest_key: key)
         | 
| 124 128 | 
             
                  end
         | 
| 125 129 | 
             
                end
         | 
| 126 130 |  | 
| 127 131 | 
             
                def check_todo_tag(n1, n2)
         | 
| 128 | 
            -
                   | 
| 129 | 
            -
             | 
| 130 | 
            -
             | 
| 131 | 
            -
             | 
| 132 | 
            -
                  elsif n2. | 
| 133 | 
            -
                     | 
| 134 | 
            -
                       | 
| 135 | 
            -
             | 
| 136 | 
            -
             | 
| 132 | 
            +
                  if n1.scalar? && n1.marked_as_todo? && !n2.marked_as_todo? && n2.value != n1.value
         | 
| 133 | 
            +
                    TodoContentError.new(n1.full_key, expect: n2.value, actual: n1.value, inverse: true)
         | 
| 134 | 
            +
                      .set_location(n1)
         | 
| 135 | 
            +
                      .set_correction_context(dest_node: n1, src_node: n2)
         | 
| 136 | 
            +
                  elsif n2.marked_as_todo?
         | 
| 137 | 
            +
                    if !n2.scalar?
         | 
| 138 | 
            +
                      InvalidTodoError.new(n2.full_key).set_location(n2)
         | 
| 139 | 
            +
                    elsif n2.value != n1.value
         | 
| 140 | 
            +
                      TodoContentError.new(n2.full_key, expect: n1.value, actual: n2.value, inverse: false)
         | 
| 141 | 
            +
                        .set_location(n2)
         | 
| 142 | 
            +
                        .set_correction_context(dest_node: n2, src_node: n1)
         | 
| 143 | 
            +
                    end
         | 
| 137 144 | 
             
                  end
         | 
| 138 145 | 
             
                end
         | 
| 139 146 |  | 
    
        data/lib/i18n_flow/version.rb
    CHANGED
    
    
| @@ -54,4 +54,40 @@ module I18nFlow::YamlAstProxy | |
| 54 54 | 
             
                stream.children << doc
         | 
| 55 55 | 
             
                create(stream)
         | 
| 56 56 | 
             
              end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
              def self.mark_as_todo(ast)
         | 
| 59 | 
            +
                if ast.alias?
         | 
| 60 | 
            +
                  return
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
                if ast.scalar?
         | 
| 63 | 
            +
                  ast.node.tag = '!todo'
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  # https://github.com/ruby/psych/blob/f30b65befa4f0a5a8548d482424a84a2383b0284/ext/psych/yaml/emitter.c#L1187
         | 
| 66 | 
            +
                  ast.node.plain = ast.node.quoted = false
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  return
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                ast.each do |k, v|
         | 
| 72 | 
            +
                  mark_as_todo(v)
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
              end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
              def self.first_key_node_of(node)
         | 
| 77 | 
            +
                first_node_of(node, 0)
         | 
| 78 | 
            +
              end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
              def self.first_value_node_of(node)
         | 
| 81 | 
            +
                first_node_of(node, 1)
         | 
| 82 | 
            +
              end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
            private
         | 
| 85 | 
            +
             | 
| 86 | 
            +
              def self.first_node_of(node, layout_offset)
         | 
| 87 | 
            +
                node
         | 
| 88 | 
            +
                  .send(:indexed_object)
         | 
| 89 | 
            +
                  .node
         | 
| 90 | 
            +
                  .tap { |n| break unless n.is_a?(Psych::Nodes::Mapping) }
         | 
| 91 | 
            +
                  &.tap { |n| break n.children[layout_offset] }
         | 
| 92 | 
            +
              end
         | 
| 57 93 | 
             
            end
         | 
| @@ -25,6 +25,12 @@ module I18nFlow::YamlAstProxy | |
| 25 25 | 
             
                end
         | 
| 26 26 | 
             
                alias []= set
         | 
| 27 27 |  | 
| 28 | 
            +
                def delete(key)
         | 
| 29 | 
            +
                  indexed_object.delete(key)
         | 
| 30 | 
            +
                  cache.delete(key)
         | 
| 31 | 
            +
                  synchronize!
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 28 34 | 
             
                def batch
         | 
| 29 35 | 
             
                  @locked = true
         | 
| 30 36 | 
             
                  yield
         | 
| @@ -49,6 +55,14 @@ module I18nFlow::YamlAstProxy | |
| 49 55 | 
             
                  end
         | 
| 50 56 | 
             
                end
         | 
| 51 57 |  | 
| 58 | 
            +
                def sort_keys!
         | 
| 59 | 
            +
                  @indexed_object = indexed_object
         | 
| 60 | 
            +
                    .sort_by { |k, v| [v.is_a?(Psych::Nodes::Mapping) ? 1 : 0, k] }
         | 
| 61 | 
            +
                    .to_h
         | 
| 62 | 
            +
                  @cache = nil
         | 
| 63 | 
            +
                  synchronize!
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
             | 
| 52 66 | 
             
              private
         | 
| 53 67 |  | 
| 54 68 | 
             
                def cache
         | 
| @@ -10,7 +10,8 @@ module I18nFlow::YamlAstProxy | |
| 10 10 | 
             
                TAG_TODO   = /^!todo(?::([,a-zA-Z_-]+))?$/
         | 
| 11 11 | 
             
                TAG_ONLY   = /^!only(?::([,a-zA-Z_-]+))?$/
         | 
| 12 12 |  | 
| 13 | 
            -
                 | 
| 13 | 
            +
                attr_accessor :node
         | 
| 14 | 
            +
                protected :node=
         | 
| 14 15 | 
             
                attr_reader :parent
         | 
| 15 16 | 
             
                attr_reader :scopes
         | 
| 16 17 | 
             
                attr_reader :file_path
         | 
| @@ -87,6 +88,12 @@ module I18nFlow::YamlAstProxy | |
| 87 88 | 
             
                  indexed_object.keys
         | 
| 88 89 | 
             
                end
         | 
| 89 90 |  | 
| 91 | 
            +
                def clone
         | 
| 92 | 
            +
                  super.tap do |n|
         | 
| 93 | 
            +
                    n.node = node.clone
         | 
| 94 | 
            +
                  end
         | 
| 95 | 
            +
                end
         | 
| 96 | 
            +
             | 
| 90 97 | 
             
              private
         | 
| 91 98 |  | 
| 92 99 | 
             
                def identity_data
         | 
| @@ -0,0 +1,197 @@ | |
| 1 | 
            +
            require 'i18n_flow/corrector'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe I18nFlow::Corrector do
         | 
| 4 | 
            +
              def correct_ast(ast_1, ast_2 = nil)
         | 
| 5 | 
            +
                I18nFlow::Corrector.new(ast_1, ast_2)
         | 
| 6 | 
            +
                  .tap(&:correct!)
         | 
| 7 | 
            +
              end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              describe '#correct!' do
         | 
| 10 | 
            +
                it 'should complement missing keys and mark as !todo' do
         | 
| 11 | 
            +
                  ast_1 = parse_yaml(<<-YAML)
         | 
| 12 | 
            +
                  es:
         | 
| 13 | 
            +
                    alfa: 'a'
         | 
| 14 | 
            +
                    delta: 'd'
         | 
| 15 | 
            +
                    echo: 'e'
         | 
| 16 | 
            +
                    golf: 'g'
         | 
| 17 | 
            +
                    hotel: 'h'
         | 
| 18 | 
            +
                  YAML
         | 
| 19 | 
            +
                  ast_2 = parse_yaml(<<-YAML)
         | 
| 20 | 
            +
                  en:
         | 
| 21 | 
            +
                    alfa: 'A'
         | 
| 22 | 
            +
                    bravo: 'B'
         | 
| 23 | 
            +
                    charlie: 'C'
         | 
| 24 | 
            +
                    delta: 'D'
         | 
| 25 | 
            +
                    echo: 'E'
         | 
| 26 | 
            +
                    foxtrot: !only 'F'
         | 
| 27 | 
            +
                    golf: 'G'
         | 
| 28 | 
            +
                    hotel: 'H'
         | 
| 29 | 
            +
                  YAML
         | 
| 30 | 
            +
                  result = parse_yaml(<<-YAML)
         | 
| 31 | 
            +
                  es:
         | 
| 32 | 
            +
                    alfa: 'a'
         | 
| 33 | 
            +
                    delta: 'd'
         | 
| 34 | 
            +
                    echo: 'e'
         | 
| 35 | 
            +
                    golf: 'g'
         | 
| 36 | 
            +
                    hotel: 'h'
         | 
| 37 | 
            +
                    bravo: !todo 'B'
         | 
| 38 | 
            +
                    charlie: !todo 'C'
         | 
| 39 | 
            +
                  YAML
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  corrected = correct_ast(ast_1, ast_2)
         | 
| 42 | 
            +
                  expect(corrected.ast_1.to_yaml).to eq(result.to_yaml)
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                it 'should complement missing elements in a sequence' do
         | 
| 46 | 
            +
                  ast_1 = parse_yaml(<<-YAML)
         | 
| 47 | 
            +
                  es:
         | 
| 48 | 
            +
                    foo:
         | 
| 49 | 
            +
                      - 'one'
         | 
| 50 | 
            +
                      - 'two'
         | 
| 51 | 
            +
                  YAML
         | 
| 52 | 
            +
                  ast_2 = parse_yaml(<<-YAML)
         | 
| 53 | 
            +
                  en:
         | 
| 54 | 
            +
                    foo:
         | 
| 55 | 
            +
                      - 'ONE'
         | 
| 56 | 
            +
                      - 'TWO'
         | 
| 57 | 
            +
                      - 'THREE'
         | 
| 58 | 
            +
                      - !only 'FOUR'
         | 
| 59 | 
            +
                  YAML
         | 
| 60 | 
            +
                  result = parse_yaml(<<-YAML)
         | 
| 61 | 
            +
                  es:
         | 
| 62 | 
            +
                    foo:
         | 
| 63 | 
            +
                      - 'one'
         | 
| 64 | 
            +
                      - 'two'
         | 
| 65 | 
            +
                      - !todo 'THREE'
         | 
| 66 | 
            +
                  YAML
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  corrected = correct_ast(ast_1, ast_2)
         | 
| 69 | 
            +
                  expect(corrected.ast_1.to_yaml).to eq(result.to_yaml)
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                it 'should delete extra keys which are not marked as !only' do
         | 
| 73 | 
            +
                  ast_1 = parse_yaml(<<-YAML)
         | 
| 74 | 
            +
                  es:
         | 
| 75 | 
            +
                    alfa: 'a'
         | 
| 76 | 
            +
                    bravo: 'b'
         | 
| 77 | 
            +
                    charlie: 'c'
         | 
| 78 | 
            +
                    delta: 'd'
         | 
| 79 | 
            +
                    echo: 'e'
         | 
| 80 | 
            +
                    foxtrot: !only 'f'
         | 
| 81 | 
            +
                    golf: 'g'
         | 
| 82 | 
            +
                    hotel: 'h'
         | 
| 83 | 
            +
                  YAML
         | 
| 84 | 
            +
                  ast_2 = parse_yaml(<<-YAML)
         | 
| 85 | 
            +
                  en:
         | 
| 86 | 
            +
                    alfa: 'A'
         | 
| 87 | 
            +
                    bravo: 'B'
         | 
| 88 | 
            +
                    charlie: 'C'
         | 
| 89 | 
            +
                    golf: 'G'
         | 
| 90 | 
            +
                    hotel: 'H'
         | 
| 91 | 
            +
                  YAML
         | 
| 92 | 
            +
                  result = parse_yaml(<<-YAML)
         | 
| 93 | 
            +
                  es:
         | 
| 94 | 
            +
                    alfa: 'a'
         | 
| 95 | 
            +
                    bravo: 'b'
         | 
| 96 | 
            +
                    charlie: 'c'
         | 
| 97 | 
            +
                    foxtrot: !only 'f'
         | 
| 98 | 
            +
                    golf: 'g'
         | 
| 99 | 
            +
                    hotel: 'h'
         | 
| 100 | 
            +
                  YAML
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                  corrected = correct_ast(ast_1, ast_2)
         | 
| 103 | 
            +
                  expect(corrected.ast_1.to_yaml).to eq(result.to_yaml)
         | 
| 104 | 
            +
                end
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                it 'should delete extra elements in a sequence' do
         | 
| 107 | 
            +
                  ast_1 = parse_yaml(<<-YAML)
         | 
| 108 | 
            +
                  es:
         | 
| 109 | 
            +
                    foo:
         | 
| 110 | 
            +
                      - 'one'
         | 
| 111 | 
            +
                      - 'two'
         | 
| 112 | 
            +
                      - 'three'
         | 
| 113 | 
            +
                      - !only 'four'
         | 
| 114 | 
            +
                  YAML
         | 
| 115 | 
            +
                  ast_2 = parse_yaml(<<-YAML)
         | 
| 116 | 
            +
                  en:
         | 
| 117 | 
            +
                    foo:
         | 
| 118 | 
            +
                      - 'ONE'
         | 
| 119 | 
            +
                      - 'TWO'
         | 
| 120 | 
            +
                  YAML
         | 
| 121 | 
            +
                  result = parse_yaml(<<-YAML)
         | 
| 122 | 
            +
                  es:
         | 
| 123 | 
            +
                    foo:
         | 
| 124 | 
            +
                      - 'one'
         | 
| 125 | 
            +
                      - 'two'
         | 
| 126 | 
            +
                      - !only 'four'
         | 
| 127 | 
            +
                  YAML
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                  corrected = correct_ast(ast_1, ast_2)
         | 
| 130 | 
            +
                  expect(corrected.ast_1.to_yaml).to eq(result.to_yaml)
         | 
| 131 | 
            +
                end
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                it 'should update translations with !todo according to the source' do
         | 
| 134 | 
            +
                  ast_1 = parse_yaml(<<-YAML)
         | 
| 135 | 
            +
                  es:
         | 
| 136 | 
            +
                    foo: !todo 'outdated'
         | 
| 137 | 
            +
                  YAML
         | 
| 138 | 
            +
                  ast_2 = parse_yaml(<<-YAML)
         | 
| 139 | 
            +
                  en:
         | 
| 140 | 
            +
                    foo: 'latest'
         | 
| 141 | 
            +
                  YAML
         | 
| 142 | 
            +
                  result = parse_yaml(<<-YAML)
         | 
| 143 | 
            +
                  es:
         | 
| 144 | 
            +
                    foo: !todo 'latest'
         | 
| 145 | 
            +
                  YAML
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                  corrected = correct_ast(ast_1, ast_2)
         | 
| 148 | 
            +
                  expect(corrected.ast_1.to_yaml).to eq(result.to_yaml)
         | 
| 149 | 
            +
                end
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                it 'should update translations with !todo (works bidirectionally)' do
         | 
| 152 | 
            +
                  ast_1 = parse_yaml(<<-YAML)
         | 
| 153 | 
            +
                  es:
         | 
| 154 | 
            +
                    foo: 'latest'
         | 
| 155 | 
            +
                  YAML
         | 
| 156 | 
            +
                  ast_2 = parse_yaml(<<-YAML)
         | 
| 157 | 
            +
                  en:
         | 
| 158 | 
            +
                    foo: !todo 'outdated'
         | 
| 159 | 
            +
                  YAML
         | 
| 160 | 
            +
                  result_1 = parse_yaml(<<-YAML)
         | 
| 161 | 
            +
                  es:
         | 
| 162 | 
            +
                    foo: 'latest'
         | 
| 163 | 
            +
                  YAML
         | 
| 164 | 
            +
                  result_2 = parse_yaml(<<-YAML)
         | 
| 165 | 
            +
                  en:
         | 
| 166 | 
            +
                    foo: !todo 'latest'
         | 
| 167 | 
            +
                  YAML
         | 
| 168 | 
            +
             | 
| 169 | 
            +
                  corrected = correct_ast(ast_1, ast_2)
         | 
| 170 | 
            +
                  expect(corrected.ast_1.to_yaml).to eq(result_1.to_yaml)
         | 
| 171 | 
            +
                  expect(corrected.ast_2.to_yaml).to eq(result_2.to_yaml)
         | 
| 172 | 
            +
                end
         | 
| 173 | 
            +
             | 
| 174 | 
            +
                it 'should update translations with !todo (synchronize)' do
         | 
| 175 | 
            +
                  ast_1 = parse_yaml(<<-YAML)
         | 
| 176 | 
            +
                  es:
         | 
| 177 | 
            +
                    foo: !todo 'outdated'
         | 
| 178 | 
            +
                  YAML
         | 
| 179 | 
            +
                  ast_2 = parse_yaml(<<-YAML)
         | 
| 180 | 
            +
                  en:
         | 
| 181 | 
            +
                    foo: !todo 'latest'
         | 
| 182 | 
            +
                  YAML
         | 
| 183 | 
            +
                  result_1 = parse_yaml(<<-YAML)
         | 
| 184 | 
            +
                  es:
         | 
| 185 | 
            +
                    foo: !todo 'latest'
         | 
| 186 | 
            +
                  YAML
         | 
| 187 | 
            +
                  result_2 = parse_yaml(<<-YAML)
         | 
| 188 | 
            +
                  en:
         | 
| 189 | 
            +
                    foo: !todo 'latest'
         | 
| 190 | 
            +
                  YAML
         | 
| 191 | 
            +
             | 
| 192 | 
            +
                  corrected = correct_ast(ast_1, ast_2)
         | 
| 193 | 
            +
                  expect(corrected.ast_1.to_yaml).to eq(result_1.to_yaml)
         | 
| 194 | 
            +
                  expect(corrected.ast_2.to_yaml).to eq(result_2.to_yaml)
         | 
| 195 | 
            +
                end
         | 
| 196 | 
            +
              end
         | 
| 197 | 
            +
            end
         | 
| @@ -0,0 +1,137 @@ | |
| 1 | 
            +
            require 'i18n_flow/formatter'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe I18nFlow::Formatter do
         | 
| 4 | 
            +
              def format_ast(ast)
         | 
| 5 | 
            +
                I18nFlow::Formatter.new(ast)
         | 
| 6 | 
            +
                  .tap(&:format!)
         | 
| 7 | 
            +
                  .ast
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              describe '#format!' do
         | 
| 11 | 
            +
                it 'should remove extra whitespaces' do
         | 
| 12 | 
            +
                  ast = parse_yaml(<<-YAML)
         | 
| 13 | 
            +
                  en:
         | 
| 14 | 
            +
                    alfa: 'A'
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    bravo:   'B'
         | 
| 17 | 
            +
                    charlie: 'C'
         | 
| 18 | 
            +
                    delta:   'D'
         | 
| 19 | 
            +
                    echo:    'E'
         | 
| 20 | 
            +
                    foxtrot: 'F'
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                    golf: 'G'
         | 
| 23 | 
            +
                    hotel: 'H'
         | 
| 24 | 
            +
                  YAML
         | 
| 25 | 
            +
                  result = parse_yaml(<<-YAML)
         | 
| 26 | 
            +
                  en:
         | 
| 27 | 
            +
                    alfa: 'A'
         | 
| 28 | 
            +
                    bravo: 'B'
         | 
| 29 | 
            +
                    charlie: 'C'
         | 
| 30 | 
            +
                    delta: 'D'
         | 
| 31 | 
            +
                    echo: 'E'
         | 
| 32 | 
            +
                    foxtrot: 'F'
         | 
| 33 | 
            +
                    golf: 'G'
         | 
| 34 | 
            +
                    hotel: 'H'
         | 
| 35 | 
            +
                  YAML
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  formatted = format_ast(ast)
         | 
| 38 | 
            +
                  expect(formatted.to_yaml).to eq(result.to_yaml)
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                context 'sort keys' do
         | 
| 42 | 
            +
                  it 'should sort keys' do
         | 
| 43 | 
            +
                    ast = parse_yaml(<<-YAML)
         | 
| 44 | 
            +
                    en:
         | 
| 45 | 
            +
                      echo: 'E'
         | 
| 46 | 
            +
                      delta: 'D'
         | 
| 47 | 
            +
                      foxtrot: 'F'
         | 
| 48 | 
            +
                      alfa: 'A'
         | 
| 49 | 
            +
                      hotel: 'H'
         | 
| 50 | 
            +
                      bravo: 'B'
         | 
| 51 | 
            +
                      charlie: 'C'
         | 
| 52 | 
            +
                      golf: 'G'
         | 
| 53 | 
            +
                    YAML
         | 
| 54 | 
            +
                    result = parse_yaml(<<-YAML)
         | 
| 55 | 
            +
                    en:
         | 
| 56 | 
            +
                      alfa: 'A'
         | 
| 57 | 
            +
                      bravo: 'B'
         | 
| 58 | 
            +
                      charlie: 'C'
         | 
| 59 | 
            +
                      delta: 'D'
         | 
| 60 | 
            +
                      echo: 'E'
         | 
| 61 | 
            +
                      foxtrot: 'F'
         | 
| 62 | 
            +
                      golf: 'G'
         | 
| 63 | 
            +
                      hotel: 'H'
         | 
| 64 | 
            +
                    YAML
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                    formatted = format_ast(ast)
         | 
| 67 | 
            +
                    expect(formatted.to_yaml).to eq(result.to_yaml)
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                  it 'should sort keys recursively' do
         | 
| 71 | 
            +
                    ast = parse_yaml(<<-YAML)
         | 
| 72 | 
            +
                    en:
         | 
| 73 | 
            +
                      alfa: 'A'
         | 
| 74 | 
            +
                      bravo:
         | 
| 75 | 
            +
                        delta: 'D'
         | 
| 76 | 
            +
                        charlie: 'C'
         | 
| 77 | 
            +
                        echo:
         | 
| 78 | 
            +
                          foxtrot: 'F'
         | 
| 79 | 
            +
                          hotel: 'H'
         | 
| 80 | 
            +
                          golf: 'G'
         | 
| 81 | 
            +
                    YAML
         | 
| 82 | 
            +
                    result = parse_yaml(<<-YAML)
         | 
| 83 | 
            +
                    en:
         | 
| 84 | 
            +
                      alfa: 'A'
         | 
| 85 | 
            +
                      bravo:
         | 
| 86 | 
            +
                        charlie: 'C'
         | 
| 87 | 
            +
                        delta: 'D'
         | 
| 88 | 
            +
                        echo:
         | 
| 89 | 
            +
                          foxtrot: 'F'
         | 
| 90 | 
            +
                          golf: 'G'
         | 
| 91 | 
            +
                          hotel: 'H'
         | 
| 92 | 
            +
                    YAML
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                    formatted = format_ast(ast)
         | 
| 95 | 
            +
                    expect(formatted.to_yaml).to eq(result.to_yaml)
         | 
| 96 | 
            +
                  end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                  it 'should move mappings at the bottom' do
         | 
| 99 | 
            +
                    ast = parse_yaml(<<-YAML)
         | 
| 100 | 
            +
                    en:
         | 
| 101 | 
            +
                      alfa: 'A'
         | 
| 102 | 
            +
                      bravo: 'B'
         | 
| 103 | 
            +
                      charlie:
         | 
| 104 | 
            +
                        - 'one'
         | 
| 105 | 
            +
                        - 'two'
         | 
| 106 | 
            +
                      delta: 'D'
         | 
| 107 | 
            +
                      echo: 'E'
         | 
| 108 | 
            +
                      foxtrot:
         | 
| 109 | 
            +
                        bar: 'bar'
         | 
| 110 | 
            +
                        foo: 'foo'
         | 
| 111 | 
            +
                      golf: 'G'
         | 
| 112 | 
            +
                      hotel:
         | 
| 113 | 
            +
                        baz: 'baz'
         | 
| 114 | 
            +
                    YAML
         | 
| 115 | 
            +
                    result = parse_yaml(<<-YAML)
         | 
| 116 | 
            +
                    en:
         | 
| 117 | 
            +
                      alfa: 'A'
         | 
| 118 | 
            +
                      bravo: 'B'
         | 
| 119 | 
            +
                      charlie:
         | 
| 120 | 
            +
                        - 'one'
         | 
| 121 | 
            +
                        - 'two'
         | 
| 122 | 
            +
                      delta: 'D'
         | 
| 123 | 
            +
                      echo: 'E'
         | 
| 124 | 
            +
                      golf: 'G'
         | 
| 125 | 
            +
                      foxtrot:
         | 
| 126 | 
            +
                        bar: 'bar'
         | 
| 127 | 
            +
                        foo: 'foo'
         | 
| 128 | 
            +
                      hotel:
         | 
| 129 | 
            +
                        baz: 'baz'
         | 
| 130 | 
            +
                    YAML
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                    formatted = format_ast(ast)
         | 
| 133 | 
            +
                    expect(formatted.to_yaml).to eq(result.to_yaml)
         | 
| 134 | 
            +
                  end
         | 
| 135 | 
            +
                end
         | 
| 136 | 
            +
              end
         | 
| 137 | 
            +
            end
         | 
| @@ -377,7 +377,7 @@ describe I18nFlow::Validator::Symmetry do | |
| 377 377 | 
             
                    ])
         | 
| 378 378 | 
             
                  end
         | 
| 379 379 |  | 
| 380 | 
            -
                  it 'should fail if texts are different' do
         | 
| 380 | 
            +
                  it 'should fail if texts are different from the master' do
         | 
| 381 381 | 
             
                    ast_1 = parse_yaml(<<-YAML)['en']
         | 
| 382 382 | 
             
                    en:
         | 
| 383 383 | 
             
                      key_1: text_1
         | 
| @@ -394,7 +394,28 @@ describe I18nFlow::Validator::Symmetry do | |
| 394 394 | 
             
                    validator.validate!
         | 
| 395 395 |  | 
| 396 396 | 
             
                    expect(validator.errors).to eq([
         | 
| 397 | 
            -
                      I18nFlow::Validator::TodoContentError.new('ja.key_2', expect: 'text_2', actual: 'text_9'),
         | 
| 397 | 
            +
                      I18nFlow::Validator::TodoContentError.new('ja.key_2', expect: 'text_2', actual: 'text_9', inverse: false),
         | 
| 398 | 
            +
                    ])
         | 
| 399 | 
            +
                  end
         | 
| 400 | 
            +
             | 
| 401 | 
            +
                  it 'should fail if texts are different from the foreign' do
         | 
| 402 | 
            +
                    ast_1 = parse_yaml(<<-YAML)['en']
         | 
| 403 | 
            +
                    en:
         | 
| 404 | 
            +
                      key_1: text_1
         | 
| 405 | 
            +
                      key_2: !todo text_9
         | 
| 406 | 
            +
                    YAML
         | 
| 407 | 
            +
                    ast_2 = parse_yaml(<<-YAML)['ja']
         | 
| 408 | 
            +
                    ja:
         | 
| 409 | 
            +
                      key_1: text_1
         | 
| 410 | 
            +
                      key_2: text_2
         | 
| 411 | 
            +
                    YAML
         | 
| 412 | 
            +
             | 
| 413 | 
            +
                    allow(validator).to receive(:ast_1).and_return(ast_1)
         | 
| 414 | 
            +
                    allow(validator).to receive(:ast_2).and_return(ast_2)
         | 
| 415 | 
            +
                    validator.validate!
         | 
| 416 | 
            +
             | 
| 417 | 
            +
                    expect(validator.errors).to eq([
         | 
| 418 | 
            +
                      I18nFlow::Validator::TodoContentError.new('en.key_2', expect: 'text_2', actual: 'text_9', inverse: true),
         | 
| 398 419 | 
             
                    ])
         | 
| 399 420 | 
             
                  end
         | 
| 400 421 | 
             
                end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: i18n_flow
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.2.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Yuki Iwanaga
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: exe
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2019-07- | 
| 11 | 
            +
            date: 2019-07-10 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: psych
         | 
| @@ -125,6 +125,7 @@ files: | |
| 125 125 | 
             
            - lib/i18n_flow/cli/color.rb
         | 
| 126 126 | 
             
            - lib/i18n_flow/cli/command_base.rb
         | 
| 127 127 | 
             
            - lib/i18n_flow/cli/copy_command.rb
         | 
| 128 | 
            +
            - lib/i18n_flow/cli/format_command.rb
         | 
| 128 129 | 
             
            - lib/i18n_flow/cli/help_command.rb
         | 
| 129 130 | 
             
            - lib/i18n_flow/cli/lint_command.rb
         | 
| 130 131 | 
             
            - lib/i18n_flow/cli/lint_command/ascii.erb
         | 
| @@ -140,6 +141,8 @@ files: | |
| 140 141 | 
             
            - lib/i18n_flow/cli/split_command.rb
         | 
| 141 142 | 
             
            - lib/i18n_flow/cli/version_command.rb
         | 
| 142 143 | 
             
            - lib/i18n_flow/configuration.rb
         | 
| 144 | 
            +
            - lib/i18n_flow/corrector.rb
         | 
| 145 | 
            +
            - lib/i18n_flow/formatter.rb
         | 
| 143 146 | 
             
            - lib/i18n_flow/parser.rb
         | 
| 144 147 | 
             
            - lib/i18n_flow/repository.rb
         | 
| 145 148 | 
             
            - lib/i18n_flow/search.rb
         | 
| @@ -162,6 +165,8 @@ files: | |
| 162 165 | 
             
            - spec/lib/i18n_flow/cli/help_command_spec.rb
         | 
| 163 166 | 
             
            - spec/lib/i18n_flow/cli/version_command_spec.rb
         | 
| 164 167 | 
             
            - spec/lib/i18n_flow/configuration_spec.rb
         | 
| 168 | 
            +
            - spec/lib/i18n_flow/corrector_spec.rb
         | 
| 169 | 
            +
            - spec/lib/i18n_flow/formatter_spec.rb
         | 
| 165 170 | 
             
            - spec/lib/i18n_flow/repository_spec.rb
         | 
| 166 171 | 
             
            - spec/lib/i18n_flow/splitter/merger_spec.rb
         | 
| 167 172 | 
             
            - spec/lib/i18n_flow/util_spec.rb
         | 
| @@ -201,6 +206,8 @@ test_files: | |
| 201 206 | 
             
            - spec/lib/i18n_flow/cli/help_command_spec.rb
         | 
| 202 207 | 
             
            - spec/lib/i18n_flow/cli/version_command_spec.rb
         | 
| 203 208 | 
             
            - spec/lib/i18n_flow/configuration_spec.rb
         | 
| 209 | 
            +
            - spec/lib/i18n_flow/corrector_spec.rb
         | 
| 210 | 
            +
            - spec/lib/i18n_flow/formatter_spec.rb
         | 
| 204 211 | 
             
            - spec/lib/i18n_flow/repository_spec.rb
         | 
| 205 212 | 
             
            - spec/lib/i18n_flow/splitter/merger_spec.rb
         | 
| 206 213 | 
             
            - spec/lib/i18n_flow/util_spec.rb
         |