yaml-validator 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
data/Gemfile.lock CHANGED
@@ -1,13 +1,15 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- yaml-validator (0.0.1)
4
+ yaml-validator (0.1.0)
5
+ colorize
5
6
  rake
6
7
  rspec
7
8
 
8
9
  GEM
9
10
  remote: http://rubygems.org/
10
11
  specs:
12
+ colorize (0.5.8)
11
13
  diff-lcs (1.1.3)
12
14
  rake (10.0.3)
13
15
  rspec (2.12.0)
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  YAML Validator
2
2
  ==============
3
3
 
4
+ [![Build Status](https://travis-ci.org/wazeHQ/yaml-validator.png?branch=master)](https://travis-ci.org/wazeHQ/yaml-validator)
5
+
4
6
  Validates .yml locale files for Ruby on Rails projects.
5
7
 
6
8
  What does it validate?
data/Rakefile CHANGED
@@ -2,3 +2,5 @@ require 'bundler/gem_tasks'
2
2
  require 'rspec/core/rake_task'
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => [:spec]
data/bin/yaml-validator CHANGED
@@ -1,15 +1,28 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require_relative '../lib/yaml-validator'
4
+ require 'colorize'
4
5
 
5
6
  def main
7
+ show_missing = true
8
+ if ARGV.include? '--no-missing'
9
+ show_missing = false
10
+ ARGV.delete '--no-missing'
11
+ end
12
+
6
13
  root_path = '.'
7
14
  root_path = ARGV[0] if ARGV.length > 0
8
15
 
9
- puts "Validating #{root_path}"
10
- errors = YamlValidator.new(root_path).validate()
16
+ puts "Validating #{root_path}...\n\n".colorize(:blue).underline
17
+ validator = YamlValidator.new(root_path, :show_missing => show_missing)
18
+ errors = validator.validate()
11
19
  puts errors
12
- puts "\nfound #{errors.length} errors"
20
+
21
+ if errors.length > 0
22
+ puts "\nfound #{errors.length} error(s)".colorize(:red).underline
23
+ else
24
+ puts "no errors".colorize(:green).underline
25
+ end
13
26
  end
14
27
 
15
28
  main()
data/lib/helpers.rb ADDED
@@ -0,0 +1,24 @@
1
+ module Helpers
2
+
3
+ def self.normalize_yaml(yaml)
4
+ return '' if yaml.nil?
5
+ return yaml if yaml.is_a? String
6
+ return yaml.to_s if yaml.is_a? Numeric
7
+ return yaml.to_s if !!yaml == yaml # if boolean
8
+ return ":#{yaml.to_s}" if yaml.is_a? Symbol
9
+ yaml = array_to_hash(yaml) if yaml.is_a? Array
10
+
11
+ normalized = {}
12
+ yaml.each do |key, value|
13
+ normalized[key] = normalize_yaml(value)
14
+ end
15
+ normalized
16
+ end
17
+
18
+ def self.array_to_hash(array)
19
+ hash = {}
20
+ array.each_with_index { |val, i| hash[i.to_s] = val }
21
+ hash
22
+ end
23
+
24
+ end
@@ -1,21 +1,38 @@
1
1
  require 'yaml'
2
2
  require 'yaml-validator/version'
3
+ require_relative './helpers'
3
4
 
4
5
  class YamlValidator
5
6
 
6
- def initialize(root_path)
7
+ def initialize(root_path, options = {})
8
+ @options = options
9
+ @options[:show_missing] ||= true
7
10
  @root_path = root_path
8
11
  end
9
12
 
13
+ def en
14
+ return @en unless @en.nil?
15
+
16
+ fullpath = File.join(@root_path, 'en.yml')
17
+ return nil unless File.exists?(fullpath)
18
+
19
+ @en = YAML.load_file(fullpath)['en']
20
+ @en = Helpers.normalize_yaml(@en)
21
+ end
22
+
10
23
  def en_with_vars
11
- @en ||= YAML.load_file(File.join(@root_path, 'en.yml'))['en']
12
- @en_with_vars ||= get_all_variables(@en)
24
+ return nil if en.nil?
25
+ @en_with_vars ||= get_all_variables(en)
13
26
  end
14
27
 
15
28
  def validate()
29
+ if en_with_vars.nil?
30
+ return ["no en.yml file in the directory (an en.yml file is required as reference)"]
31
+ end
16
32
  yml_files = File.join(@root_path, '*.yml')
17
33
  errors = []
18
34
  Dir[yml_files].each do |filename|
35
+ next if File.basename(filename) == 'en.yml'
19
36
  errors.concat validate_yaml(filename)
20
37
  end
21
38
  errors
@@ -29,14 +46,21 @@ class YamlValidator
29
46
  rescue Psych::SyntaxError => e
30
47
  return [e.message.sub(/^\([^)]+\)/, filename)]
31
48
  end
49
+
32
50
  yaml_object = yaml_object[yaml_object.keys[0]]
51
+ yaml_object = Helpers.normalize_yaml(yaml_object)
33
52
  errors = validate_yaml_object('', yaml_object)
53
+ if @options[:show_missing]
54
+ errors.concat find_missing_translations(yaml_object)
55
+ end
34
56
 
35
57
  errors.map { |err| "#{filename}: #{err}" }
36
58
  end
37
59
 
38
60
  def validate_yaml_object(full_key, yaml_object)
61
+ return [] if yaml_object.nil?
39
62
  errors = []
63
+
40
64
  yaml_object.each do |key, value|
41
65
  full_subkey = (full_key.empty?) ? key : "#{full_key}.#{key}"
42
66
  if value.is_a? String
@@ -48,6 +72,41 @@ class YamlValidator
48
72
  errors
49
73
  end
50
74
 
75
+ def find_missing_translations(yaml_object)
76
+ find_missing_translations_in_en_object('', en, yaml_object)
77
+ end
78
+
79
+ def find_missing_translations_in_en_object(full_key, en_yaml_object, yaml_object)
80
+ return [] if en_yaml_object.nil?
81
+ errors = []
82
+
83
+ en_yaml_object.each do |key, value|
84
+ full_subkey = (full_key.empty?) ? key : "#{full_key}.#{key}"
85
+ if value.is_a? String or value.is_a? Symbol
86
+ if self.class.find_key_in_yaml_object(full_subkey, yaml_object).nil?
87
+ errors << "missing translation for #{full_subkey} ('#{value}')"
88
+ end
89
+ else
90
+ errors.concat find_missing_translations_in_en_object(full_subkey, value, yaml_object)
91
+ end
92
+ end
93
+ errors
94
+ end
95
+
96
+ def self.find_key_in_yaml_object(full_key, yaml_object)
97
+ position = yaml_object
98
+ full_key.split('.').each do |key|
99
+ return nil unless position.is_a? Hash
100
+ position = position[key]
101
+ end
102
+
103
+ if position.is_a? String or position.is_a? Symbol
104
+ position
105
+ else
106
+ nil
107
+ end
108
+ end
109
+
51
110
  def validate_item(full_key, value)
52
111
  real_vars = get_key_en_vars(full_key)
53
112
  if real_vars.nil?
@@ -80,10 +139,14 @@ class YamlValidator
80
139
  end
81
140
 
82
141
  def get_all_variables(yaml_object)
142
+ return {} if yaml_object.nil?
83
143
  with_vars = {}
144
+
84
145
  yaml_object.each do |key, value|
85
146
  if value.is_a? String
86
147
  with_vars[key] = identify_variables(value)
148
+ elsif value.is_a? Symbol
149
+ with_vars[key] = {}
87
150
  else
88
151
  with_vars[key] = get_all_variables(value)
89
152
  end
@@ -94,5 +157,6 @@ class YamlValidator
94
157
  def identify_variables(string)
95
158
  string.scan(/%{([^}]+)}/).map { |v| v[0] }
