i18n-tasks 0.9.6 → 0.9.7

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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/Rakefile +2 -1
  4. data/bin/i18n-tasks +1 -0
  5. data/config/locales/en.yml +15 -9
  6. data/config/locales/ru.yml +80 -74
  7. data/i18n-tasks.gemspec +9 -8
  8. data/lib/i18n/tasks.rb +2 -5
  9. data/lib/i18n/tasks/cli.rb +14 -18
  10. data/lib/i18n/tasks/command/commander.rb +2 -3
  11. data/lib/i18n/tasks/command/commands/health.rb +1 -1
  12. data/lib/i18n/tasks/command/commands/meta.rb +2 -2
  13. data/lib/i18n/tasks/command/commands/missing.rb +17 -19
  14. data/lib/i18n/tasks/command/commands/tree.rb +3 -3
  15. data/lib/i18n/tasks/command/commands/usages.rb +6 -5
  16. data/lib/i18n/tasks/command/commands/xlsx.rb +1 -1
  17. data/lib/i18n/tasks/command/dsl.rb +1 -2
  18. data/lib/i18n/tasks/command/option_parsers/enum.rb +6 -6
  19. data/lib/i18n/tasks/command/option_parsers/locale.rb +3 -1
  20. data/lib/i18n/tasks/command/options/data.rb +22 -21
  21. data/lib/i18n/tasks/command_error.rb +1 -3
  22. data/lib/i18n/tasks/configuration.rb +37 -27
  23. data/lib/i18n/tasks/console_context.rb +3 -2
  24. data/lib/i18n/tasks/data.rb +5 -4
  25. data/lib/i18n/tasks/data/adapter/json_adapter.rb +13 -12
  26. data/lib/i18n/tasks/data/adapter/yaml_adapter.rb +13 -14
  27. data/lib/i18n/tasks/data/file_formats.rb +15 -10
  28. data/lib/i18n/tasks/data/file_system_base.rb +18 -22
  29. data/lib/i18n/tasks/data/router/conservative_router.rb +3 -6
  30. data/lib/i18n/tasks/data/router/pattern_router.rb +4 -5
  31. data/lib/i18n/tasks/data/tree/node.rb +20 -19
  32. data/lib/i18n/tasks/data/tree/nodes.rb +4 -3
  33. data/lib/i18n/tasks/data/tree/siblings.rb +36 -29
  34. data/lib/i18n/tasks/data/tree/traversal.rb +32 -35
  35. data/lib/i18n/tasks/google_translation.rb +22 -25
  36. data/lib/i18n/tasks/html_keys.rb +2 -0
  37. data/lib/i18n/tasks/ignore_keys.rb +3 -2
  38. data/lib/i18n/tasks/key_pattern_matching.rb +9 -8
  39. data/lib/i18n/tasks/locale_list.rb +4 -6
  40. data/lib/i18n/tasks/locale_pathname.rb +8 -8
  41. data/lib/i18n/tasks/logging.rb +3 -3
  42. data/lib/i18n/tasks/missing_keys.rb +29 -31
  43. data/lib/i18n/tasks/plural_keys.rb +6 -7
  44. data/lib/i18n/tasks/references.rb +72 -41
  45. data/lib/i18n/tasks/reports/base.rb +8 -7
  46. data/lib/i18n/tasks/reports/spreadsheet.rb +15 -16
  47. data/lib/i18n/tasks/reports/terminal.rb +32 -32
  48. data/lib/i18n/tasks/scanners/file_scanner.rb +6 -5
  49. data/lib/i18n/tasks/scanners/files/caching_file_finder_provider.rb +1 -2
  50. data/lib/i18n/tasks/scanners/files/caching_file_reader.rb +0 -1
  51. data/lib/i18n/tasks/scanners/files/file_finder.rb +12 -15
  52. data/lib/i18n/tasks/scanners/files/file_reader.rb +0 -1
  53. data/lib/i18n/tasks/scanners/occurrence_from_position.rb +8 -7
  54. data/lib/i18n/tasks/scanners/pattern_mapper.rb +2 -2
  55. data/lib/i18n/tasks/scanners/pattern_scanner.rb +9 -7
  56. data/lib/i18n/tasks/scanners/pattern_with_scope_scanner.rb +1 -2
  57. data/lib/i18n/tasks/scanners/relative_keys.rb +11 -11
  58. data/lib/i18n/tasks/scanners/results/key_occurrences.rb +3 -4
  59. data/lib/i18n/tasks/scanners/results/occurrence.rb +8 -7
  60. data/lib/i18n/tasks/scanners/ruby_ast_call_finder.rb +2 -2
  61. data/lib/i18n/tasks/scanners/ruby_ast_scanner.rb +39 -31
  62. data/lib/i18n/tasks/scanners/scanner_multiplexer.rb +9 -3
  63. data/lib/i18n/tasks/split_key.rb +5 -6
  64. data/lib/i18n/tasks/stats.rb +10 -8
  65. data/lib/i18n/tasks/string_interpolation.rb +1 -1
  66. data/lib/i18n/tasks/unused_keys.rb +2 -2
  67. data/lib/i18n/tasks/used_keys.rb +47 -43
  68. data/lib/i18n/tasks/version.rb +1 -1
  69. data/templates/rspec/i18n_spec.rb +2 -2
  70. metadata +30 -3
