i18n-tasks 0.9.6 → 0.9.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/Rakefile +2 -1
- data/bin/i18n-tasks +1 -0
- data/config/locales/en.yml +15 -9
- data/config/locales/ru.yml +80 -74
- data/i18n-tasks.gemspec +9 -8
- data/lib/i18n/tasks.rb +2 -5
- data/lib/i18n/tasks/cli.rb +14 -18
- data/lib/i18n/tasks/command/commander.rb +2 -3
- data/lib/i18n/tasks/command/commands/health.rb +1 -1
- data/lib/i18n/tasks/command/commands/meta.rb +2 -2
- data/lib/i18n/tasks/command/commands/missing.rb +17 -19
- data/lib/i18n/tasks/command/commands/tree.rb +3 -3
- data/lib/i18n/tasks/command/commands/usages.rb +6 -5
- data/lib/i18n/tasks/command/commands/xlsx.rb +1 -1
- data/lib/i18n/tasks/command/dsl.rb +1 -2
- data/lib/i18n/tasks/command/option_parsers/enum.rb +6 -6
- data/lib/i18n/tasks/command/option_parsers/locale.rb +3 -1
- data/lib/i18n/tasks/command/options/data.rb +22 -21
- data/lib/i18n/tasks/command_error.rb +1 -3
- data/lib/i18n/tasks/configuration.rb +37 -27
- data/lib/i18n/tasks/console_context.rb +3 -2
- data/lib/i18n/tasks/data.rb +5 -4
- data/lib/i18n/tasks/data/adapter/json_adapter.rb +13 -12
- data/lib/i18n/tasks/data/adapter/yaml_adapter.rb +13 -14
- data/lib/i18n/tasks/data/file_formats.rb +15 -10
- data/lib/i18n/tasks/data/file_system_base.rb +18 -22
- data/lib/i18n/tasks/data/router/conservative_router.rb +3 -6
- data/lib/i18n/tasks/data/router/pattern_router.rb +4 -5
- data/lib/i18n/tasks/data/tree/node.rb +20 -19
- data/lib/i18n/tasks/data/tree/nodes.rb +4 -3
- data/lib/i18n/tasks/data/tree/siblings.rb +36 -29
- data/lib/i18n/tasks/data/tree/traversal.rb +32 -35
- data/lib/i18n/tasks/google_translation.rb +22 -25
- data/lib/i18n/tasks/html_keys.rb +2 -0
- data/lib/i18n/tasks/ignore_keys.rb +3 -2
- data/lib/i18n/tasks/key_pattern_matching.rb +9 -8
- data/lib/i18n/tasks/locale_list.rb +4 -6
- data/lib/i18n/tasks/locale_pathname.rb +8 -8
- data/lib/i18n/tasks/logging.rb +3 -3
- data/lib/i18n/tasks/missing_keys.rb +29 -31
- data/lib/i18n/tasks/plural_keys.rb +6 -7
- data/lib/i18n/tasks/references.rb +72 -41
- data/lib/i18n/tasks/reports/base.rb +8 -7
- data/lib/i18n/tasks/reports/spreadsheet.rb +15 -16
- data/lib/i18n/tasks/reports/terminal.rb +32 -32
- data/lib/i18n/tasks/scanners/file_scanner.rb +6 -5
- data/lib/i18n/tasks/scanners/files/caching_file_finder_provider.rb +1 -2
- data/lib/i18n/tasks/scanners/files/caching_file_reader.rb +0 -1
- data/lib/i18n/tasks/scanners/files/file_finder.rb +12 -15
- data/lib/i18n/tasks/scanners/files/file_reader.rb +0 -1
- data/lib/i18n/tasks/scanners/occurrence_from_position.rb +8 -7
- data/lib/i18n/tasks/scanners/pattern_mapper.rb +2 -2
- data/lib/i18n/tasks/scanners/pattern_scanner.rb +9 -7
- data/lib/i18n/tasks/scanners/pattern_with_scope_scanner.rb +1 -2
- data/lib/i18n/tasks/scanners/relative_keys.rb +11 -11
- data/lib/i18n/tasks/scanners/results/key_occurrences.rb +3 -4
- data/lib/i18n/tasks/scanners/results/occurrence.rb +8 -7
- data/lib/i18n/tasks/scanners/ruby_ast_call_finder.rb +2 -2
- data/lib/i18n/tasks/scanners/ruby_ast_scanner.rb +39 -31
- data/lib/i18n/tasks/scanners/scanner_multiplexer.rb +9 -3
- data/lib/i18n/tasks/split_key.rb +5 -6
- data/lib/i18n/tasks/stats.rb +10 -8
- data/lib/i18n/tasks/string_interpolation.rb +1 -1
- data/lib/i18n/tasks/unused_keys.rb +2 -2
- data/lib/i18n/tasks/used_keys.rb +47 -43
- data/lib/i18n/tasks/version.rb +1 -1
- data/templates/rspec/i18n_spec.rb +2 -2
- metadata +30 -3
@@ -5,21 +5,22 @@ module I18n::Tasks
|
|
5
5
|
module Data
|
6
6
|
module Adapter
|
7
7
|
module JsonAdapter
|
8
|
-
|
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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
+
# @return [String]
|
15
|
+
def dump(tree, opts)
|
16
|
+
JSON.generate(tree, parse_opts(opts))
|
17
|
+
end
|
14
18
|
|
15
|
-
|
16
|
-
def dump(tree, opts)
|
17
|
-
JSON.generate(tree, parse_opts(opts))
|
18
|
-
end
|
19
|
+
private
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
20
|
-
|
21
|
-
|
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
|
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
|
-
|
47
|
-
|
48
|
-
|
49
|
-
::FileUtils.mkpath(File.dirname
|
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
|
61
|
+
@fn_patterns.detect do |(_name, pattern, _adapter)|
|
62
62
|
::File.fnmatch(pattern, path)
|
63
|
-
|
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
|
74
|
+
@fn_patterns.detect do |(adapter_name, _pattern, _adapter)|
|
73
75
|
adapter_name.to_s == name
|
74
|
-
|
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
|
-
|
19
|
-
|
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
|
36
|
+
locale = locale.to_s
|
37
37
|
@trees ||= {}
|
38
38
|
@trees[locale] ||= Tree::Siblings[locale => {}].merge!(
|
39
|
-
|
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)
|
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}'
|
91
|
+
[pattern, Dir.glob(pattern % { locale: '*' })] if pattern.include?('%{locale}')
|
92
92
|
end.compact.each do |pattern, paths|
|
93
|
-
p = pattern.gsub('\\'
|
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 { |
|
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
|
-
|
130
|
-
|
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)
|
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)
|
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
|
-
|
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
|
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
|
14
|
+
@key = key
|
15
15
|
@key = @key.to_s.freeze if @key
|
16
|
-
@value
|
17
|
-
@data
|
18
|
-
@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
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
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
|
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
|
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
|
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
|
-
|
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)
|
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.
|
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
|
-
|
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
|
-
|
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
|
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
|
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
|
-
|
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
|
-
|
220
|
-
|
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)
|
227
|
-
key_attrs.each
|
228
|
-
|
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
|
-
|
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)
|
238
|
-
keys.each
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
282
|
+
key: opts[:parent_locale], data: { locale: opts[:parent_locale] }
|
283
|
+
)
|
277
284
|
end
|
278
285
|
end
|
279
286
|
end
|