96
159
  end
160
+
97
161
  end
98
162
 
@@ -1,3 +1,3 @@
1
1
  class YamlValidator
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
Binary file
@@ -0,0 +1,2 @@
1
+ en:
2
+ key1: value1
@@ -0,0 +1,12 @@
1
+ en:
2
+ key1: 'value1'
3
+ parent1:
4
+ subkey1: 'value1'
5
+ key2: 'value2'
6
+ parent2:
7
+ key3: 'value3'
8
+ parent3:
9
+ - one
10
+ - :two
11
+ - three
12
+ -
@@ -0,0 +1,7 @@
1
+ he:
2
+ key1: 'value1'
3
+ parent1:
4
+ subkey1: 'value1'
5
+ parent3:
6
+ - 1
7
+ - heb two
@@ -0,0 +1,53 @@
1
+ require_relative '../lib/helpers'
2
+
3
+ describe Helpers do
4
+ describe "#normalize_yaml" do
5
+ it "converts symbols to strings (with ':' prefix)" do
6
+ yaml = { 'key3' => :bob }
7
+
8
+ Helpers.normalize_yaml(yaml).should == {
9
+ 'key3' => ':bob'
10
+ }
11
+ end
12
+
13
+ it "converts nil to ''" do
14
+ yaml = { 'key3' => nil }
15
+ Helpers.normalize_yaml(yaml).should == {
16
+ 'key3' => ''
17
+ }
18
+ end
19
+
20
+ it "converts numbers to string" do
21
+ yaml = { 'key3' => 123 }
22
+ Helpers.normalize_yaml(yaml).should == {
23
+ 'key3' => '123'
24
+ }
25
+ end
26
+
27
+ it "converts booleans to string" do
28
+ yaml = { 'key3' => true }
29
+ Helpers.normalize_yaml(yaml).should == {
30
+ 'key3' => 'true'
31
+ }
32
+ end
33
+
34
+ it "converts arrays to hashes" do
35
+ yaml = { 'key1' => 'value1',
36
+ 'key2' => [ 'value2', nil, :bla ],
37
+ 'parent1' => { 'key3' => [ :bob ] } }
38
+
39
+ Helpers.normalize_yaml(yaml).should == {
40
+ 'key1' => 'value1',
41
+ 'key2' => { '0' => 'value2', '1' => '', '2' => ':bla' },
42
+ 'parent1' => { 'key3' => { '0' => ':bob' } }
43
+ }
44
+ end
45
+ end
46
+
47
+ describe "#array_to_hash" do
48
+ it "returns hash with numeric indexes" do
49
+ Helpers.array_to_hash(['a','b']).should == { '0' => 'a', '1' => 'b' }
50
+ end
51
+ end
52
+
53
+ end
@@ -6,6 +6,15 @@ describe YamlValidator do
6
6
 