@@ -5,21 +5,22 @@ module I18n::Tasks
5
5
  module Data
6
6
  module Adapter
7
7
  module JsonAdapter
8
- extend self
8
+ class << self
9
+ # @return [Hash] locale tree
10
+ def parse(str, opts)
11
+ JSON.parse(str, parse_opts(opts))
12
+ end
9
13
 
10
- # @return [Hash] locale tree
11
- def parse(str, opts)
12
- JSON.parse(str, parse_opts(opts))
13
- end
14
+ # @return [String]
15
+ def dump(tree, opts)
16
+ JSON.generate(tree, parse_opts(opts))
17
+ end
14
18
 
15
- # @return [String]
16
- def dump(tree, opts)
17
- JSON.generate(tree, parse_opts(opts))
18
- end
19
+ private
19
20
 
20
- private
21
- def parse_opts(opts)
22
- opts.try(:symbolize_keys) || {}
21
+ def parse_opts(opts)
22
+ opts.try(:symbolize_keys) || {}
23
+ end
23
24
  end
24
25
  end
25
26
  end
@@ -4,23 +4,22 @@ module I18n::Tasks
4
4
  module Data
5
5
  module Adapter
6
6
  module YamlAdapter
7
- extend self
8
-
9
- # @return [Hash] locale tree
10
- def parse(str, options)
11
- if YAML.method(:load).arity.abs == 2
12
- YAML.load(str, options || {})
13
- else
14
- # older jruby and rbx 2.2.7 do not accept options
15
- YAML.load(str)
7
+ class << self
8
+ # @return [Hash] locale tree
9
+ def parse(str, options)
10
+ if YAML.method(:load).arity.abs == 2
11
+ YAML.load(str, options || {})
12
+ else
13
+ # older jruby and rbx 2.2.7 do not accept options
14
+ YAML.load(str)
15
+ end
16
16
  end
17
- end
18
17
 
19
- # @return [String]
20
- def dump(tree, options)
21
- tree.to_yaml(options || {})
18
+ # @return [String]
19
+ def dump(tree, options)
20
+ tree.to_yaml(options || {})
21
+ end
22
22
  end
23
-
24
23
  end
25
24
  end
26
25
  end
@@ -19,8 +19,8 @@ module I18n
19
19
 
20
20
  def adapter_op(op, format, tree, config)
21
21
  self.class.adapter_by_name(format).send(op, tree, config)
22
- rescue Exception => e
23
- raise CommandError.new("#{format} #{op} error: #{e.message}")
22
+ rescue Exception => e # rubocop:disable Lint/RescueException
23
+ raise CommandError, "#{format} #{op} error: #{e.message}"
24
24
  end
