yaml-validator 0.0.1 → 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.
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