i18n-tasks 0.5.4 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +9 -0
- data/README.md +46 -36
- data/Rakefile +2 -2
- data/lib/i18n/tasks/base_task.rb +2 -0
- data/lib/i18n/tasks/commands.rb +55 -35
- data/lib/i18n/tasks/commands_base.rb +36 -9
- data/lib/i18n/tasks/configuration.rb +14 -13
- data/lib/i18n/tasks/console_context.rb +0 -1
- data/lib/i18n/tasks/data.rb +2 -2
- data/lib/i18n/tasks/data/adapter/json_adapter.rb +6 -2
- data/lib/i18n/tasks/data/file_formats.rb +19 -4
- data/lib/i18n/tasks/data/file_system_base.rb +1 -1
- data/lib/i18n/tasks/data/tree/node.rb +11 -37
- data/lib/i18n/tasks/data/tree/nodes.rb +6 -2
- data/lib/i18n/tasks/data/tree/siblings.rb +53 -29
- data/lib/i18n/tasks/data/tree/traversal.rb +4 -2
- data/lib/i18n/tasks/fill_tasks.rb +5 -2
- data/lib/i18n/tasks/ignore_keys.rb +1 -1
- data/lib/i18n/tasks/locale_pathname.rb +1 -1
- data/lib/i18n/tasks/logging.rb +2 -0
- data/lib/i18n/tasks/missing_keys.rb +74 -31
- data/lib/i18n/tasks/plural_keys.rb +4 -3
- data/lib/i18n/tasks/reports/base.rb +8 -5
- data/lib/i18n/tasks/reports/spreadsheet.rb +15 -3
- data/lib/i18n/tasks/reports/terminal.rb +62 -23
- data/lib/i18n/tasks/scanners/base_scanner.rb +4 -2
- data/lib/i18n/tasks/scanners/relative_keys.rb +21 -0
- data/lib/i18n/tasks/split_key.rb +39 -0
- data/lib/i18n/tasks/used_keys.rb +1 -1
- data/lib/i18n/tasks/version.rb +1 -1
- data/spec/fixtures/app/views/index.html.slim +2 -0
- data/spec/i18n_tasks_spec.rb +19 -5
- data/spec/locale_pathname_spec.rb +24 -0
- data/spec/locale_tree/siblings_spec.rb +41 -4
- data/spec/split_key_spec.rb +27 -0
- data/spec/support/i18n_tasks_output_matcher.rb +7 -13
- data/spec/support/trees.rb +4 -0
- data/templates/config/i18n-tasks.yml +82 -0
- data/templates/rspec/i18n_spec.rb +18 -0
- metadata +10 -3
- data/lib/i18n/tasks/relative_keys.rb +0 -19
@@ -1,4 +1,5 @@
|
|
1
1
|
# coding: utf-8
|
2
|
+
require 'set'
|
2
3
|
module I18n::Tasks::PluralKeys
|
3
4
|
PLURAL_KEY_SUFFIXES = Set.new %w(zero one two few many other)
|
4
5
|
PLURAL_KEY_RE = /\.(?:#{PLURAL_KEY_SUFFIXES.to_a * '|'})$/
|
@@ -20,10 +21,10 @@ module I18n::Tasks::PluralKeys
|
|
20
21
|
# @return the base form if the key is a specific plural form (e.g. apple for apple.many), and the key as passed otherwise
|
21
22
|
def depluralize_key(key, locale = base_locale)
|
22
23
|
return key if key !~ PLURAL_KEY_RE
|
23
|
-
|
24
|
-
nodes =
|
24
|
+
parent_key = split_key(key)[0..-2] * '.'
|
25
|
+
nodes = tree("#{locale}.#{parent_key}").presence || (locale != base_locale && tree("#{base_locale}.#{parent_key}"))
|
25
26
|
if nodes && nodes.all? { |x| x.leaf? && ".#{x.key}" =~ PLURAL_KEY_RE }
|
26
|
-
|
27
|
+
parent_key
|
27
28
|
else
|
28
29
|
key
|
29
30
|
end
|
@@ -13,9 +13,8 @@ module I18n::Tasks::Reports
|
|
13
13
|
protected
|
14
14
|
|
15
15
|
MISSING_TYPES = {
|
16
|
-
|
17
|
-
|
18
|
-
eq_base: {glyph: '=', summary: 'value equals base value'}
|
16
|
+
missing_used: {glyph: '✗', summary: 'used in code but missing from base locale'},
|
17
|
+
missing_diff: {glyph: '∅', summary: 'translated in one locale but not in the other'}
|
19
18
|
}
|
20
19
|
|
21
20
|
def missing_types
|
@@ -30,11 +29,15 @@ module I18n::Tasks::Reports
|
|
30
29
|
"Unused keys (#{key_values.count || '∅'})"
|
31
30
|
end
|
32
31
|
|
32
|
+
def eq_base_title(key_values, locale = base_locale)
|
33
|
+
"Same value as #{locale} (#{key_values.count || '∅'})"
|
34
|
+
end
|
35
|
+
|
33
36
|
def used_title(used_tree)
|
34
37
|
leaves = used_tree.leaves.to_a
|
35
38
|
filter = used_tree.first.root.data[:key_filter]
|
36
39
|
used_n = leaves.map { |node| node.data[:source_locations].size }.reduce(:+).to_i
|
37
|
-
"#{leaves.length} key#{'s' if leaves.size != 1}#{"
|
40
|
+
"#{leaves.length} key#{'s' if leaves.size != 1}#{" matching '#{filter}'" if filter}#{" (#{used_n} usage#{'s' if used_n != 1})" if used_n > 0}"
|
38
41
|
end
|
39
42
|
|
40
43
|
# Sort keys by their attributes in order
|
@@ -50,7 +53,7 @@ module I18n::Tasks::Reports
|
|
50
53
|
|
51
54
|
def forest_to_attr(forest)
|
52
55
|
forest.keys(root: false).map { |key, node|
|
53
|
-
{key: key, type: node.data[:type], locale: node.root.key}
|
56
|
+
{key: key, value: node.value, type: node.data[:type], locale: node.root.key, data: node.data}
|
54
57
|
}
|
55
58
|
end
|
56
59
|
end
|
@@ -10,6 +10,7 @@ module I18n::Tasks::Reports
|
|
10
10
|
p = Axlsx::Package.new
|
11
11
|
add_missing_sheet p.workbook
|
12
12
|
add_unused_sheet p.workbook
|
13
|
+
add_eq_base_sheet p.workbook
|
13
14
|
p.use_shared_strings = true
|
14
15
|
FileUtils.mkpath(File.dirname(path))
|
15
16
|
p.serialize(path)
|
@@ -37,9 +38,20 @@ module I18n::Tasks::Reports
|
|
37
38
|
end
|
38
39
|
end
|
39
40
|
|
41
|
+
def add_eq_base_sheet(wb)
|
42
|
+
keys = task.eq_base_keys.root_key_values(true)
|
43
|
+
add_locale_key_value_table wb, keys, name: eq_base_title(keys)
|
44
|
+
end
|
45
|
+
|
40
46
|
def add_unused_sheet(wb)
|
41
|
-
keys = task.unused_keys.root_key_values
|
42
|
-
wb
|
47
|
+
keys = task.unused_keys.root_key_values(true)
|
48
|
+
add_locale_key_value_table wb, keys, name: unused_title(keys)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def add_locale_key_value_table(wb, keys, worksheet_opts = {})
|
54
|
+
wb.add_worksheet worksheet_opts do |sheet|
|
43
55
|
sheet.add_row ['Locale', 'Key', 'Value']
|
44
56
|
style_header sheet
|
45
57
|
keys.each do |locale_k_v|
|
@@ -48,7 +60,7 @@ module I18n::Tasks::Reports
|
|
48
60
|
end
|
49
61
|
end
|
50
62
|
|
51
|
-
|
63
|
+
|
52
64
|
def style_header(sheet)
|
53
65
|
border_bottom = sheet.workbook.styles.add_style(border: {style: :thin, color: '000000', edges: [:bottom]})
|
54
66
|
sheet.rows.first.style = border_bottom
|
@@ -11,26 +11,21 @@ module I18n
|
|
11
11
|
print_title missing_title(forest)
|
12
12
|
|
13
13
|
if forest.present?
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
if type == :missing_from_base
|
24
|
-
locale = magenta locale
|
25
|
-
base_value = ''
|
14
|
+
keys_attr = sort_by_attr! forest_to_attr(forest), {locale: :asc, type: :desc, key: :asc}
|
15
|
+
print_table headings: [cyan(bold('Locale')), cyan(bold 'Key'), 'Details'] do |t|
|
16
|
+
t.rows = keys_attr.map do |a|
|
17
|
+
locale, key = a[:locale], a[:key], a[:type]
|
18
|
+
if a[:type] == :missing_used
|
19
|
+
occ = a[:data][:source_locations]
|
20
|
+
first = occ.first
|
21
|
+
info = [green("#{first[:src_path]}:#{first[:line_num]}"),
|
22
|
+
("(#{occ.length - 1} more)" if occ.length > 1)].compact.join(' ')
|
26
23
|
else
|
27
|
-
|
28
|
-
base_value = task.t(key, base_locale).to_s.strip
|
24
|
+
info = a[:value].to_s.strip
|
29
25
|
end
|
30
|
-
[{value: locale, alignment: :center},
|
31
|
-
|
32
|
-
|
33
|
-
cyan(base_value)]
|
26
|
+
[{value: cyan(locale), alignment: :center},
|
27
|
+
cyan(key),
|
28
|
+
wrap_string(info, 60)]
|
34
29
|
end
|
35
30
|
end
|
36
31
|
else
|
@@ -38,6 +33,11 @@ module I18n
|
|
38
33
|
end
|
39
34
|
end
|
40
35
|
|
36
|
+
def icon(type)
|
37
|
+
glyph = missing_types[type][:glyph]
|
38
|
+
{missing_used: red(glyph), missing_diff: yellow(glyph)}[type]
|
39
|
+
end
|
40
|
+
|
41
41
|
def used_keys(used_tree = task.used_tree(source_locations: true))
|
42
42
|
print_title used_title(used_tree)
|
43
43
|
keys_nodes = used_tree.keys.to_a
|
@@ -60,21 +60,43 @@ module I18n
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def unused_keys(tree = task.unused_keys)
|
63
|
-
keys = tree.root_key_values
|
63
|
+
keys = tree.root_key_values(true)
|
64
64
|
print_title unused_title(keys)
|
65
65
|
if keys.present?
|
66
|
-
|
67
|
-
t.rows = keys.map { |(locale, k, v)| [magenta(locale), magenta(k), cyan(v.to_s)] }
|
68
|
-
end
|
66
|
+
print_locale_key_value_table keys
|
69
67
|
else
|
70
68
|
print_success 'Every translation is used!'
|
71
69
|
end
|
72
70
|
end
|
73
71
|
|
72
|
+
def eq_base_keys(tree = task.eq_base_keys)
|
73
|
+
keys = tree.root_key_values(true)
|
74
|
+
print_title eq_base_title(keys)
|
75
|
+
if keys.present?
|
76
|
+
print_locale_key_value_table keys
|
77
|
+
else
|
78
|
+
print_info cyan('No translations are the same as base value')
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def show_tree(tree)
|
83
|
+
print_locale_key_value_table tree.root_key_values(true)
|
84
|
+
end
|
85
|
+
|
74
86
|
private
|
75
87
|
|
88
|
+
def print_locale_key_value_table(locale_key_values)
|
89
|
+
if locale_key_values.present?
|
90
|
+
print_table headings: [bold(cyan('Locale')), bold(cyan('Key')), 'Value'] do |t|
|
91
|
+
t.rows = locale_key_values.map { |(locale, k, v)| [{value: cyan(locale), alignment: :center}, cyan(k), v.to_s] }
|
92
|
+
end
|
93
|
+
else
|
94
|
+
puts 'ø'
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
76
98
|
def print_title(title)
|
77
|
-
log_stderr "#{bold
|
99
|
+
log_stderr "#{bold title.strip} #{dark "|"} #{"i18n-tasks v#{I18n::Tasks::VERSION}"}"
|
78
100
|
end
|
79
101
|
|
80
102
|
def print_success(message)
|
@@ -97,6 +119,23 @@ module I18n
|
|
97
119
|
def print_table(opts, &block)
|
98
120
|
puts ::Terminal::Table.new(opts, &block)
|
99
121
|
end
|
122
|
+
|
123
|
+
def wrap_string(s, max)
|
124
|
+
chars = []
|
125
|
+
dist = 0
|
126
|
+
s.chars.each do |c|
|
127
|
+
chars << c
|
128
|
+
dist += 1
|
129
|
+
if c == "\n"
|
130
|
+
dist = 0
|
131
|
+
elsif dist == max
|
132
|
+
dist = 0
|
133
|
+
chars << "\n"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
chars = chars[0..-2] if chars.last == "\n"
|
137
|
+
chars.join
|
138
|
+
end
|
100
139
|
end
|
101
140
|
end
|
102
141
|
end
|
@@ -1,9 +1,10 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
require 'i18n/tasks/key_pattern_matching'
|
3
|
-
require 'i18n/tasks/relative_keys'
|
3
|
+
require 'i18n/tasks/scanners/relative_keys'
|
4
|
+
|
4
5
|
module I18n::Tasks::Scanners
|
5
6
|
class BaseScanner
|
6
|
-
include
|
7
|
+
include RelativeKeys
|
7
8
|
include ::I18n::Tasks::KeyPatternMatching
|
8
9
|
include ::I18n::Tasks::Logging
|
9
10
|
|
@@ -11,6 +12,7 @@ module I18n::Tasks::Scanners
|
|
11
12
|
|
12
13
|
def initialize(config = {})
|
13
14
|
@config = config.dup.with_indifferent_access.tap do |conf|
|
15
|
+
conf[:relative_roots] = %w(app/views) if conf[:relative_roots].blank?
|
14
16
|
conf[:paths] = %w(app/) if conf[:paths].blank?
|
15
17
|
conf[:include] = Array(conf[:include]) if conf[:include].present?
|
16
18
|
if conf.key?(:exclude)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module I18n
|
3
|
+
module Tasks
|
4
|
+
module Scanners
|
5
|
+
module RelativeKeys
|
6
|
+
# @param key [String] relative i18n key (starts with a .)
|
7
|
+
# @param path [String] path to the file containing the key
|
8
|
+
# @return [String] absolute version of the key
|
9
|
+
def absolutize_key(key, path, roots = relative_roots)
|
10
|
+
# normalized path
|
11
|
+
path = File.expand_path path
|
12
|
+
(path_root = roots.map { |path| File.expand_path path }.sort.reverse.detect { |root| path.start_with?(root + '/') }) or
|
13
|
+
raise CommandError.new("Error scanning #{path}: cannot resolve relative key \"#{key}\".\nSet relative_roots in config/i18n-tasks.yml (currently #{relative_roots.inspect})")
|
14
|
+
# key prefix based on path
|
15
|
+
prefix = path.gsub(%r(#{path_root}/|(\.[^/]+)*$), '').tr('/', '.').gsub(%r(\._), '.')
|
16
|
+
"#{prefix}#{key}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module SplitKey
|
2
|
+
extend self
|
3
|
+
|
4
|
+
# split a key taking parenthesis into account
|
5
|
+
# split_key 'a.b' # => ['a', 'b']
|
6
|
+
# split_key 'a.#{b.c}' # => ['a', '#{b.c}']
|
7
|
+
# split_key 'a.b.c', 2 # => ['a', 'b.c']
|
8
|
+
def split_key(key, max = Float::INFINITY)
|
9
|
+
parts = []
|
10
|
+
nesting = NESTING_CHARS
|
11
|
+
counts = Array.new(NESTING_CHARS.size, 0)
|
12
|
+
delim = '.'.freeze
|
13
|
+
buf = []
|
14
|
+
key.to_s.chars.each do |char|
|
15
|
+
nest_i, nest_inc = nesting[char]
|
16
|
+
if nest_i
|
17
|
+
counts[nest_i] += nest_inc
|
18
|
+
buf << char
|
19
|
+
elsif char == delim && parts.length + 1 < max && counts.all?(&:zero?)
|
20
|
+
part = buf.join
|
21
|
+
buf.clear
|
22
|
+
parts << part
|
23
|
+
yield part if block_given?
|
24
|
+
else
|
25
|
+
buf << char
|
26
|
+
end
|
27
|
+
end
|
28
|
+
parts << buf.join unless buf.empty?
|
29
|
+
parts
|
30
|
+
end
|
31
|
+
|
32
|
+
NESTING_CHARS = %w({} [] ()).inject({}) { |h, s|
|
33
|
+
i = h.size / 2
|
34
|
+
h[s[0].freeze] = [i, 1].freeze
|
35
|
+
h[s[1].freeze] = [i, -1].freeze
|
36
|
+
h
|
37
|
+
}.freeze
|
38
|
+
private_constant :NESTING_CHARS
|
39
|
+
end
|
data/lib/i18n/tasks/used_keys.rb
CHANGED
@@ -22,7 +22,7 @@ module I18n::Tasks
|
|
22
22
|
@scanner ||= begin
|
23
23
|
search_config = (config[:search] || {}).with_indifferent_access
|
24
24
|
class_name = search_config[:scanner] || '::I18n::Tasks::Scanners::PatternWithScopeScanner'
|
25
|
-
class_name.constantize.new search_config
|
25
|
+
class_name.constantize.new search_config
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
data/lib/i18n/tasks/version.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
| x-t :fp_dash_before
|
3
3
|
/ t(:fp_comment)
|
4
4
|
/ i18n-tasks-use t(:fn_comment)
|
5
|
+
= t 'only_in_es'
|
5
6
|
p #{t('ca.a')} #{t 'ca.b'} #{t "ca.c"}
|
6
7
|
p #{t 'ca.d'} #{t 'ca.f', i: 'world'} #{t 'ca.e', i: 'world'}
|
7
8
|
p #{t 'missing_in_es.a'} #{t 'same_in_es.a'} #{t 'blank_in_es.a'}
|
@@ -22,3 +23,4 @@ p = t 'missing_in_es_plural_2.a', count: 2
|
|
22
23
|
p = t 'devise.a'
|
23
24
|
p = t :missing_symbol_key
|
24
25
|
p #{t :"missing_symbol.key_two"} #{t :'missing_symbol.key_three'}
|
26
|
+
= t 'present_in_es_but_not_en.a'
|
data/spec/i18n_tasks_spec.rb
CHANGED
@@ -9,15 +9,16 @@ describe 'i18n-tasks' do
|
|
9
9
|
describe 'missing' do
|
10
10
|
let (:expected_missing_keys) {
|
11
11
|
%w( en.used_but_missing.key en.relative.index.missing
|
12
|
-
es.missing_in_es.a
|
12
|
+
es.missing_in_es.a
|
13
|
+
en.present_in_es_but_not_en.a
|
13
14
|
en.hash.pattern_missing.a en.hash.pattern_missing.b
|
14
15
|
en.missing_symbol_key en.missing_symbol.key_two en.missing_symbol.key_three
|
15
16
|
es.missing_in_es_plural_1.a es.missing_in_es_plural_2.a
|
16
17
|
en.missing-key-with-a-dash.key
|
17
|
-
en.fn_comment
|
18
|
+
en.fn_comment en.only_in_es
|
18
19
|
)
|
19
20
|
}
|
20
|
-
it 'detects missing
|
21
|
+
it 'detects missing' do
|
21
22
|
capture_stderr do
|
22
23
|
expect(run_cmd :missing).to be_i18n_keys expected_missing_keys
|
23
24
|
es_keys = expected_missing_keys.grep(/^es\./)
|
@@ -28,6 +29,14 @@ describe 'i18n-tasks' do
|
|
28
29
|
end
|
29
30
|
end
|
30
31
|
|
32
|
+
describe 'eq_base' do
|
33
|
+
it 'detects eq_base' do
|
34
|
+
capture_stderr do
|
35
|
+
expect(run_cmd :eq_base).to be_i18n_keys %w(es.same_in_es.a)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
31
40
|
let(:expected_unused_keys) { %w(unused.a unused.numeric unused.plural).map { |k| %w(en es).map { |l| "#{l}.#{k}" } }.reduce(:+) }
|
32
41
|
describe 'unused' do
|
33
42
|
it 'detects unused' do
|
@@ -41,7 +50,7 @@ describe 'i18n-tasks' do
|
|
41
50
|
it 'removes unused' do
|
42
51
|
in_test_app_dir do
|
43
52
|
t = i18n_task
|
44
|
-
unused = expected_unused_keys.map { |k|
|
53
|
+
unused = expected_unused_keys.map { |k| SplitKey.split_key(k, 2)[1] }
|
45
54
|
unused.each do |key|
|
46
55
|
expect(t.key_value?(key, :en)).to be true
|
47
56
|
expect(t.key_value?(key, :es)).to be true
|
@@ -88,6 +97,7 @@ describe 'i18n-tasks' do
|
|
88
97
|
run_cmd :add_missing, locales: 'base'
|
89
98
|
in_test_app_dir {
|
90
99
|
expect(YAML.load_file('config/locales/en.yml')['en']['used_but_missing']['key']).to eq 'Key'
|
100
|
+
expect(YAML.load_file('config/locales/en.yml')['en']['present_in_es_but_not_en']['a']).to eq 'A'
|
91
101
|
}
|
92
102
|
end
|
93
103
|
|
@@ -111,6 +121,7 @@ describe 'i18n-tasks' do
|
|
111
121
|
in_test_app_dir {
|
112
122
|
expect(YAML.load_file('config/locales/es.yml')['es']['missing_in_es']['a']).to eq 'TRME'
|
113
123
|
expect(YAML.load_file('config/locales/devise.es.yml')['es']['devise']['a']).to eq 'ES_TEXT'
|
124
|
+
expect(YAML.load_file('config/locales/en.yml')['en']['present_in_es_but_not_en']['a']).to eq 'TRME'
|
114
125
|
}
|
115
126
|
end
|
116
127
|
|
@@ -121,6 +132,7 @@ describe 'i18n-tasks' do
|
|
121
132
|
run_cmd :add_missing, locales: 'all', placeholder: 'TRME %{base_value}'
|
122
133
|
in_test_app_dir {
|
123
134
|
expect(YAML.load_file('config/locales/es.yml')['es']['missing_in_es']['a']).to eq 'TRME EN_TEXT'
|
135
|
+
expect(YAML.load_file('config/locales/en.yml')['en']['present_in_es_but_not_en']['a']).to eq 'TRME ES_TEXT'
|
124
136
|
}
|
125
137
|
end
|
126
138
|
end
|
@@ -148,7 +160,7 @@ used.a 2
|
|
148
160
|
|
149
161
|
|
150
162
|
# --- setup ---
|
151
|
-
BENCH_KEYS =
|
163
|
+
BENCH_KEYS = 10
|
152
164
|
before(:each) do
|
153
165
|
gen_data = ->(v) {
|
154
166
|
v_num = v.chars.map(&:ord).join('').to_i
|
@@ -194,6 +206,8 @@ used.a 2
|
|
194
206
|
es_data['blank_in_es']['a'] = ''
|
195
207
|
es_data['ignore_eq_base_all']['a'] = 'EN_TEXT'
|
196
208
|
es_data['ignore_eq_base_es']['a'] = 'EN_TEXT'
|
209
|
+
es_data['only_in_es'] = 1
|
210
|
+
es_data['present_in_es_but_not_en'] = {'a' => 'ES_TEXT'}
|
197
211
|
|
198
212
|
fs = fixtures_contents.merge(
|
199
213
|
'config/locales/en.yml' => {'en' => en_data}.to_yaml,
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'LocalePathname' do
|
4
|
+
include ::I18n::Tasks::LocalePathname
|
5
|
+
context '#replace_locale' do
|
6
|
+
|
7
|
+
it 'es.yml' do
|
8
|
+
expect(replace_locale 'es.yml', 'es', 'fr').to eq 'fr.yml'
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'scope.es.yml' do
|
12
|
+
expect(replace_locale 'scope.es.yml', 'es', 'fr').to eq 'scope.fr.yml'
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'path/es.yml' do
|
16
|
+
expect(replace_locale 'path/es.yml', 'es', 'fr').to eq 'path/fr.yml'
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'path/scope.es.yml' do
|
20
|
+
expect(replace_locale 'path/scope.es.yml', 'es', 'fr').to eq 'path/scope.fr.yml'
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -10,12 +10,12 @@ describe 'Tree siblings / forest' do
|
|
10
10
|
key: 'fr',
|
11
11
|
children: children
|
12
12
|
)
|
13
|
-
expect(node.to_siblings.first.children.
|
13
|
+
expect(node.to_siblings.first.children.parent.key).to eq 'fr'
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
17
|
context 'a tree' do
|
18
|
-
let(:a_hash) { {a
|
18
|
+
let(:a_hash) { {'a' => 1, 'b' => {'ba' => 1, 'bb' => 2}} }
|
19
19
|
|
20
20
|
it '::from_nested_hash' do
|
21
21
|
a = build_tree(a_hash)
|
@@ -34,14 +34,34 @@ describe 'Tree siblings / forest' do
|
|
34
34
|
|
35
35
|
it '#merge' do
|
36
36
|
a = build_tree(a_hash)
|
37
|
-
b_hash = {b
|
37
|
+
b_hash = {'b' => {'bc' => 1}, 'c' => 1}
|
38
38
|
expect(a.merge(build_tree(b_hash)).to_hash).to eq(a_hash.deep_merge(b_hash))
|
39
39
|
end
|
40
40
|
|
41
|
+
it '#merge does not modify self' do
|
42
|
+
a = build_tree(a: 1)
|
43
|
+
b = build_tree(a: 2)
|
44
|
+
c = a.merge b
|
45
|
+
expect(a['a'].value).to eq 1
|
46
|
+
expect(c['a'].value).to eq 2
|
47
|
+
expect(b['a'].value).to eq 2
|
48
|
+
end
|
49
|
+
|
50
|
+
it '#merge conflict value <- scope' do
|
51
|
+
a = build_tree(a: 1)
|
52
|
+
b = build_tree(a: {b: 1})
|
53
|
+
expect { silence_stderr { a.merge(b) } }.to_not raise_error
|
54
|
+
end
|
55
|
+
|
56
|
+
it '#set conflict value <- scope' do
|
57
|
+
a = build_tree(a: 1)
|
58
|
+
expect { silence_stderr { a.set('a.b', build_node(key: 'b', value: 1)) } }.to_not raise_error
|
59
|
+
end
|
60
|
+
|
41
61
|
it '#intersect' do
|
42
62
|
x = {a: 1, b: {ba: 1, bb: 2}}
|
43
63
|
y = {b: {ba: 1, bc: 3}, c: 1}
|
44
|
-
intersection = {b
|
64
|
+
intersection = {'b' => {'ba' => 1}}
|
45
65
|
a = build_tree(x)
|
46
66
|
b = build_tree(y)
|
47
67
|
expect(a.intersect_keys(b, root: true).to_hash).to eq(intersection)
|
@@ -50,5 +70,22 @@ describe 'Tree siblings / forest' do
|
|
50
70
|
it '#select_keys' do
|
51
71
|
expect(build_tree(a: 1, b: 1).select_keys {|k, node| k == 'b'}.to_hash).to eq({'b' => 1})
|
52
72
|
end
|
73
|
+
|
74
|
+
it '#append!' do
|
75
|
+
expect(build_tree({'a' => 1}).append!(build_node(key: 'b', value: 2)).to_hash).to eq('a' => 1, 'b' => 2)
|
76
|
+
end
|
77
|
+
|
78
|
+
it '#set replace value' do
|
79
|
+
expect(build_tree(a: {b: 1}).tap {|t| t['a.b'] = build_node(key: 'b', value: 2) }.to_hash).to(
|
80
|
+
eq('a' => {'b' => 2})
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
84
|
+
it '#set get' do
|
85
|
+
t = build_tree(a: {x: 1})
|
86
|
+
node = build_node(key: 'd', value: 'e')
|
87
|
+
t['a.b.c.' + node.key] = node
|
88
|
+
expect(t['a.b.c.d'].value).to eq('e')
|
89
|
+
end
|
53
90
|
end
|
54
91
|
end
|