25
25
 
26
26
  protected
@@ -43,10 +43,10 @@ module I18n
43
43
  content = adapter_dump(hash, adapter)
44
44
  # Ignore unchanged data
45
45
  return if File.file?(path) &&
46
- # Comparing hashes for equality directly would ignore key order.
47
- # Round-trip through the adapter and compare the strings instead:
48
- content == adapter_dump(load_file(path), adapter)
49
- ::FileUtils.mkpath(File.dirname path)
46
+ # Comparing hashes for equality directly would ignore key order.
47
+ # Round-trip through the adapter and compare the strings instead:
48
+ content == adapter_dump(load_file(path), adapter)
49
+ ::FileUtils.mkpath(File.dirname(path))
50
50
  ::File.open(path, 'w') { |f| f.write content }
51
51
  end
52
52
 
@@ -58,9 +58,11 @@ module I18n
58
58
  end
59
59
 
60
60
  def adapter_name_for_path(path)
61
- @fn_patterns.detect { |(_name, pattern, _adapter)|
61
+ @fn_patterns.detect do |(_name, pattern, _adapter)|
62
62
  ::File.fnmatch(pattern, path)
63
- }.try(:first) or raise CommandError.new("Adapter not found for #{path}. Registered adapters: #{@fn_patterns.inspect}")
63
+ end.try(:first) || fail(
64
+ CommandError, "Adapter not found for #{path}. Registered adapters: #{@fn_patterns.inspect}"
65
+ )
64
66
  end
65
67
 
66
68
  def adapter_names
@@ -69,9 +71,12 @@ module I18n
69
71
 
70
72
  def adapter_by_name(name)
71
73
  name = name.to_s
72
- @fn_patterns.detect { |(adapter_name, _pattern, _adapter)|
74
+ @fn_patterns.detect do |(adapter_name, _pattern, _adapter)|
73
75
  adapter_name.to_s == name
74
- }.try(:last) or raise CommandError.new("Adapter with name #{name.inspect} not found. Registered adapters: #{@fn_patterns.inspect}")
76
+ end.try(:last) || fail(
77
+ CommandError,
78
+ "Adapter with name #{name.inspect} not found. Registered adapters: #{@fn_patterns.inspect}"
79
+ )
75
80
  end
76
81
  end
77
82
  end
@@ -15,9 +15,9 @@ module I18n::Tasks
15
15
  attr_writer :locales
16
16
 
17
17
  DEFAULTS = {
18
- read: ['config/locales/%{locale}.yml'],
19
- write: ['config/locales/%{locale}.yml']
20
- }
18
+ read: ['config/locales/%{locale}.yml'],
19
+ write: ['config/locales/%{locale}.yml']
20
+ }.freeze
21
21
 
22
22
  def initialize(config = {})
23
23
  self.config = config.except(:base_locale, :locales)
@@ -33,10 +33,10 @@ module I18n::Tasks
33
33
 
34
34
  # get locale tree
35
35
  def get(locale)
36
- locale = locale.to_s
36
+ locale = locale.to_s
37
37
  @trees ||= {}
38
38
  @trees[locale] ||= Tree::Siblings[locale => {}].merge!(
39
- read_locale locale
39
+ read_locale(locale)
40
40
  )
41
41
  end
42
42
 
@@ -57,12 +57,12 @@ module I18n::Tasks
57
57
  end
58
58
 
59
59
  def merge!(forest)
60
- forest.inject(Tree::Siblings.new) { |result, root|
60
+ forest.inject(Tree::Siblings.new) do |result, root|
61
61
  locale = root.key
62
62
  merged = get(locale).merge(root)
63
63
  set locale, merged
64
64
  result.merge! merged
65
- }
65
+ end
66
66
  end
67
67
 
68
68
  def remove_by_key!(forest)
@@ -88,17 +88,13 @@ module I18n::Tasks
88
88
  @available_locales ||= begin
89
89
  locales = Set.new