7
7
  describe "#validate" do
8
8
 
9
+ describe "no en.yml file" do
10
+ it "returns 'no en.yml' error" do
11
+ validator = YamlValidator.new('spec/fixtures')
12
+ errors = validator.validate()
13
+ errors.should ==
14
+ ['no en.yml file in the directory (an en.yml file is required as reference)']
15
+ end
16
+ end
17
+
9
18
  describe "wrong_variables scenario" do
10
19
  it "returns two errors" do
11
20
  validator = YamlValidator.new('spec/fixtures/wrong_variables')
@@ -25,7 +34,9 @@ describe YamlValidator do
25
34
  "he.yml: parent1.key1.subkey1 doesn't exist in en.yml",
26
35
  "he.yml: parent2.key2 doesn't exist in en.yml",
27
36
  "he.yml: key3 doesn't exist in en.yml",
28
- "he.yml: parent3.key4 doesn't exist in en.yml"
37
+ "he.yml: parent3.key4 doesn't exist in en.yml",
38
+ "he.yml: missing translation for parent1.key1 ('Hello, %{name}, this is %{day_of_week}')",
39
+ "he.yml: missing translation for parent2.key2.subkey ('bla bla')"
29
40
  ]
30
41
  end
31
42
  end
@@ -40,6 +51,19 @@ describe YamlValidator do
40
51
  ]
41
52
  end
42
53
  end
54
+
55
+ describe "missing translations" do
56
+ it "returns invalid yaml error" do
57
+ validator = YamlValidator.new('spec/fixtures/missing_translations')
58
+ errors = validator.validate()
59
+ errors.should == [
60
+ "he.yml: missing translation for key2 ('value2')",
61
+ "he.yml: missing translation for parent2.key3 ('value3')",
62
+ "he.yml: missing translation for parent3.2 ('three')",
63
+ "he.yml: missing translation for parent3.3 ('')",
64
+ ]
65
+ end
66
+ end
43
67
 
44
68
  end
45
69
 
@@ -134,5 +158,49 @@ describe YamlValidator do
134
158
  end
135
159
  end
136
160
  end
161
+
162
+ describe "#find_missing_translations" do
163
+ it "returns the missing translation keys" do
164
+ validator = YamlValidator.new('spec/fixtures/missing_translations')
165
+
166
+ yaml_object = YAML.load_file('spec/fixtures/missing_translations/he.yml')['he']
167
+ yaml_object = Helpers.normalize_yaml(yaml_object)
168
+
169
+ errors = validator.find_missing_translations(yaml_object)
170
+ errors.should == [
171
+ "missing translation for key2 ('value2')",
172
+ "missing translation for parent2.key3 ('value3')",
173
+ "missing translation for parent3.2 ('three')",
174
+ "missing translation for parent3.3 ('')"
175
+ ]
176
+ end
177
+ end
178
+
179
+ describe "#find_key_in_yaml_object" do
180
+ it "handles subkeys" do
181
+ yaml_object = { 'parent1' => { 'key1' => 'value1' } }
182
+ YamlValidator.find_key_in_yaml_object('parent1.key1', yaml_object).should == 'value1'
183
+ end
184
+
185
+ it "handles root keys" do
186
+ yaml_object = { "key2" => 'value2' }
187
+ YamlValidator.find_key_in_yaml_object('key2', yaml_object).should == 'value2'
188
+ end
189
+
190
+ it "returns nil when a root key doesn't exist" do
191
+ yaml_object = { "key2" => 'value2' }
192
+ YamlValidator.find_key_in_yaml_object('key1', yaml_object).should be_nil
193
+ end
194
+
195
+ it "returns nil when a subkey doesn't exist" do
196
+ yaml_object = { "parent1" => { "key2" => 'value2' } }
197
+ YamlValidator.find_key_in_yaml_object('parent1.key1', yaml_object).should be_nil
198
+ end
199
+
200
+ it "returns nil when a subkey is an object" do
201
+ yaml_object = { "parent1" => { "parent2" => { "key1" => 'value1' } } }
202
+ YamlValidator.find_key_in_yaml_object('parent1.key2', yaml_object).should be_nil
203
+ end
137
204
 
