i18n_flow 0.1.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 +7 -0
- data/.gitignore +7 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -0
- data/.travis.yml +13 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +45 -0
- data/LICENSE +22 -0
- data/README.md +103 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/doc/rules.md +316 -0
- data/doc/tags.md +488 -0
- data/example/example.en.yml +14 -0
- data/example/example.ja.yml +9 -0
- data/exe/i18n_flow +11 -0
- data/i18n_flow.gemspec +28 -0
- data/i18n_flow.yml +8 -0
- data/lib/i18n_flow/cli/color.rb +18 -0
- data/lib/i18n_flow/cli/command_base.rb +33 -0
- data/lib/i18n_flow/cli/copy_command.rb +69 -0
- data/lib/i18n_flow/cli/help_command.rb +29 -0
- data/lib/i18n_flow/cli/lint_command/ascii.erb +45 -0
- data/lib/i18n_flow/cli/lint_command/ascii_renderer.rb +58 -0
- data/lib/i18n_flow/cli/lint_command/markdown.erb +49 -0
- data/lib/i18n_flow/cli/lint_command/markdown_renderer.rb +55 -0
- data/lib/i18n_flow/cli/lint_command.rb +55 -0
- data/lib/i18n_flow/cli/read_config_command.rb +20 -0
- data/lib/i18n_flow/cli/search_command/default.erb +11 -0
- data/lib/i18n_flow/cli/search_command/default_renderer.rb +67 -0
- data/lib/i18n_flow/cli/search_command/oneline.erb +5 -0
- data/lib/i18n_flow/cli/search_command/oneline_renderer.rb +39 -0
- data/lib/i18n_flow/cli/search_command.rb +59 -0
- data/lib/i18n_flow/cli/split_command.rb +20 -0
- data/lib/i18n_flow/cli/version_command.rb +9 -0
- data/lib/i18n_flow/cli.rb +42 -0
- data/lib/i18n_flow/configuration.rb +205 -0
- data/lib/i18n_flow/parser.rb +34 -0
- data/lib/i18n_flow/repository.rb +39 -0
- data/lib/i18n_flow/search.rb +176 -0
- data/lib/i18n_flow/splitter/merger.rb +60 -0
- data/lib/i18n_flow/splitter/strategy.rb +66 -0
- data/lib/i18n_flow/splitter.rb +5 -0
- data/lib/i18n_flow/util.rb +57 -0
- data/lib/i18n_flow/validator/errors.rb +99 -0
- data/lib/i18n_flow/validator/file_scope.rb +58 -0
- data/lib/i18n_flow/validator/multiplexer.rb +58 -0
- data/lib/i18n_flow/validator/symmetry.rb +154 -0
- data/lib/i18n_flow/validator.rb +4 -0
- data/lib/i18n_flow/version.rb +7 -0
- data/lib/i18n_flow/yaml_ast_proxy/mapping.rb +72 -0
- data/lib/i18n_flow/yaml_ast_proxy/node.rb +128 -0
- data/lib/i18n_flow/yaml_ast_proxy/node_meta_data.rb +86 -0
- data/lib/i18n_flow/yaml_ast_proxy/sequence.rb +29 -0
- data/lib/i18n_flow/yaml_ast_proxy.rb +57 -0
- data/lib/i18n_flow.rb +15 -0
- data/spec/lib/i18n_flow/cli/command_base_spec.rb +46 -0
- data/spec/lib/i18n_flow/cli/help_command_spec.rb +13 -0
- data/spec/lib/i18n_flow/cli/version_command_spec.rb +13 -0
- data/spec/lib/i18n_flow/configuration_spec.rb +334 -0
- data/spec/lib/i18n_flow/repository_spec.rb +40 -0
- data/spec/lib/i18n_flow/splitter/merger_spec.rb +149 -0
- data/spec/lib/i18n_flow/util_spec.rb +194 -0
- data/spec/lib/i18n_flow/validator/file_scope_spec.rb +74 -0
- data/spec/lib/i18n_flow/validator/multiplexer_spec.rb +68 -0
- data/spec/lib/i18n_flow/validator/symmetry_spec.rb +511 -0
- data/spec/lib/i18n_flow/yaml_ast_proxy/node_spec.rb +151 -0
- data/spec/lib/i18n_flow_spec.rb +21 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/support/repository_examples.rb +60 -0
- data/spec/support/util_macro.rb +14 -0
- metadata +214 -0
@@ -0,0 +1,194 @@
|
|
1
|
+
require 'i18n_flow/util'
|
2
|
+
|
3
|
+
describe I18nFlow::Util do
|
4
|
+
describe '.extract_args' do
|
5
|
+
it 'should extract translation arguments' do
|
6
|
+
{
|
7
|
+
'' => %w[],
|
8
|
+
'foo' => %w[],
|
9
|
+
'%{arg_1}' => %w[arg_1],
|
10
|
+
'foo %{arg_1}' => %w[arg_1],
|
11
|
+
'foo %{arg_1} bar %{arg_2}' => %w[arg_1 arg_2],
|
12
|
+
'foo %{arg_2} bar %{arg_1}' => %w[arg_1 arg_2],
|
13
|
+
'%{arg_1}%{arg_2}' => %w[arg_1 arg_2],
|
14
|
+
'%{arg_1}%%{arg_2}' => %w[arg_1],
|
15
|
+
'%%{arg_1} bar %%%{arg_2}' => %w[arg_2],
|
16
|
+
}.each do |input, output|
|
17
|
+
expect(I18nFlow::Util.extract_args(input)).to eq(output)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '.filepath_to_scope' do
|
23
|
+
it 'should parse file path into locale and scopes' do
|
24
|
+
{
|
25
|
+
'en.yml' => %w[en],
|
26
|
+
'.en.yml' => %w[en],
|
27
|
+
'foo.en.yml' => %w[en foo],
|
28
|
+
'foo/bar/en.yml' => %w[en foo bar],
|
29
|
+
'foo/bar/baz.en.yml' => %w[en foo bar baz],
|
30
|
+
'foo/bar/baz.bax.en.yml' => %w[en foo bar baz bax],
|
31
|
+
}.each do |input, output|
|
32
|
+
expect(I18nFlow::Util.filepath_to_scope(input)).to eq(output)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '.scope_to_filepath' do
|
38
|
+
it 'should build file path from scopes' do
|
39
|
+
{
|
40
|
+
[nil, 'en'] => 'en.yml',
|
41
|
+
%w[en] => 'en.yml',
|
42
|
+
%w[en foo] => 'foo.en.yml',
|
43
|
+
%w[en foo bar] => 'foo/bar.en.yml',
|
44
|
+
%w[en foo bar baz] => 'foo/bar/baz.en.yml',
|
45
|
+
%w[en foo bar baz bax] => 'foo/bar/baz/bax.en.yml',
|
46
|
+
}.each do |input, output|
|
47
|
+
expect(I18nFlow::Util.scope_to_filepath(input)).to eq(output)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '.find_file_upward' do
|
53
|
+
let(:file) { FakeFS::FakeFile.new }
|
54
|
+
let(:file_name) { 'file' }
|
55
|
+
|
56
|
+
it 'should return a file path if a file is exist in the current directory' do
|
57
|
+
pwd = '/a/b/c/d'
|
58
|
+
file_path = "/a/b/c/d/#{file_name}"
|
59
|
+
|
60
|
+
FakeFS::FileSystem.add(pwd)
|
61
|
+
FakeFS::FileSystem.add(file_path, file)
|
62
|
+
|
63
|
+
Dir.chdir(pwd) do
|
64
|
+
expect(I18nFlow::Util.find_file_upward(file_name)).to eq(file_path)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should return a file path if a file is exist at the parent directory' do
|
69
|
+
pwd = '/a/b/c/d'
|
70
|
+
file_path = "/a/b/c/#{file_name}"
|
71
|
+
|
72
|
+
FakeFS::FileSystem.add(pwd)
|
73
|
+
FakeFS::FileSystem.add(file_path, file)
|
74
|
+
|
75
|
+
Dir.chdir(pwd) do
|
76
|
+
expect(I18nFlow::Util.find_file_upward(file_name)).to eq(file_path)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should return a file path if a file is exist at somewhere of parent directories' do
|
81
|
+
pwd = '/a/b/c/d'
|
82
|
+
file_path = "/a/#{file_name}"
|
83
|
+
|
84
|
+
FakeFS::FileSystem.add(pwd)
|
85
|
+
FakeFS::FileSystem.add(file_path, file)
|
86
|
+
|
87
|
+
Dir.chdir(pwd) do
|
88
|
+
expect(I18nFlow::Util.find_file_upward(file_name)).to eq(file_path)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should return nil if a file is not exist in any parent directories' do
|
93
|
+
pwd = '/a/b/c/d'
|
94
|
+
file_path = "/abc/#{file_name}"
|
95
|
+
|
96
|
+
FakeFS::FileSystem.add(pwd)
|
97
|
+
FakeFS::FileSystem.add(file_path, file)
|
98
|
+
|
99
|
+
Dir.chdir(pwd) do
|
100
|
+
expect(I18nFlow::Util.find_file_upward(file_name)).to be_nil
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context 'Multiple file names' do
|
105
|
+
let(:file_2) { FakeFS::FakeFile.new }
|
106
|
+
let(:file_name_2) { 'file' }
|
107
|
+
|
108
|
+
it 'should return a file path if one of files is exist at somewhere of parent directories' do
|
109
|
+
pwd = '/a/b/c/d'
|
110
|
+
file_path = "/a/#{file_name}"
|
111
|
+
|
112
|
+
FakeFS::FileSystem.add(pwd)
|
113
|
+
FakeFS::FileSystem.add(file_path, file)
|
114
|
+
|
115
|
+
Dir.chdir(pwd) do
|
116
|
+
expect(I18nFlow::Util.find_file_upward(file_name, file_name_2)).to eq(file_path)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'should return the first match if one of files is exist at somewhere of parent directories' do
|
121
|
+
pwd = '/a/b/c/d'
|
122
|
+
file_path = "/a/#{file_name}"
|
123
|
+
file_path_2 = "/a/#{file_name_2}"
|
124
|
+
|
125
|
+
FakeFS::FileSystem.add(pwd)
|
126
|
+
FakeFS::FileSystem.add(file_path, file)
|
127
|
+
FakeFS::FileSystem.add(file_path_2, file)
|
128
|
+
|
129
|
+
Dir.chdir(pwd) do
|
130
|
+
expect(I18nFlow::Util.find_file_upward(file_name, file_name_2)).to eq(file_path)
|
131
|
+
expect(I18nFlow::Util.find_file_upward(file_name_2, file_name)).to eq(file_path_2)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'should return nil if non of files is not exist in any parent directories' do
|
136
|
+
pwd = '/a/b/c/d'
|
137
|
+
file_path = "/abc/#{file_name}"
|
138
|
+
file_path_2 = "/cba/#{file_name_2}"
|
139
|
+
|
140
|
+
FakeFS::FileSystem.add(pwd)
|
141
|
+
FakeFS::FileSystem.add(file_path, file)
|
142
|
+
FakeFS::FileSystem.add(file_path_2, file)
|
143
|
+
|
144
|
+
Dir.chdir(pwd) do
|
145
|
+
expect(I18nFlow::Util.find_file_upward(file_name, file_name_2)).to be_nil
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe '.parse_options' do
|
152
|
+
it 'should parse short options' do
|
153
|
+
args = ['-a', '-b']
|
154
|
+
options = I18nFlow::Util.parse_options(args)
|
155
|
+
|
156
|
+
expect(args).to be_empty
|
157
|
+
expect(options).to be_a(Hash)
|
158
|
+
expect(options['a']).to be(true)
|
159
|
+
expect(options['b']).to be(true)
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'should parse long options' do
|
163
|
+
args = ['--long-a', '--long-b']
|
164
|
+
options = I18nFlow::Util.parse_options(args)
|
165
|
+
|
166
|
+
expect(args).to be_empty
|
167
|
+
expect(options).to be_a(Hash)
|
168
|
+
expect(options['long-a']).to be(true)
|
169
|
+
expect(options['long-b']).to be(true)
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'should parse long options with value' do
|
173
|
+
args = ['--long-a=value-of-a', '--long-b=value-of-b']
|
174
|
+
options = I18nFlow::Util.parse_options(args)
|
175
|
+
|
176
|
+
expect(args).to be_empty
|
177
|
+
expect(options).to be_a(Hash)
|
178
|
+
expect(options['long-a']).to eq('value-of-a')
|
179
|
+
expect(options['long-b']).to eq('value-of-b')
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'should not parse options after once non-option args appears' do
|
183
|
+
args = ['-a', '--long-a', 'non-option', '--long-b=value-of-b']
|
184
|
+
options = I18nFlow::Util.parse_options(args)
|
185
|
+
|
186
|
+
expect(args).to eq(['non-option', '--long-b=value-of-b'])
|
187
|
+
expect(options).to be_a(Hash)
|
188
|
+
expect(options['a']).to be(true)
|
189
|
+
expect(options['long-a']).to be(true)
|
190
|
+
expect(options['long-b']).to be_nil
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'i18n_flow/validator/file_scope'
|
2
|
+
require 'i18n_flow/validator/errors'
|
3
|
+
|
4
|
+
describe I18nFlow::Validator::FileScope do
|
5
|
+
let(:filepath) { 'foo/bar.en.yml' }
|
6
|
+
let(:validator) { I18nFlow::Validator::FileScope.new(nil, filepath: filepath) }
|
7
|
+
|
8
|
+
describe '#validate' do
|
9
|
+
it 'should pass if the filepath and the scope are perfectly matched' do
|
10
|
+
ast = parse_yaml(<<-YAML)
|
11
|
+
en:
|
12
|
+
foo:
|
13
|
+
bar:
|
14
|
+
key_1: text_1
|
15
|
+
YAML
|
16
|
+
|
17
|
+
allow(validator).to receive(:ast).and_return(ast)
|
18
|
+
validator.validate!
|
19
|
+
|
20
|
+
expect(validator.errors).to eq([])
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should fail if the scope is missing' do
|
24
|
+
ast = parse_yaml(<<-YAML)
|
25
|
+
en:
|
26
|
+
foo:
|
27
|
+
key_1: text_1
|
28
|
+
YAML
|
29
|
+
|
30
|
+
allow(validator).to receive(:ast).and_return(ast)
|
31
|
+
validator.validate!
|
32
|
+
|
33
|
+
expect(validator.errors).to eq([
|
34
|
+
I18nFlow::Validator::MissingKeyError.new('en.foo.bar'),
|
35
|
+
])
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should fail if its structure is invalid' do
|
39
|
+
ast = parse_yaml(<<-YAML)
|
40
|
+
en:
|
41
|
+
foo:
|
42
|
+
bar: text_1
|
43
|
+
YAML
|
44
|
+
|
45
|
+
allow(validator).to receive(:ast).and_return(ast)
|
46
|
+
validator.validate!
|
47
|
+
|
48
|
+
expect(validator.errors).to eq([
|
49
|
+
I18nFlow::Validator::InvalidTypeError.new('en.foo.bar'),
|
50
|
+
])
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should fail if it contains extra keys' do
|
54
|
+
ast = parse_yaml(<<-YAML)
|
55
|
+
en:
|
56
|
+
foo:
|
57
|
+
bar:
|
58
|
+
key_1: text_1
|
59
|
+
baz:
|
60
|
+
key_2: text_2
|
61
|
+
bax:
|
62
|
+
key_2: text_2
|
63
|
+
YAML
|
64
|
+
|
65
|
+
allow(validator).to receive(:ast).and_return(ast)
|
66
|
+
validator.validate!
|
67
|
+
|
68
|
+
expect(validator.errors).to eq([
|
69
|
+
I18nFlow::Validator::ExtraKeyError.new('en.foo.baz'),
|
70
|
+
I18nFlow::Validator::ExtraKeyError.new('en.foo.bax'),
|
71
|
+
])
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
describe I18nFlow::Validator::Multiplexer do
|
2
|
+
include_examples :create_repository
|
3
|
+
|
4
|
+
let(:validator) do
|
5
|
+
I18nFlow::Validator::Multiplexer.new(
|
6
|
+
repository: repository,
|
7
|
+
valid_locales: %w[en ja fr],
|
8
|
+
locale_pairs: [
|
9
|
+
%w[en ja],
|
10
|
+
%w[ja fr],
|
11
|
+
],
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#validate' do
|
16
|
+
it 'should pass' do
|
17
|
+
validator.validate!
|
18
|
+
expect(validator.errors).to eq({})
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'violate single validator rules' do
|
22
|
+
let(:models_user_ja_yml) do
|
23
|
+
<<-YAML
|
24
|
+
ja:
|
25
|
+
modulo:
|
26
|
+
user:
|
27
|
+
key_1: text_1
|
28
|
+
YAML
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should fail' do
|
32
|
+
validator.validate!
|
33
|
+
expect(validator.errors).to eq({
|
34
|
+
'models/user.ja.yml' => {
|
35
|
+
'ja.models' => I18nFlow::Validator::MissingKeyError.new('ja.models'),
|
36
|
+
'ja.modulo' => I18nFlow::Validator::ExtraKeyError.new('ja.modulo'),
|
37
|
+
},
|
38
|
+
})
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'violate symmetry validator rules' do
|
43
|
+
let(:views_profiles_show_ja_yml) do
|
44
|
+
<<-YAML
|
45
|
+
ja:
|
46
|
+
views:
|
47
|
+
profiles:
|
48
|
+
show:
|
49
|
+
key_2: text_2
|
50
|
+
YAML
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should fail' do
|
54
|
+
validator.validate!
|
55
|
+
expect(validator.errors).to eq({
|
56
|
+
'views/profiles/show.ja.yml' => {
|
57
|
+
'ja.views.profiles.show.key_1' => I18nFlow::Validator::MissingKeyError.new('ja.views.profiles.show.key_1'),
|
58
|
+
'ja.views.profiles.show.key_2' => I18nFlow::Validator::ExtraKeyError.new('ja.views.profiles.show.key_2'),
|
59
|
+
},
|
60
|
+
'views/profiles/show.fr.yml' => {
|
61
|
+
"fr.views.profiles.show.key_1"=> I18nFlow::Validator::ExtraKeyError.new('fr.views.profiles.show.key_1'),
|
62
|
+
'fr.views.profiles.show.key_2'=> I18nFlow::Validator::MissingKeyError.new('fr.views.profiles.show.key_2'),
|
63
|
+
},
|
64
|
+
})
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|