90
90
  Array(config[:read]).map do |pattern|
91
- [pattern, Dir.glob(pattern % {locale: '*'})] if pattern.include?('%{locale}'.freeze)
91
+ [pattern, Dir.glob(pattern % { locale: '*' })] if pattern.include?('%{locale}')
92
92
  end.compact.each do |pattern, paths|
93
- p = pattern.gsub('\\'.freeze, '\\\\'.freeze).gsub('/'.freeze, '\/'.freeze).gsub('.'.freeze, '\.'.freeze)
94
- p = p.gsub(/(\*+)/) {
95
- $1 == '**'.freeze ? '.*'.freeze : '[^/]*?'.freeze
96
- }.gsub('%{locale}'.freeze, '([^/.]+)'.freeze)
93
+ p = pattern.gsub('\\', '\\\\').gsub('/', '\/').gsub('.', '\.')
94
+ p = p.gsub(/(\*+)/) { Regexp.last_match(1) == '**' ? '.*' : '[^/]*?' }.gsub('%{locale}', '([^/.]+)')
97
95
  re = /\A#{p}\z/
98
96
  paths.each do |path|
99
- if re =~ path
100
- locales << $1
101
- end
97
+ locales << Regexp.last_match(1) if re =~ path
102
98
  end
103
99
  end
104
100
  locales
@@ -112,7 +108,7 @@ module I18n::Tasks
112
108
  end
113
109
 
114
110
  def config=(config)
115
- @config = DEFAULTS.deep_merge((config || {}).reject { |k, v| v.nil? })
111
+ @config = DEFAULTS.deep_merge((config || {}).reject { |_k, v| v.nil? })
116
112
  reload
117
113
  end
118
114
 
@@ -124,16 +120,16 @@ module I18n::Tasks
124
120
  self.router = router_was
125
121
  end
126
122
 
127
-
128
123
  ROUTER_NAME_ALIASES = {
129
- 'conservative_router' => 'I18n::Tasks::Data::Router::ConservativeRouter',
130
- 'pattern_router' => 'I18n::Tasks::Data::Router::PatternRouter'
131
- }
124
+ 'conservative_router' => 'I18n::Tasks::Data::Router::ConservativeRouter',
125
+ 'pattern_router' => 'I18n::Tasks::Data::Router::PatternRouter'
126
+ }.freeze
132
127
  def router
133
128
  @router ||= begin
134
129
  name = @config[:router].presence || 'conservative_router'
135
130
  name = ROUTER_NAME_ALIASES[name] || name
136
- ActiveSupport::Inflector.constantize(name).new(self, @config.merge(base_locale: base_locale, locales: locales))
131
+ router_class = ActiveSupport::Inflector.constantize(name)
132
+ router_class.new(self, @config.merge(base_locale: base_locale, locales: locales))
137
133
  end
138
134
  end
139
135
  attr_writer :router
@@ -142,7 +138,7 @@ module I18n::Tasks
142
138
 
143
139
  def read_locale(locale)
144
140
  Array(config[:read]).map do |path|
145
- Dir.glob path % {locale: locale}
141
+ Dir.glob path % { locale: locale }
146
142
  end.reduce(:+).map do |path|
147
143
  [path.freeze, load_file(path) || {}]
148
144
  end.map do |path, data|
@@ -20,9 +20,7 @@ module I18n::Tasks
20
20
  path = key_path(locale, key)
21
21
  # infer from another locale
22
22
  unless path
23
- inferred_from = (locales - [locale]).detect { |loc|
24
- path = key_path(loc, key)
25
- }
23
+ inferred_from = (locales - [locale]).detect { |loc| path = key_path(loc, key) }
26
24
  path = LocalePathname.replace_locale(path, inferred_from, locale) if inferred_from
27
25
  end
28
26
  key_with_locale = "#{locale}.#{key}"
@@ -36,9 +34,9 @@ module I18n::Tasks
36
34
  if not_found.present?
37
35
  # fall back to pattern router
