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.
- 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
|