hash_schema 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 +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +8 -0
- data/hash_schema.gemspec +30 -0
- data/lib/hash_schema.rb +188 -0
- data/lib/hash_schema/version.rb +3 -0
- data/spec/hash_schema_spec.rb +232 -0
- data/spec/spec_helper.rb +1 -0
- metadata +129 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 4a30a25b38f10cad859deefa8e318414c94055ab
|
|
4
|
+
data.tar.gz: 4683b1715e4b134c3ea58bbf72bb711268b13e35
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 134fe97ce8908958baa7ca21b01990e5e50da4ff3d4c99fa9025cfd2228f639d045dcb453f2f0ff6c70ec1608433ba4f8c986467bbc445d41221c5081887359c
|
|
7
|
+
data.tar.gz: d1c4aacaa841a7ce398becee5dc10a8cce192905a4eb84c82c23d5ce8258e0e0af5cd80827ddaf78943b04340b961d0ba8db1b96c6a903ba0d9f06d54524fe0f
|
data/.gitignore
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
*.gem
|
|
2
|
+
*.rbc
|
|
3
|
+
.bundle
|
|
4
|
+
.config
|
|
5
|
+
.yardoc
|
|
6
|
+
Gemfile.lock
|
|
7
|
+
InstalledFiles
|
|
8
|
+
_yardoc
|
|
9
|
+
coverage
|
|
10
|
+
doc/
|
|
11
|
+
lib/bundler/man
|
|
12
|
+
pkg
|
|
13
|
+
rdoc
|
|
14
|
+
spec/reports
|
|
15
|
+
test/tmp
|
|
16
|
+
test/version_tmp
|
|
17
|
+
tmp
|
|
18
|
+
*.bundle
|
|
19
|
+
*.so
|
|
20
|
+
*.o
|
|
21
|
+
*.a
|
|
22
|
+
mkmf.log
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2014 Po Chen
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# HashSchema
|
|
2
|
+
|
|
3
|
+
A ruby gem that validates Hash against some schema, works for hashes created from loading json and yml files, and suchlike
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add this line to your application's Gemfile:
|
|
8
|
+
|
|
9
|
+
gem 'hash_schema'
|
|
10
|
+
|
|
11
|
+
And then execute:
|
|
12
|
+
|
|
13
|
+
$ bundle
|
|
14
|
+
|
|
15
|
+
Or install it yourself as:
|
|
16
|
+
|
|
17
|
+
$ gem install hash_schema
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
TODO: Write usage instructions here
|
|
22
|
+
|
|
23
|
+
## Contributing
|
|
24
|
+
|
|
25
|
+
1. Fork it ( https://github.com/[my-github-username]/hash_schema/fork )
|
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
29
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/hash_schema.gemspec
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'hash_schema/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "hash_schema"
|
|
8
|
+
spec.version = HashSchema::VERSION
|
|
9
|
+
spec.authors = ["Po Chen"]
|
|
10
|
+
spec.email = ["pchen@zendesk.com"]
|
|
11
|
+
spec.summary = %q{Validate Hash against Schema}
|
|
12
|
+
spec.description = <<-DESC
|
|
13
|
+
This gem provides schema building blocks that can be used to define
|
|
14
|
+
complicated hash structures, which can then be used to validate hashes
|
|
15
|
+
that are created by loading json or yml files.
|
|
16
|
+
DESC
|
|
17
|
+
spec.homepage = "https://github.com/princemaple/hash_schema"
|
|
18
|
+
spec.license = "MIT"
|
|
19
|
+
|
|
20
|
+
spec.files = `git ls-files -z`.split("\x0")
|
|
21
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
22
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
23
|
+
spec.require_paths = ["lib"]
|
|
24
|
+
|
|
25
|
+
spec.add_development_dependency "bump"
|
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
|
27
|
+
spec.add_development_dependency "byebug"
|
|
28
|
+
spec.add_development_dependency "rake"
|
|
29
|
+
spec.add_development_dependency "rspec"
|
|
30
|
+
end
|
data/lib/hash_schema.rb
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
require "hash_schema/version"
|
|
2
|
+
|
|
3
|
+
module HashSchema
|
|
4
|
+
class Schema
|
|
5
|
+
def initialize(chain = nil)
|
|
6
|
+
@chain = chain
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def interpret(data)
|
|
10
|
+
(@interpretations = []).tap do |_|
|
|
11
|
+
interpret_errors(validate(data), 'root')
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
attr_reader :chain
|
|
16
|
+
|
|
17
|
+
def pretty_validate(data)
|
|
18
|
+
require 'json'
|
|
19
|
+
JSON.pretty_generate(validate(data))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def expectation
|
|
23
|
+
self.class.name.split('::').last.sub('Schema', '')
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
protected
|
|
27
|
+
|
|
28
|
+
def error(data)
|
|
29
|
+
expect(expectation, data)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def expect(wanted, unwanted)
|
|
33
|
+
"Expected #{wanted} but got #{unwanted.inspect}"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def interpret_errors(errors, *prefixes)
|
|
39
|
+
*prefixes, prefix = prefixes
|
|
40
|
+
prefixes <<= format(errors, prefix)
|
|
41
|
+
|
|
42
|
+
case errors
|
|
43
|
+
when Hash
|
|
44
|
+
errors.each do |key, val|
|
|
45
|
+
next if val.nil?
|
|
46
|
+
interpret_errors(val, *prefixes, key)
|
|
47
|
+
end
|
|
48
|
+
when Array
|
|
49
|
+
errors.each_with_index do |val, index|
|
|
50
|
+
next if val.nil?
|
|
51
|
+
interpret_errors(val, *prefixes, index)
|
|
52
|
+
end
|
|
53
|
+
when String
|
|
54
|
+
@interpretations << (prefixes << errors).join(' > ')
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def format(error, name)
|
|
59
|
+
if name.kind_of?(Numeric)
|
|
60
|
+
name = "##{name}"
|
|
61
|
+
return name if error.is_a?(String)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
case error
|
|
65
|
+
when Hash
|
|
66
|
+
"#{name}:{}"
|
|
67
|
+
when Array
|
|
68
|
+
"#{name}:[]"
|
|
69
|
+
when String
|
|
70
|
+
".#{name}"
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class OptionalSchema < Schema
|
|
76
|
+
def validate(data)
|
|
77
|
+
return if data.is_a?(Void)
|
|
78
|
+
return chain.validate(data) if chain.kind_of?(Schema)
|
|
79
|
+
expect(chain.inspect, data) unless chain == data
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
class OrSchema < Schema
|
|
84
|
+
def initialize(*schemas)
|
|
85
|
+
@chain = schemas
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def validate(data)
|
|
89
|
+
chain.each do |schema|
|
|
90
|
+
if data.is_a?(Array) && schema.is_a?(ArraySchema) || data.is_a?(Hash) && schema.is_a?(HashSchema)
|
|
91
|
+
return schema.validate(data)
|
|
92
|
+
end
|
|
93
|
+
return if schema.validate(data).nil?
|
|
94
|
+
end
|
|
95
|
+
error(data)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def expectation
|
|
99
|
+
*names, name = chain.map(&:expectation)
|
|
100
|
+
names.join(', ') << " or #{name}"
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
class StringSchema < Schema
|
|
105
|
+
def validate(data)
|
|
106
|
+
return if data.is_a?(String)
|
|
107
|
+
error(data)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
class NumberSchema < Schema
|
|
112
|
+
def validate(data)
|
|
113
|
+
return if data.kind_of?(Numeric)
|
|
114
|
+
error(data)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
class BooleanSchema < Schema
|
|
119
|
+
def validate(data)
|
|
120
|
+
return if data.is_a?(TrueClass) || data.is_a?(FalseClass)
|
|
121
|
+
error(data)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
class ArraySchema < Schema
|
|
126
|
+
def validate(data)
|
|
127
|
+
return data.map { |item| chain.validate(item) } if data.is_a?(Array)
|
|
128
|
+
error(data)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def expectation
|
|
132
|
+
"[#{chain.expectation}]"
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
class EnumSchema < Schema
|
|
137
|
+
def initialize(*enum)
|
|
138
|
+
@chain = enum
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def validate(data)
|
|
142
|
+
return if chain.include?(data)
|
|
143
|
+
error(data)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def expectation
|
|
147
|
+
*vals, val = chain.map(&:inspect)
|
|
148
|
+
vals.join(', ') << " or #{val}"
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
class HashSchema < Schema
|
|
153
|
+
def initialize(strict: false, **hash)
|
|
154
|
+
@chain = hash
|
|
155
|
+
@strict = strict
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def validate(hash)
|
|
159
|
+
return error(hash) unless hash.is_a?(Hash)
|
|
160
|
+
{}.tap do |output|
|
|
161
|
+
chain.each do |key, schema|
|
|
162
|
+
val = hash[key] || hash.fetch(key.to_s, Void.new)
|
|
163
|
+
output[key] = if schema.kind_of?(Schema)
|
|
164
|
+
schema.validate(val)
|
|
165
|
+
else
|
|
166
|
+
schema == val ? nil : expect(schema.inspect, val)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
output.merge!((hash.keys - chain.keys).map { |k| [k, unexpected(k)] }.to_h) if @strict
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def unexpected(key)
|
|
175
|
+
"Unexpected key: #{key.to_s.inspect}"
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
private
|
|
180
|
+
|
|
181
|
+
class Void
|
|
182
|
+
def to_s
|
|
183
|
+
'Nothing'
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
alias :inspect :to_s
|
|
187
|
+
end
|
|
188
|
+
end
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe HashSchema do
|
|
4
|
+
Testers = ['abc123', 123, true, HashSchema::Void.new]
|
|
5
|
+
|
|
6
|
+
def except(type)
|
|
7
|
+
Testers.reject { |tester| tester.is_a? type }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
describe HashSchema::StringSchema do
|
|
11
|
+
subject { HashSchema::StringSchema }
|
|
12
|
+
|
|
13
|
+
it 'passes for String type value' do
|
|
14
|
+
expect(subject.new.validate('abc123!@#$%^&*()_=-+')).to be_nil
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'fails for other types' do
|
|
18
|
+
except(String).each do |tester|
|
|
19
|
+
expect(subject.new.validate(tester)).to_not be_nil
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe HashSchema::NumberSchema do
|
|
25
|
+
subject { HashSchema::NumberSchema }
|
|
26
|
+
|
|
27
|
+
it 'passes for Numeric type value' do
|
|
28
|
+
expect(subject.new.validate(1234567890)).to be_nil
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'fails for other types' do
|
|
32
|
+
except(Numeric).each do |tester|
|
|
33
|
+
expect(subject.new.validate(tester)).to_not be_nil
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe HashSchema::BooleanSchema do
|
|
39
|
+
subject { HashSchema::BooleanSchema }
|
|
40
|
+
|
|
41
|
+
it 'passes for Boolean type value' do
|
|
42
|
+
expect(subject.new.validate(true)).to be_nil
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'fails for other types' do
|
|
46
|
+
except(TrueClass).each do |tester|
|
|
47
|
+
expect(subject.new.validate(tester)).to_not be_nil
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
describe HashSchema::OptionalSchema do
|
|
53
|
+
subject { HashSchema::OptionalSchema }
|
|
54
|
+
|
|
55
|
+
it 'passes for Void type value' do
|
|
56
|
+
expect(subject.new.validate(HashSchema::Void.new)).to be_nil
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
context 'when initialized with a Schema' do
|
|
60
|
+
it 'delegates to the inner Schema' do
|
|
61
|
+
schema = Class.new(HashSchema::Schema).new
|
|
62
|
+
|
|
63
|
+
expect(schema).to receive(:validate)
|
|
64
|
+
|
|
65
|
+
subject.new(schema).validate(1)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
context 'when initialized with literal value' do
|
|
70
|
+
it 'passes for the exact value' do
|
|
71
|
+
expect(subject.new(1).validate(1)).to be_nil
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it 'fails for different value' do
|
|
75
|
+
expect(subject.new(1).validate(2)).to_not be_nil
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
describe HashSchema::EnumSchema do
|
|
81
|
+
subject { HashSchema::EnumSchema.new(1, 'a', true) }
|
|
82
|
+
|
|
83
|
+
it 'passes for provided literal values' do
|
|
84
|
+
expect(subject.validate(1)).to be_nil
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it 'passes for provided literal values' do
|
|
88
|
+
expect(subject.validate('a')).to be_nil
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it 'passes for provided literal values' do
|
|
92
|
+
expect(subject.validate(true)).to be_nil
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it 'fails for other values' do
|
|
96
|
+
expect(subject.validate(2)).to_not be_nil
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it 'fails for other values' do
|
|
100
|
+
expect(subject.validate('b')).to_not be_nil
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it 'fails for other values' do
|
|
104
|
+
expect(subject.validate(false)).to_not be_nil
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
describe HashSchema::OrSchema do
|
|
109
|
+
subject { HashSchema::OrSchema }
|
|
110
|
+
|
|
111
|
+
let(:multitype) do
|
|
112
|
+
subject.new(
|
|
113
|
+
HashSchema::NumberSchema.new,
|
|
114
|
+
HashSchema::StringSchema.new,
|
|
115
|
+
HashSchema::ArraySchema.new(HashSchema::StringSchema.new),
|
|
116
|
+
HashSchema::HashSchema.new
|
|
117
|
+
)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it 'passes if any inner schema passes' do
|
|
121
|
+
expect(multitype.validate(1)).to be_nil
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
it 'passes if any inner schema passes' do
|
|
125
|
+
expect(multitype.validate('a')).to be_nil
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
context 'when having inner ArraySchema or HashSchema' do
|
|
129
|
+
it 'delegates to given ArraySchema for array' do
|
|
130
|
+
expect(multitype.validate([])).to be_a(Array)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it 'delegates to given HashSchema for hash' do
|
|
134
|
+
expect(multitype.validate({})).to be_a(Hash)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it 'fails if all inner schema fail' do
|
|
139
|
+
expect(multitype.validate(false)).to_not be_nil
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
describe HashSchema::ArraySchema do
|
|
144
|
+
subject { HashSchema::ArraySchema }
|
|
145
|
+
|
|
146
|
+
let(:string_array) { ['a', 'b', 'c', 'd'] }
|
|
147
|
+
let(:chaos_array) { ['a', 1, false, [], {}] }
|
|
148
|
+
let(:string_array_schema) { subject.new(HashSchema::StringSchema.new) }
|
|
149
|
+
let(:hash_array_schema) { subject.new(HashSchema::HashSchema.new) }
|
|
150
|
+
|
|
151
|
+
it 'passes for array of the given type' do
|
|
152
|
+
expect(string_array_schema.validate(string_array).compact).to eq([])
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
it 'fails for non-array input' do
|
|
156
|
+
expect(string_array_schema.validate(123)).to_not be_nil
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
it 'handles nested structure' do
|
|
160
|
+
expect(hash_array_schema.validate([{}, { x: 1 }, { y: 2 }])).to eq([{}, {}, {}])
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
it 'fails for array containing values not being the given type' do
|
|
164
|
+
expect(string_array_schema.validate(chaos_array).compact.size).to eq(4)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
describe HashSchema::HashSchema do
|
|
169
|
+
subject { HashSchema::HashSchema }
|
|
170
|
+
|
|
171
|
+
it 'passes if all literal attributes pass' do
|
|
172
|
+
schema = subject.new(
|
|
173
|
+
string: HashSchema::StringSchema.new,
|
|
174
|
+
number: HashSchema::NumberSchema.new,
|
|
175
|
+
boolean: HashSchema::BooleanSchema.new,
|
|
176
|
+
nothing: HashSchema::OptionalSchema.new,
|
|
177
|
+
enum: HashSchema::EnumSchema.new(1, 'a', true)
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
data = {
|
|
181
|
+
string: 'abc',
|
|
182
|
+
number: 123,
|
|
183
|
+
boolean: true,
|
|
184
|
+
enum: 'a'
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
expect(schema.validate(data).reject { |_, v| v.nil? }).to be_empty
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
it 'handles nested structure' do
|
|
191
|
+
schema = subject.new(
|
|
192
|
+
hash: subject.new(
|
|
193
|
+
literal: 1
|
|
194
|
+
)
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
data = {
|
|
198
|
+
hash: {
|
|
199
|
+
literal: 1
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
expect(schema.validate(data)).to eq({ hash: { literal: nil } })
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
it 'handles nested structure' do
|
|
207
|
+
schema = subject.new(
|
|
208
|
+
array: HashSchema::ArraySchema.new(HashSchema::BooleanSchema.new)
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
data = {
|
|
212
|
+
array: [true, false]
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
expect(schema.validate(data)).to eq({ array: [nil, nil] })
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
it 'fails for non-hash input' do
|
|
219
|
+
expect(subject.new.validate(123)).to_not be_nil
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
context 'when strict mode' do
|
|
223
|
+
it 'fails for extra attributes' do
|
|
224
|
+
schema = subject.new(strict: true, x: 0)
|
|
225
|
+
|
|
226
|
+
data = { x: 0, y: 1 }
|
|
227
|
+
|
|
228
|
+
expect(schema.validate(data).reject { |_, v| v.nil? }).to include(:y)
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'hash_schema'
|
metadata
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: hash_schema
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Po Chen
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2014-11-02 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bump
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: bundler
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '1.6'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '1.6'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: byebug
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rake
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rspec
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
83
|
+
description: |2
|
|
84
|
+
This gem provides schema building blocks that can be used to define
|
|
85
|
+
complicated hash structures, which can then be used to validate hashes
|
|
86
|
+
that are created by loading json or yml files.
|
|
87
|
+
email:
|
|
88
|
+
- pchen@zendesk.com
|
|
89
|
+
executables: []
|
|
90
|
+
extensions: []
|
|
91
|
+
extra_rdoc_files: []
|
|
92
|
+
files:
|
|
93
|
+
- ".gitignore"
|
|
94
|
+
- Gemfile
|
|
95
|
+
- LICENSE.txt
|
|
96
|
+
- README.md
|
|
97
|
+
- Rakefile
|
|
98
|
+
- hash_schema.gemspec
|
|
99
|
+
- lib/hash_schema.rb
|
|
100
|
+
- lib/hash_schema/version.rb
|
|
101
|
+
- spec/hash_schema_spec.rb
|
|
102
|
+
- spec/spec_helper.rb
|
|
103
|
+
homepage: https://github.com/princemaple/hash_schema
|
|
104
|
+
licenses:
|
|
105
|
+
- MIT
|
|
106
|
+
metadata: {}
|
|
107
|
+
post_install_message:
|
|
108
|
+
rdoc_options: []
|
|
109
|
+
require_paths:
|
|
110
|
+
- lib
|
|
111
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
112
|
+
requirements:
|
|
113
|
+
- - ">="
|
|
114
|
+
- !ruby/object:Gem::Version
|
|
115
|
+
version: '0'
|
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
|
+
requirements:
|
|
118
|
+
- - ">="
|
|
119
|
+
- !ruby/object:Gem::Version
|
|
120
|
+
version: '0'
|
|
121
|
+
requirements: []
|
|
122
|
+
rubyforge_project:
|
|
123
|
+
rubygems_version: 2.2.2
|
|
124
|
+
signing_key:
|
|
125
|
+
specification_version: 4
|
|
126
|
+
summary: Validate Hash against Schema
|
|
127
|
+
test_files:
|
|
128
|
+
- spec/hash_schema_spec.rb
|
|
129
|
+
- spec/spec_helper.rb
|