38
36
  not_found_tree = forest.select_keys(root: true) { |key, _| not_found.include?(key) }
39
- super(locale, not_found_tree) { |path, tree|
37
+ super(locale, not_found_tree) do |path, tree|
40
38
  out[path] += tree.key_names(root: true)
41
- }
39
+ end
42
40
  end
43
41
 
44
42
  out.each do |dest, keys|
@@ -60,4 +58,3 @@ module I18n::Tasks
60
58
  end
61
59
  end
62
60
  end
63
-
@@ -32,11 +32,11 @@ module I18n::Tasks
32
32
  pattern, path = routes.detect { |route| route[0] =~ key }
33
33
  if pattern
34
34
  key_match = $~
35
- path = path % {locale: locale}
35
+ path = path % { locale: locale }
36
36
  path.gsub!(/\\\d+/) { |m| key_match[m[1..-1].to_i] }
37
37
  (out[path] ||= Set.new) << "#{locale}.#{key}"
38
38
  else
39
- raise CommandError.new("Cannot route key #{key}. Routes are #{@routes_config.inspect}")
39
+ fail CommandError, "Cannot route key #{key}. Routes are #{@routes_config.inspect}"
40
40
  end
41
41
  end
42
42
  out.each do |dest, keys|
@@ -48,11 +48,10 @@ module I18n::Tasks
48
48
  private
49
49
 
50
50
  def compile_routes(routes)
51
- routes.map { |x| x.is_a?(String) ? ['*', x] : x }.map { |x|
51
+ routes.map { |x| x.is_a?(String) ? ['*', x] : x }.map do |x|
52
52
  [compile_key_pattern(x[0]), x[1]]
53
- }
53
+ end
54
54
  end
55
55
  end
56
56
  end
57
57
  end
58
-
@@ -3,7 +3,7 @@
3
3
  require 'i18n/tasks/data/tree/traversal'
4
4
  require 'i18n/tasks/data/tree/siblings'
5
5
  module I18n::Tasks::Data::Tree
6
- class Node
6
+ class Node # rubocop:disable Metrics/ClassLength
7
7
  include Enumerable
8
8
  include Traversal
9
9
 
@@ -11,16 +11,16 @@ module I18n::Tasks::Data::Tree
11
11
  attr_reader :key, :children, :parent
12
12
 
13
13
  def initialize(key:, value: nil, data: nil, parent: nil, children: nil)
14
- @key = key
14
+ @key = key
15
15
  @key = @key.to_s.freeze if @key
16
- @value = value
17
- @data = data
18
- @parent = parent
16
+ @value = value
17
+ @data = data
18
+ @parent = parent
19
19
  self.children = (children if children)
20
20
  end
21
21
 
22
22
  def attributes
23
- {key: @key, value: @value, data: @data.try(:clone), parent: @parent, children: @children}
23
+ { key: @key, value: @value, data: @data.try(:clone), parent: @parent, children: @children }
24
24
  end
25
25
 
26
26
  def derive(new_attr = {})
@@ -29,12 +29,12 @@ module I18n::Tasks::Data::Tree
29
29
 
30
30
  def children=(children)
31
31
  @children = case children
32
- when Siblings
33
- children.parent == self ? children : children.derive(parent: self)
34
- when NilClass
35
- nil
36
- else
37
- Siblings.new(nodes: children, parent: self)
32
+ when Siblings
33
+ children.parent == self ? children : children.derive(parent: self)
34
+ when NilClass
35
+ nil
36
+ else
37
+ Siblings.new(nodes: children, parent: self)
38
38
  end
39
39
  dirty!
40
40
  end
@@ -59,7 +59,7 @@ module I18n::Tasks::Data::Tree
59
59
  end
60
60
 
61
61
  def parent?
62
- !!parent
62
+ !parent.nil?
63
63
  end
64
64
 
65
65
  def children?
@@ -99,8 +99,8 @@ module I18n::Tasks::Data::Tree
99
99
  end
100
100
 
101
101
  def full_key(opts = {})
