rschema 1.0.1 → 1.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 +4 -4
- data/README.md +103 -43
- data/lib/rschema.rb +30 -27
- data/lib/rschema/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cb8c9bd48727609ab023125d1e36c302db187f18
|
|
4
|
+
data.tar.gz: f78899c23ad12c9b089b8789a3c5b1cfd36daf20
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9e190a4eb7fd75bf1a119f31aa29f3265dd4750b3732848c13468ca5226edaed503bc28bfdfde73a1cb4b4d7bbe1a20674586431d7991efb7eda9ab9162bd332
|
|
7
|
+
data.tar.gz: edc80b54868d241d2ad2e83dceacaddd4d23c750f700a1707126f35f2b676fb9c221f0f8c96e2136fd69bb356edc15bbf702c7c4f1a5a81f257e6b09c383e04e
|
data/README.md
CHANGED
|
@@ -21,11 +21,11 @@ Schemas can be used to validate data. That is, they can check whether
|
|
|
21
21
|
data is in the correct shape:
|
|
22
22
|
|
|
23
23
|
```ruby
|
|
24
|
-
RSchema.validate
|
|
24
|
+
RSchema.validate(post_schema, {
|
|
25
25
|
title: "You won't beleive how this developer foo'd her bar",
|
|
26
26
|
tags: [:foos, :bars, :unbeleivable],
|
|
27
27
|
body: '<p>blah blah</p>'
|
|
28
|
-
})
|
|
28
|
+
}) #=> true
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
What is a schema?
|
|
@@ -35,8 +35,8 @@ Schemas are Ruby data structures. The simplest type of schema is just a class:
|
|
|
35
35
|
|
|
36
36
|
```ruby
|
|
37
37
|
schema = Integer
|
|
38
|
-
RSchema.validate
|
|
39
|
-
RSchema.validate
|
|
38
|
+
RSchema.validate(schema, 5) #=> true
|
|
39
|
+
RSchema.validate(schema, 'hello') #=> false
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
Then there are composite schemas, which are schemas composed of subschemas.
|
|
@@ -44,16 +44,16 @@ Arrays are composite schemas:
|
|
|
44
44
|
|
|
45
45
|
```ruby
|
|
46
46
|
schema = [Integer]
|
|
47
|
-
RSchema.validate
|
|
48
|
-
RSchema.validate
|
|
47
|
+
RSchema.validate(schema, [10, 11, 12]) #=> true
|
|
48
|
+
RSchema.validate(schema, [10, 11, '12']) #=> false
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
And so are hashes:
|
|
52
52
|
|
|
53
53
|
```ruby
|
|
54
54
|
schema = { fname: String, age: Integer }
|
|
55
|
-
RSchema.validate
|
|
56
|
-
RSchema.validate
|
|
55
|
+
RSchema.validate(schema, { fname: 'Jane', age: 27 }) #=> true
|
|
56
|
+
RSchema.validate(schema, { fname: 'Johnny' }) #=> false
|
|
57
57
|
```
|
|
58
58
|
|
|
59
59
|
While schemas are just plain old Ruby data structures, RSchema also provides
|
|
@@ -66,14 +66,67 @@ schema = RSchema.schema {{
|
|
|
66
66
|
children_by_age: hash_of(Integer => String)
|
|
67
67
|
}}
|
|
68
68
|
|
|
69
|
-
RSchema.validate
|
|
69
|
+
RSchema.validate(schema, {
|
|
70
70
|
fname: 'Johnny',
|
|
71
71
|
favourite_foods: Set.new([:bacon, :cheese, :onion]),
|
|
72
72
|
children_by_age: {
|
|
73
73
|
7 => 'Jenny',
|
|
74
74
|
5 => 'Simon'
|
|
75
75
|
}
|
|
76
|
-
})
|
|
76
|
+
}) #=> true
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
When Validation Fails
|
|
80
|
+
---------------------
|
|
81
|
+
|
|
82
|
+
Using `RSchema.validate`, it is often difficult to tell exactly which values
|
|
83
|
+
are failing validation.
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
schema = RSchema.schema do
|
|
87
|
+
[
|
|
88
|
+
{
|
|
89
|
+
name: String,
|
|
90
|
+
hair: enum([:red, :brown, :blonde, :black])
|
|
91
|
+
}
|
|
92
|
+
]
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
value = [
|
|
96
|
+
{ name: 'Dane', hair: :black },
|
|
97
|
+
{ name: 'Tom', hair: :brown },
|
|
98
|
+
{ name: 'Effie', hair: :blond },
|
|
99
|
+
{ name: 'Chris', hair: :red },
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
RSchema.validate(schema, value) #=> false
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
To see exactly where validation fails, we can look at an
|
|
106
|
+
`RSchema::ErrorDetails` object.
|
|
107
|
+
|
|
108
|
+
The `validate!` method throws an exception when validation fails, and the
|
|
109
|
+
exception contains the `RSchema::ErrorDetails` object.
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
RSchema.validate!(schema, value) # throws exception:
|
|
113
|
+
#=> RSchema::ValidationError: The value at [2, :hair] is not a valid enum member: :blond
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The error above says that the value `:blond`, which exists at location
|
|
117
|
+
`value[2][:hair]`, is not a valid enum member. Looking back at the schema, we
|
|
118
|
+
see that there is a typo, and it should be `:blonde` instead of `:blond`.
|
|
119
|
+
|
|
120
|
+
To get an `RSchema::ErrorDetails` object _without_ using exceptions, we can use
|
|
121
|
+
the `RSchema.validation_error` method.
|
|
122
|
+
|
|
123
|
+
```ruby
|
|
124
|
+
error_details = RSchema.validation_error(schema, value)
|
|
125
|
+
|
|
126
|
+
error_details.failing_value #=> :blond
|
|
127
|
+
error_details.reason #=> "is not a valid enum member"
|
|
128
|
+
error_details.key_path #=> [2, :hair]
|
|
129
|
+
error_details.to_s #=> "The value at [2, :hair] is not a valid enum member: :blond"
|
|
77
130
|
```
|
|
78
131
|
|
|
79
132
|
Array Schemas
|
|
@@ -84,18 +137,18 @@ element, it is a variable-length array schema:
|
|
|
84
137
|
|
|
85
138
|
```ruby
|
|
86
139
|
schema = [Symbol]
|
|
87
|
-
RSchema.validate
|
|
88
|
-
RSchema.validate
|
|
89
|
-
RSchema.validate
|
|
140
|
+
RSchema.validate(schema, [:a, :b, :c]) #=> true
|
|
141
|
+
RSchema.validate(schema, [:a]) #=> true
|
|
142
|
+
RSchema.validate(schema, []) #=> true
|
|
90
143
|
```
|
|
91
144
|
|
|
92
145
|
Otherwise, it is a fixed-length array schema
|
|
93
146
|
|
|
94
147
|
```ruby
|
|
95
148
|
schema = [Integer, String]
|
|
96
|
-
RSchema.validate
|
|
97
|
-
RSchema.validate
|
|
98
|
-
RSchema.validate
|
|
149
|
+
RSchema.validate(schema, [10, 'hello']) #=> true
|
|
150
|
+
RSchema.validate(schema, [10, 'hello', 'world']) #=> false
|
|
151
|
+
RSchema.validate(schema, [10]) #=> false
|
|
99
152
|
```
|
|
100
153
|
|
|
101
154
|
Hash Schemas
|
|
@@ -105,7 +158,7 @@ Hash schemas map constant keys to subschema values:
|
|
|
105
158
|
|
|
106
159
|
```ruby
|
|
107
160
|
schema = { fname: String }
|
|
108
|
-
RSchema.validate
|
|
161
|
+
RSchema.validate(schema, { fname: 'William' }) #=> true
|
|
109
162
|
```
|
|
110
163
|
|
|
111
164
|
Keys can be optional:
|
|
@@ -115,8 +168,8 @@ schema = RSchema.schema {{
|
|
|
115
168
|
:fname => String,
|
|
116
169
|
_?(:age) => Integer
|
|
117
170
|
}}
|
|
118
|
-
RSchema.validate
|
|
119
|
-
RSchema.validate
|
|
171
|
+
RSchema.validate(schema, { fname: 'Lucy', age: 21 }) #=> true
|
|
172
|
+
RSchema.validate(schema, { fname: 'Tom' }) #=> true
|
|
120
173
|
```
|
|
121
174
|
|
|
122
175
|
There is also another type of hash schema that represents hashes with variable
|
|
@@ -124,9 +177,9 @@ keys:
|
|
|
124
177
|
|
|
125
178
|
```ruby
|
|
126
179
|
schema = RSchema.schema { hash_of(String => Integer) }
|
|
127
|
-
RSchema.validate
|
|
128
|
-
RSchema.validate
|
|
129
|
-
RSchema.validate
|
|
180
|
+
RSchema.validate(schema, { 'hello' => 1, 'world' => 2 }) #=> true
|
|
181
|
+
RSchema.validate(schema, { 'hello' => 1 }) #=> true
|
|
182
|
+
RSchema.validate(schema, {}) #=> true
|
|
130
183
|
```
|
|
131
184
|
|
|
132
185
|
Other Schema Types
|
|
@@ -137,25 +190,25 @@ RSchema provides a few other schema types through its DSL:
|
|
|
137
190
|
```ruby
|
|
138
191
|
# boolean
|
|
139
192
|
boolean_schema = RSchema.schema{ boolean }
|
|
140
|
-
RSchema.validate
|
|
141
|
-
RSchema.validate
|
|
193
|
+
RSchema.validate(boolean_schema, false) #=> true
|
|
194
|
+
RSchema.validate(boolean_schema, nil) #=> false
|
|
142
195
|
|
|
143
196
|
# maybe
|
|
144
197
|
maybe_schema = RSchema.schema{ maybe(Integer) }
|
|
145
|
-
RSchema.validate
|
|
146
|
-
RSchema.validate
|
|
198
|
+
RSchema.validate(maybe_schema, 5) #=> true
|
|
199
|
+
RSchema.validate(maybe_schema, nil) #=> true
|
|
147
200
|
|
|
148
201
|
# enum
|
|
149
202
|
enum_schema = RSchema.schema{ enum([:a, :b, :c]) }
|
|
150
|
-
RSchema.validate
|
|
151
|
-
RSchema.validate
|
|
203
|
+
RSchema.validate(enum_schema, :a) #=> true
|
|
204
|
+
RSchema.validate(enum_schema, :z) #=> false
|
|
152
205
|
|
|
153
206
|
# predicate
|
|
154
207
|
predicate_schema = RSchema.schema do
|
|
155
208
|
predicate('even') { |x| x.even? }
|
|
156
209
|
end
|
|
157
|
-
RSchema.validate
|
|
158
|
-
RSchema.validate
|
|
210
|
+
RSchema.validate(predicate_schema, 4) #=> true
|
|
211
|
+
RSchema.validate(predicate_schema, 5) #=> false
|
|
159
212
|
```
|
|
160
213
|
|
|
161
214
|
Coercion
|
|
@@ -188,18 +241,23 @@ RSchema.coerce!(schema, value)
|
|
|
188
241
|
Extending the DSL
|
|
189
242
|
-----------------
|
|
190
243
|
|
|
191
|
-
|
|
244
|
+
You can create new, custom DSLs that extend the default DSL like so:
|
|
192
245
|
|
|
193
246
|
```ruby
|
|
194
|
-
module
|
|
247
|
+
module MyCustomDSL
|
|
248
|
+
extend RSchema::DSL::Base
|
|
195
249
|
def self.positive_and_even(type)
|
|
196
250
|
predicate { |x| x > 0 && x.even? }
|
|
197
251
|
end
|
|
198
252
|
end
|
|
253
|
+
```
|
|
199
254
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
255
|
+
Pass the custom DSL to `RSchema.schema` to use it:
|
|
256
|
+
|
|
257
|
+
```ruby
|
|
258
|
+
schema = RSchema.schema(MyCustomDSL) { positive_and_even }
|
|
259
|
+
RSchema.validate(schema, 6) #=> true
|
|
260
|
+
RSchema.validate(schema, -6) #=> false
|
|
203
261
|
```
|
|
204
262
|
|
|
205
263
|
Custom Schema Types
|
|
@@ -214,16 +272,16 @@ in an array:
|
|
|
214
272
|
class CoordinateSchema
|
|
215
273
|
def schema_walk(value, mapper)
|
|
216
274
|
# validate `value`
|
|
217
|
-
return RSchema::ErrorDetails.new('is not an Array') unless value.is_a?(Array)
|
|
218
|
-
return RSchema::ErrorDetails.new('does not have two elements') unless value.size == 2
|
|
275
|
+
return RSchema::ErrorDetails.new(value, 'is not an Array') unless value.is_a?(Array)
|
|
276
|
+
return RSchema::ErrorDetails.new(value, 'does not have two elements') unless value.size == 2
|
|
219
277
|
|
|
220
278
|
# walk the subschemas/subvalues
|
|
221
279
|
x, x_error = RSchema.walk(Float, value[0], mapper)
|
|
222
280
|
y, y_error = RSchema.walk(Float, value[1], mapper)
|
|
223
281
|
|
|
224
282
|
# look for subschema errors, and propagate them if found
|
|
225
|
-
return
|
|
226
|
-
return
|
|
283
|
+
return x_error.extend_key_path(:x) if x_error
|
|
284
|
+
return y_error.extend_key_path(:y) if y_error
|
|
227
285
|
|
|
228
286
|
# return the valid value
|
|
229
287
|
[x, y]
|
|
@@ -239,9 +297,9 @@ end
|
|
|
239
297
|
|
|
240
298
|
# use the custom schema type (coercion works too)
|
|
241
299
|
schema = RSchema.schema { coordinate }
|
|
242
|
-
RSchema.validate
|
|
243
|
-
RSchema.validate
|
|
244
|
-
RSchema.coerce!(schema, ["1", "2"])
|
|
300
|
+
RSchema.validate(schema, [1.0, 2.0]) #=> true
|
|
301
|
+
RSchema.validate(schema, [1, 2]) #=> false
|
|
302
|
+
RSchema.coerce!(schema, ["1", "2"]) #=> [1.0, 2.0]
|
|
245
303
|
```
|
|
246
304
|
|
|
247
305
|
The `schema_walk` method receives two arguments:
|
|
@@ -262,7 +320,9 @@ The `schema_walk` method has three responsibilities:
|
|
|
262
320
|
|
|
263
321
|
3. It must propagate any `RSchema::ErrorDetails` objects returned from walking
|
|
264
322
|
the subvalues. Walking subvalues with `RSchema.walk` may return an error,
|
|
265
|
-
in which case the `rschema_walk` method must also return an error.
|
|
323
|
+
in which case the `rschema_walk` method must also return an error. Use the
|
|
324
|
+
method `RSchema::ErrorDetails#extend_key_path` in this situation, to
|
|
325
|
+
include additional information in the error before returning it.
|
|
266
326
|
|
|
267
327
|
[Prismatic/schema]: https://github.com/Prismatic/schema
|
|
268
328
|
|
data/lib/rschema.rb
CHANGED
|
@@ -20,8 +20,8 @@ module RSchema
|
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
def self.schema(&block)
|
|
24
|
-
|
|
23
|
+
def self.schema(dsl=RSchema::DSL, &block)
|
|
24
|
+
dsl.instance_exec(&block)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def self.validation_error(schema, value)
|
|
@@ -69,37 +69,40 @@ module RSchema
|
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
module DSL
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
module Base
|
|
73
|
+
def _?(key)
|
|
74
|
+
OptionalHashKey.new(key)
|
|
75
|
+
end
|
|
75
76
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
def hash_of(subschemas_hash)
|
|
78
|
+
raise InvalidSchemaError unless subschemas_hash.size == 1
|
|
79
|
+
GenericHashSchema.new(subschemas_hash.keys.first, subschemas_hash.values.first)
|
|
80
|
+
end
|
|
80
81
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
def set_of(subschema)
|
|
83
|
+
GenericSetSchema.new(subschema)
|
|
84
|
+
end
|
|
84
85
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
def predicate(name = nil, &block)
|
|
87
|
+
raise InvalidSchemaError unless block
|
|
88
|
+
PredicateSchema.new(name, block)
|
|
89
|
+
end
|
|
89
90
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
def maybe(subschema)
|
|
92
|
+
raise InvalidSchemaError unless subschema
|
|
93
|
+
MaybeSchema.new(subschema)
|
|
94
|
+
end
|
|
94
95
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
def enum(possible_values, subschema = nil)
|
|
97
|
+
raise InvalidSchemaError unless possible_values && possible_values.size > 0
|
|
98
|
+
EnumSchema.new(Set.new(possible_values), subschema)
|
|
99
|
+
end
|
|
99
100
|
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
def boolean
|
|
102
|
+
BooleanSchema
|
|
103
|
+
end
|
|
102
104
|
end
|
|
105
|
+
extend Base
|
|
103
106
|
end
|
|
104
107
|
|
|
105
108
|
module CoercionMapper
|
|
@@ -222,7 +225,7 @@ module RSchema
|
|
|
222
225
|
if value_set.include?(value_walked)
|
|
223
226
|
value_walked
|
|
224
227
|
else
|
|
225
|
-
RSchema::ErrorDetails.new(value_walked, "
|
|
228
|
+
RSchema::ErrorDetails.new(value_walked, "is not a valid enum member")
|
|
226
229
|
end
|
|
227
230
|
end
|
|
228
231
|
end
|
data/lib/rschema/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rschema
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tom Dalling
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2015-02-
|
|
11
|
+
date: 2015-02-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rspec
|