205
+ end
138
206
  end
@@ -10,7 +10,7 @@ Gem::Specification.new do |gem|
10
10
  gem.email = ["3david@gmail.com"]
11
11
  gem.description = %q{YAML locales validator}
12
12
  gem.summary = %q{Validates .yml locale files for Ruby on Rails projects}
13
- gem.homepage = ""
13
+ gem.homepage = "http://github.com/wazeHQ/yaml-validator"
14
14
 
15
15
  gem.files = `git ls-files`.split($/)
16
16
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
@@ -19,4 +19,5 @@ Gem::Specification.new do |gem|
19
19
 
20
20
  gem.add_dependency('rake')
21
21
  gem.add_dependency('rspec')
22
+ gem.add_dependency('colorize')
22
23
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yaml-validator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-06 00:00:00.000000000 Z
12
+ date: 2013-02-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -43,6 +43,22 @@ dependencies:
43
43
  - - ! '>='
44
44
  - !ruby/object:Gem::Version
45
45
  version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: colorize
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
46
62
  description: YAML locales validator
47
63
  email:
48
64
  - 3david@gmail.com
@@ -52,21 +68,28 @@ extensions: []
52
68
  extra_rdoc_files: []
53
69
  files:
54
70
  - .rspec
71
+ - .travis.yml
55
72
  - Gemfile
56
73
  - Gemfile.lock
57
74
  - README.md
58
75
  - Rakefile
59
76
  - bin/yaml-validator
77
+ - lib/helpers.rb
60
78
  - lib/yaml-validator.rb
61
79
  - lib/yaml-validator/version.rb
80
+ - pkg/yaml-validator-0.0.1.gem
62
81
  - spec/fixtures/inconsistent_types/en.yml
63
82
  - spec/fixtures/inconsistent_types/he.yml
83
+ - spec/fixtures/invalid_yml/en.yml
64
84
  - spec/fixtures/invalid_yml/invalid.yml
85
+ - spec/fixtures/missing_translations/en.yml
86
+ - spec/fixtures/missing_translations/he.yml
65
87
  - spec/fixtures/wrong_variables/en.yml
66
88
  - spec/fixtures/wrong_variables/he.yml
89
+ - spec/helpers_spec.rb
67
90
  - spec/yaml-validator_spec.rb
68
91
  - yaml-validator.gemspec
69
- homepage: ''
92
+ homepage: http://github.com/wazeHQ/yaml-validator
70
93
  licenses: []
71
94
  post_install_message:
72
95
  rdoc_options: []
@@ -80,7 +103,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
80
103
  version: '0'
81
104
  segments:
82
105
  - 0
83
- hash: 1983922220360573853
106
+ hash: 1734535970002030944
84
107
  required_rubygems_version: !ruby/object:Gem::Requirement
85
108
  none: false
86
109
  requirements:
@@ -89,7 +112,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
89
112
  version: '0'
90
113
  segments:
91
114
  - 0
92
- hash: 1983922220360573853
115
+ hash: 1734535970002030944
93
116
  requirements: []
94
117
  rubyforge_project:
95
118
  rubygems_version: 1.8.24
@@ -99,7 +122,11 @@ summary: Validates .yml locale files for Ruby on Rails projects
99
122
  test_files:
100
123
  - spec/fixtures/inconsistent_types/en.yml
101
124
  - spec/fixtures/inconsistent_types/he.yml
125
+ - spec/fixtures/invalid_yml/en.yml
102
126
  - spec/fixtures/invalid_yml/invalid.yml
127
+ - spec/fixtures/missing_translations/en.yml
128
+ - spec/fixtures/missing_translations/he.yml
103
129
  - spec/fixtures/wrong_variables/en.yml
104
130
  - spec/fixtures/wrong_variables/he.yml
131
+ - spec/helpers_spec.rb
105
132
  - spec/yaml-validator_spec.rb