102
- root = opts.key?(:root) ? opts[:root] : true
103
- @full_key ||= {}
102
+ root = opts.key?(:root) ? opts[:root] : true
103
+ @full_key ||= {}
104
104
  @full_key[root] ||= "#{"#{parent.full_key(root: root)}." if parent? && (root || parent.parent?)}#{key}"
105
105
  end
106
106
 
@@ -128,6 +128,7 @@ module I18n::Tasks::Data::Tree
128
128
  dirty!
129
129
  node
130
130
  end
131
+
131
132
  alias []= set
132
133
 
133
134
  def to_nodes
@@ -144,9 +145,9 @@ module I18n::Tasks::Data::Tree
144
145
  if key.nil?
145
146
  children_hash
146
147
  elsif leaf?
147
- {key => value}
148
+ { key => value }
148
149
  else
149
- {key => children_hash}
150
+ { key => children_hash }
150
151
  end
151
152
  end
152
153
  end
@@ -167,7 +168,7 @@ module I18n::Tasks::Data::Tree
167
168
 
168
169
  def format_value_for_inspect(value)
169
170
  if value.is_a?(Symbol)
170
- "#{Term::ANSIColor.bold(Term::ANSIColor.yellow '⮕ ')}#{Term::ANSIColor.yellow value.to_s}"
171
+ "#{Term::ANSIColor.bold(Term::ANSIColor.yellow('⮕ '))}#{Term::ANSIColor.yellow value.to_s}"
171
172
  else
172
173
  Term::ANSIColor.cyan(value.to_s)
173
174
  end
@@ -176,7 +177,7 @@ module I18n::Tasks::Data::Tree
176
177
  protected
177
178
 
178
179
  def dirty!
179
- @hash = nil
180
+ @hash = nil
180
181
  @full_key = nil
181
182
  end
182
183
 
@@ -20,7 +20,7 @@ module I18n::Tasks::Data::Tree
20
20
  end
21
21
 
22
22
  def attributes
23
- {nodes: @list}
23
+ { nodes: @list }
24
24
  end
25
25
 
26
26
  def derive(new_attr = {})
@@ -33,7 +33,7 @@ module I18n::Tasks::Data::Tree
33
33
  def to_hash(sort = false)
34
34
  (@hash ||= {})[sort] ||= begin
35
35
  if sort
36
- self.sort { |a, b| a.key <=> b.key }
36
+ sort_by(&:key)
37
37
  else
38
38
  self
39
39
  end.map { |node| node.to_hash(sort) }.reduce({}, :deep_merge!)
@@ -54,7 +54,7 @@ module I18n::Tasks::Data::Tree
54
54
  # methods below change state
55
55
 
56
56
  def remove!(node)
57
- @list.delete(node) or raise "#{node.full_key} not found in #{self.inspect}"
57
+ @list.delete(node) || fail("#{node.full_key} not found in #{inspect}")
58
58
  dirty!
59
59
  self
60
60
  end
@@ -88,6 +88,7 @@ module I18n::Tasks::Data::Tree
88
88
  alias children? any?
89
89
 
90
90
  protected
91
+
91
92
  def dirty!
92
93
  @hash = nil
93
94
  end
@@ -8,7 +8,7 @@ module I18n::Tasks::Data::Tree
8
8
  # Siblings represents a subtree sharing a common parent
9
9
  # in case of an empty parent (nil) it represents a forest
10
10
  # siblings' keys are unique
11
- class Siblings < Nodes
11
+ class Siblings < Nodes # rubocop:disable Metrics/ClassLength
12
12
  include ::I18n::Tasks::SplitKey
13
13
 
14
14
  attr_reader :parent, :key_to_node
@@ -17,7 +17,7 @@ module I18n::Tasks::Data::Tree
17
17
  super(nodes: opts[:nodes])
18
18
  @parent = opts[:parent] || first.try(:parent)
19
19
  @list.map! { |node| node.parent == @parent ? node : node.derive(parent: @parent) }
