hash_schema 0.1.3 → 0.2.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 +4 -4
- data/.ruby-version +1 -0
- data/.travis.yml +6 -0
- data/README.md +100 -12
- data/lib/hash_schema.rb +51 -5
- data/lib/hash_schema/version.rb +1 -1
- data/spec/hash_schema_spec.rb +22 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b87caa584dee8275eeb175b5fe4b8d36107db062
|
4
|
+
data.tar.gz: 9d03b279577309ed4af1cfa41ba785fc03941641
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e608f9f48ae0fd3ff26f58c081b45677a49fb9763fc723d81e4d4f6cfaf76086caead85c9a3d00f8e5783c71e11e0732a10f340fb0391be5578413fbbb1c43b7
|
7
|
+
data.tar.gz: 0c01d4c2e02eaa7d2fb02b1756ddb666281fc0d1d5580e45585da54518e4cc81950f060d93fe81b35a811c571026983323d7ff94cb3e7d2d4f4fa69d500785ad
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.3.1
|
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -18,22 +18,110 @@ Or install it yourself as:
|
|
18
18
|
|
19
19
|
## Usage
|
20
20
|
|
21
|
+
### What it is / isn't for
|
22
|
+
|
23
|
+
It is for:
|
24
|
+
|
25
|
+
- validating structure
|
26
|
+
- e.g. a hash contains keys `greetings` and `name`, and `name` is an inner hash that contains `give`, `family` and `other`
|
27
|
+
- { "greetings": "Hello", "name": { "given": "John", "family": "Doe", "other": "" } }
|
28
|
+
- validating data type
|
29
|
+
- e.g. { "offset": [7, 8] }, where `offset` has to be an array of numbers
|
30
|
+
- validating constants
|
31
|
+
- e.g. some value has to be set to `true`
|
32
|
+
|
33
|
+
It is **not** for:
|
34
|
+
|
35
|
+
- validating dependency
|
36
|
+
- e.g. key A has to exist if key B exists
|
37
|
+
|
38
|
+
[Jump down to see detailed example](#demo)
|
39
|
+
|
21
40
|
### Defined schemas
|
22
41
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
*
|
28
|
-
|
29
|
-
|
30
|
-
|
42
|
+
#### StringSchema
|
43
|
+
|
44
|
+
- `.new`
|
45
|
+
- takes no arguments
|
46
|
+
- `#validate` :: (String|\*) -> (*Null*|error)
|
47
|
+
|
48
|
+
#### NumberSchema
|
49
|
+
|
50
|
+
- `.new`
|
51
|
+
- takes no arguments
|
52
|
+
- `#validate` :: (Number|\*) -> (*Null*|error)
|
53
|
+
|
54
|
+
#### BooleanSchema
|
55
|
+
|
56
|
+
- `.new`
|
57
|
+
- takes no arguments
|
58
|
+
- `#validate` :: (Boolean|\*) -> (*Null*|error)
|
59
|
+
|
60
|
+
#### EnumSchema
|
61
|
+
|
62
|
+
- `.new`
|
63
|
+
- takes literal constants, e.g. 123, "456", false, true...
|
64
|
+
- `#validate` :: (\*) -> (*Null*|error)
|
65
|
+
- An inclusion check is performed
|
31
66
|
|
32
|
-
|
67
|
+
#### OptionalSchema
|
68
|
+
|
69
|
+
- `.new`
|
70
|
+
- takes a Schema or literal constant
|
71
|
+
- `#validate` :: (Void|\*) -> (*Null*|error)
|
72
|
+
- `Void.new` is the internal representation of `Nothing`
|
73
|
+
- if anything other than `Void.new` is given
|
74
|
+
- it is delegated to the Schema, if a Schema was given when initializing
|
75
|
+
- it performs equality check, if initialized with anything other than a Schema
|
76
|
+
|
77
|
+
#### OrSchema
|
78
|
+
|
79
|
+
- `.new`
|
80
|
+
- takes multiple Schemas
|
81
|
+
- `#validate` :: (\*) -> (*Null*|error)
|
82
|
+
- it passes if any of the given Schema passes
|
83
|
+
- it errors if all of the given Schema end up with error
|
84
|
+
|
85
|
+
#### ArraySchema
|
86
|
+
|
87
|
+
- `.new`
|
88
|
+
- takes one Schemas
|
89
|
+
- `#validate` :: (Array|\*) -> (**Array**|error)
|
90
|
+
- it errors if the arugment passed to `#validate` is not an array
|
91
|
+
- it maps the Schema validation over the elements in the array
|
92
|
+
- it returns the mapped-over array
|
93
|
+
|
94
|
+
#### HashSchema
|
95
|
+
|
96
|
+
- `.new`
|
97
|
+
- takes keywords, see `#1`
|
98
|
+
- other than Schemas, you can also specify literal constants like in `#2`
|
99
|
+
- you can specify `strict` mode if you want to see errors for not specified keys
|
100
|
+
- if you have the key `strict` in your data hash, see `#3`
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
#1
|
104
|
+
HashSchema.new(a: NumberSchema.new, b: BooleanSchema.new, c: StringSchema.new)
|
105
|
+
|
106
|
+
#2
|
107
|
+
HashSchema.new(strict: true,
|
108
|
+
number: 1,
|
109
|
+
boolean: true,
|
110
|
+
string: 'string',
|
111
|
+
ampm_array: ArraySchema.new(EnumSchema.new('am', 'pm'))
|
112
|
+
)
|
113
|
+
|
114
|
+
#3
|
115
|
+
HashSchema.new(strict: true,
|
116
|
+
schema_hash: {
|
117
|
+
strict: 'I am strict'
|
118
|
+
}
|
119
|
+
)
|
120
|
+
```
|
33
121
|
|
34
|
-
- `#valdiate` :: Hash -> Hash
|
35
|
-
- `#pretty_validate` :: Hash -> String **(*)**
|
36
|
-
- `#interpret` :: Hash -> [String]
|
122
|
+
- `#valdiate` :: (Hash|\*) -> (**Hash**|error)
|
123
|
+
- `#pretty_validate` :: (Hash|\*) -> String **(*)**
|
124
|
+
- `#interpret` :: (Hash|\*) -> [String]
|
37
125
|
|
38
126
|
**(*)** `#pretty_validate` is just a `JSON.pretty_generate` wrapper of `#validate`
|
39
127
|
|
data/lib/hash_schema.rb
CHANGED
@@ -86,19 +86,65 @@ module HashSchema
|
|
86
86
|
end
|
87
87
|
|
88
88
|
def validate(data)
|
89
|
+
empty_error = true
|
90
|
+
last_error = nil
|
89
91
|
chain.each do |schema|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
92
|
+
next unless schema_matches_data_type(schema, data)
|
93
|
+
last_error = schema.validate(data)
|
94
|
+
empty_error = recursive_empty_error?(last_error)
|
95
|
+
break if empty_error
|
96
|
+
end
|
97
|
+
if empty_error
|
98
|
+
last_error
|
99
|
+
else
|
100
|
+
error(data)
|
94
101
|
end
|
95
|
-
error(data)
|
96
102
|
end
|
97
103
|
|
98
104
|
def expectation
|
99
105
|
*names, name = chain.map(&:expectation)
|
100
106
|
names.join(', ') << " or #{name}"
|
101
107
|
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def recursive_empty_error?(error)
|
112
|
+
case error
|
113
|
+
when NilClass
|
114
|
+
true
|
115
|
+
when String
|
116
|
+
false
|
117
|
+
when Hash, Array
|
118
|
+
recursive_flat_error_values(error).empty?
|
119
|
+
else
|
120
|
+
false
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def schema_matches_data_type(schema, data)
|
125
|
+
case schema
|
126
|
+
when ArraySchema
|
127
|
+
data.is_a? Array
|
128
|
+
when HashSchema
|
129
|
+
data.is_a? Hash
|
130
|
+
else
|
131
|
+
true
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def recursive_flat_error_values(error_object)
|
136
|
+
if error_object.is_a? Hash
|
137
|
+
error_object.values.flat_map do |value|
|
138
|
+
recursive_flat_error_values(value)
|
139
|
+
end.compact
|
140
|
+
elsif error_object.is_a? Array
|
141
|
+
error_object.flat_map do |value|
|
142
|
+
recursive_flat_error_values(value)
|
143
|
+
end.compact
|
144
|
+
else
|
145
|
+
error_object
|
146
|
+
end
|
147
|
+
end
|
102
148
|
end
|
103
149
|
|
104
150
|
class StringSchema < Schema
|
data/lib/hash_schema/version.rb
CHANGED
data/spec/hash_schema_spec.rb
CHANGED
@@ -113,6 +113,9 @@ describe HashSchema do
|
|
113
113
|
HashSchema::NumberSchema.new,
|
114
114
|
HashSchema::StringSchema.new,
|
115
115
|
HashSchema::ArraySchema.new(HashSchema::StringSchema.new),
|
116
|
+
HashSchema::ArraySchema.new(HashSchema::NumberSchema.new),
|
117
|
+
HashSchema::ArraySchema.new(HashSchema::HashSchema.new(stringy: HashSchema::StringSchema.new)),
|
118
|
+
HashSchema::HashSchema.new(boolean: HashSchema::BooleanSchema),
|
116
119
|
HashSchema::HashSchema.new
|
117
120
|
)
|
118
121
|
end
|
@@ -131,7 +134,25 @@ describe HashSchema do
|
|
131
134
|
end
|
132
135
|
|
133
136
|
it 'delegates to given HashSchema for hash' do
|
134
|
-
expect(multitype.validate(
|
137
|
+
expect(multitype.validate(boolean: true)).to be_a(Hash)
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'delegates to more than one inner ArraySchema' do
|
141
|
+
validation_result = multitype.validate([1, 2, 3])
|
142
|
+
expect(validation_result).to be_a(Array)
|
143
|
+
expect(validation_result.compact).to be_empty
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'delegates to more than one inner HashSchema' do
|
147
|
+
validation_result = multitype.validate(boolean: 5)
|
148
|
+
expect(validation_result).to be_a(Hash)
|
149
|
+
expect(validation_result.values.compact).to be_empty
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'recurses into hashes and arrays' do
|
153
|
+
validation_result = multitype.validate([ { stringy: 'hello' } ])
|
154
|
+
expect(validation_result).to be_a(Array)
|
155
|
+
expect(validation_result).to eq [ { stringy: nil } ]
|
135
156
|
end
|
136
157
|
end
|
137
158
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hash_schema
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Po Chen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-10-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bump
|
@@ -91,6 +91,8 @@ extensions: []
|
|
91
91
|
extra_rdoc_files: []
|
92
92
|
files:
|
93
93
|
- ".gitignore"
|
94
|
+
- ".ruby-version"
|
95
|
+
- ".travis.yml"
|
94
96
|
- Gemfile
|
95
97
|
- LICENSE.txt
|
96
98
|
- README.md
|
@@ -120,7 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
120
122
|
version: '0'
|
121
123
|
requirements: []
|
122
124
|
rubyforge_project:
|
123
|
-
rubygems_version: 2.
|
125
|
+
rubygems_version: 2.6.4
|
124
126
|
signing_key:
|
125
127
|
specification_version: 4
|
126
128
|
summary: Validate Hash against Schema
|