20
- @key_to_node = @list.inject({}) { |h, node| h[node.key] = node; h }
20
+ @key_to_node = @list.each_with_object({}) { |node, h| h[node.key] = node }
21
21
  end
22
22
 
23
23
  def attributes
@@ -53,9 +53,7 @@ module I18n::Tasks::Data::Tree
53
53
  def get(full_key)
54
54
  first_key, rest = split_key(full_key.to_s, 2)
55
55
  node = key_to_node[first_key]
56
- if rest && node
57
- node = node.children.try(:get, rest)
58
- end
56
+ node = node.children.try(:get, rest) if rest && node
59
57
  node
60
58
  end
61
59
 
@@ -63,7 +61,7 @@ module I18n::Tasks::Data::Tree
63
61
 
64
62
  # add or replace node by full key
65
63
  def set(full_key, node)
66
- raise 'value should be a I18n::Tasks::Data::Tree::Node' unless node.is_a?(Node)
64
+ fail 'value should be a I18n::Tasks::Data::Tree::Node' unless node.is_a?(Node)
67
65
  key_part, rest = split_key(full_key, 2)
68
66
  child = key_to_node[key_part]
69
67
 
@@ -87,7 +85,6 @@ module I18n::Tasks::Data::Tree
87
85
 
88
86
  alias []= set
89
87
 
90
-
91
88
  # methods below change state
92
89
 
93
90
  def remove!(node)
@@ -98,7 +95,7 @@ module I18n::Tasks::Data::Tree
98
95
 
99
96
  def append!(nodes)
100
97
  nodes = nodes.map do |node|
101
- raise "already has a child with key '#{node.key}'" if key_to_node.key?(node.key)
98
+ fail "already has a child with key '#{node.key}'" if key_to_node.key?(node.key)
102
99
  key_to_node[node.key] = (node.parent == parent ? node : node.derive(parent: parent))
103
100
  end
104
101
  super(nodes)
@@ -123,11 +120,11 @@ module I18n::Tasks::Data::Tree
123
120
  end
124
121
 
125
122
  def subtract_keys(keys)
126
- remove_nodes_and_emptied_ancestors(keys.inject(Set.new) { |set, key| (node = get(key)) ? set << node : set })
123
+ remove_nodes_and_emptied_ancestors(find_nodes(keys))
127
124
  end
128
125
 
129
126
  def subtract_keys!(keys)
130
- remove_nodes_and_emptied_ancestors!(keys.inject(Set.new) { |set, key| (node = get(key)) ? set << node : set })
127
+ remove_nodes_and_emptied_ancestors!(find_nodes(keys))
131
128
  end
132
129
 
133
130
  def subtract_by_key(other)
@@ -146,7 +143,7 @@ module I18n::Tasks::Data::Tree
146
143
  end
147
144
 
148
145
  # @param on_leaves_merge [Proc] invoked when a leaf is merged with another leaf
149
- def merge_node!(node, on_leaves_merge: nil)
146
+ def merge_node!(node, on_leaves_merge: nil) # rubocop:disable Metrics/AbcSize,Metrics/PerceivedComplexity
150
147
  if key_to_node.key?(node.key)
151
148
  our = key_to_node[node.key]
152
149
  return if our == node
@@ -182,6 +179,13 @@ module I18n::Tasks::Data::Tree
182
179
 
183
180
  private
184
181
 
182
+ def find_nodes(keys)
183
+ keys.each_with_object(Set.new) do |key, set|
184
+ node = get(key)
185
+ set << node if node
186
+ end
187
+ end
188
+
185
189
  # Adds all the ancestors that only contain the given nodes as descendants to the given nodes.
186
190
  # @param nodes [Set] Modified in-place.
187
191
  def add_ancestors_that_only_contain_nodes!(nodes)
@@ -205,7 +209,7 @@ module I18n::Tasks::Data::Tree
205
209
  opts[:nodes] ||= []
206
210
  parse_parent_opt!(opts)
207
211
  forest = Siblings.new(opts)
208
- block.call(forest) if block
212
+ yield(forest) if block
209
213
  # forest.parent.children = forest
210
214
  forest
211
215
  end
@@ -216,38 +220,39 @@ module I18n::Tasks::Data::Tree
216
220
  build_forest do |forest|
217
221
  key_occurrences.each do |key_occurrence|
218
222
  forest[key_occurrence.key] = ::I18n::Tasks::Data::Tree::Node.new(
219
- key: split_key(key_occurrence.key).last,
220
- data: {occurrences: key_occurrence.occurrences})
223
+ key: split_key(key_occurrence.key).last,
224
+ data: { occurrences: key_occurrence.occurrences }
225
+ )
221
226
  end
222
227
  end
223
228
  end
224
229
 
225
230
  def from_key_attr(key_attrs, opts = {}, &block)
226
- build_forest(opts) { |forest|
227
- key_attrs.each { |(full_key, attr)|
228
- raise "Invalid key #{full_key.inspect}" if full_key.end_with?('.')
231
+ build_forest(opts) do |forest|
232
+ key_attrs.each do |(full_key, attr)|
233
+ fail "Invalid key #{full_key.inspect}" if full_key.end_with?('.')
229
234
  node = ::I18n::Tasks::Data::Tree::Node.new(attr.merge(key: split_key(full_key).last))
230
- block.call(full_key, node) if block
235
+ yield(full_key, node) if block
231
236
  forest[full_key] = node
232
- }
233
- }
237
+ end
238
+ end
234
239
  end
235
240
 
236
241
  def from_key_names(keys, opts = {}, &block)
237
- build_forest(opts) { |forest|
238
- keys.each { |full_key|
242
+ build_forest(opts) do |forest|
243
+ keys.each do |full_key|
239
244
  node = ::I18n::Tasks::Data::Tree::Node.new(key: split_key(full_key).last)
240
- block.call(full_key, node) if block
245
+ yield(full_key, node) if block
241
246
  forest[full_key] = node
242
- }
243
- }
247
+ end
248
+ end
244
249
  end
245
250
 
246
251
  # build forest from nested hash, e.g. {'es' => { 'common' => { name => 'Nombre', 'age' => 'Edad' } } }
247
252
  # this is the native i18n gem format
248
253
  def from_nested_hash(hash, opts = {})
249
254
  parse_parent_opt!(opts)
250
- raise ::I18n::Tasks::CommandError.new("invalid tree #{hash.inspect}") unless hash.respond_to?(:map)
255
+ fail I18n::Tasks::CommandError, "invalid tree #{hash.inspect}" unless hash.respond_to?(:map)
251
256
  opts[:nodes] = hash.map { |key, value| Node.from_key_value key, value }
252
257
  Siblings.new(opts)
253
258
  end
@@ -257,13 +262,14 @@ module I18n::Tasks::Data::Tree
257
262
  # build forest from [[Full Key, Value]]
258
263
  def from_flat_pairs(pairs)
259
264
  Siblings.new.tap do |siblings|
260
- pairs.each { |full_key, value|
265
+ pairs.each do |full_key, value|
261
266
  siblings[full_key] = ::I18n::Tasks::Data::Tree::Node.new(key: split_key(full_key).last, value: value)
262
- }
267
+ end
263
268
  end
264
269
  end
265
270
 
266
271
  private
272
+
267
273
  def parse_parent_opt!(opts)
268
274
  if opts[:parent_key]
269
275
  opts[:parent] = ::I18n::Tasks::Data::Tree::Node.new(key: opts[:parent_key])
@@ -273,7 +279,8 @@ module I18n::Tasks::Data::Tree
273
279
  end
274
280
  if opts[:parent_locale]
275
281
  opts[:parent] = ::I18n::Tasks::Data::Tree::Node.new(
276
- key: opts[:parent_locale], data: {locale: opts[:parent_locale]})
282
+ key: opts[:parent_locale], data: { locale: opts[:parent_locale] }
283
+ )
277
284
  end
278
285
  end
279
286
  end