schemacop 3.0.0.rc0 → 3.0.0.rc5
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/.releaser_config +0 -1
- data/.travis.yml +1 -0
- data/CHANGELOG.md +34 -1
- data/README.md +13 -3
- data/README_V3.md +717 -147
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/lib/schemacop.rb +1 -0
- data/lib/schemacop/schema2.rb +2 -2
- data/lib/schemacop/scoped_env.rb +1 -1
- data/lib/schemacop/v2.rb +0 -1
- data/lib/schemacop/v2/caster.rb +1 -0
- data/lib/schemacop/v2/node_supporting_field.rb +25 -11
- data/lib/schemacop/v2/node_supporting_type.rb +2 -2
- data/lib/schemacop/v3/array_node.rb +1 -2
- data/lib/schemacop/v3/hash_node.rb +77 -27
- data/lib/schemacop/v3/node.rb +11 -6
- data/lib/schemacop/v3/node_registry.rb +0 -4
- data/lib/schemacop/v3/reference_node.rb +7 -1
- data/lib/schemacop/v3/string_node.rb +15 -7
- data/schemacop.gemspec +7 -4
- data/test/lib/test_helper.rb +17 -2
- data/test/unit/schemacop/v2/casting_test.rb +37 -0
- data/test/unit/schemacop/v2/validator_hash_test.rb +11 -0
- data/test/unit/schemacop/v3/all_of_node_test.rb +1 -2
- data/test/unit/schemacop/v3/any_of_node_test.rb +6 -6
- data/test/unit/schemacop/v3/array_node_test.rb +13 -3
- data/test/unit/schemacop/v3/global_context_test.rb +2 -0
- data/test/unit/schemacop/v3/hash_node_test.rb +211 -14
- data/test/unit/schemacop/v3/node_test.rb +14 -0
- data/test/unit/schemacop/v3/one_of_node_test.rb +6 -6
- data/test/unit/schemacop/v3/reference_node_test.rb +76 -60
- data/test/unit/schemacop/v3/string_node_test.rb +38 -0
- metadata +16 -3
- data/lib/schemacop/v2/root_node.rb +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7fdf2a714534a94acf7c83e028fe7ce1e4b62c76266cc3e01786cd408d5c17c9
|
4
|
+
data.tar.gz: 21ba336e330bf881d4f99cb63c4070dea7559813d7387faa0a2ee53c85fdbb1e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3636130ba0c0ee471e97e3ef9e259cc86840eaaf6b6435466fcaa17dafdb17aaff406b052e7e3eeffbbbb6e0f1fe3283feb5dda4dfcd5a008d3d60ee8c01f5bf
|
7
|
+
data.tar.gz: 46fa1c3b015cfa725e9c624f74fc16f0bf07c6db64e74282e12136c17dc664ae02edb128fe3110516728fab47c2fd105068fb2d9b318f617db39f82dc6264b64
|
data/.releaser_config
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -10,7 +10,40 @@
|
|
10
10
|
### Changes
|
11
11
|
-->
|
12
12
|
|
13
|
-
## 3.0.0.
|
13
|
+
## 3.0.0.rc5 (2021-02-05)
|
14
|
+
|
15
|
+
* Use `ruby2_keywords` for compatibility with ruby `2.6.2`
|
16
|
+
|
17
|
+
## 3.0.0.rc4 (2021-02-02)
|
18
|
+
|
19
|
+
* Fix some minor bugs
|
20
|
+
|
21
|
+
* Improve documentation
|
22
|
+
|
23
|
+
* `used_external_schemas` for the `ReferenceNode` is now applied
|
24
|
+
recursively
|
25
|
+
|
26
|
+
## 3.0.0.rc3 (2021-01-28)
|
27
|
+
|
28
|
+
* Add minor improvements to the documentation
|
29
|
+
|
30
|
+
* Internal restructuring, no changes in API
|
31
|
+
|
32
|
+
## 3.0.0.rc2 (2021-01-28)
|
33
|
+
|
34
|
+
* Represent node names as strings internally
|
35
|
+
|
36
|
+
* Update documentation
|
37
|
+
|
38
|
+
## 3.0.0.rc1 (2021-01-22)
|
39
|
+
|
40
|
+
* Add support for ruby `3.0.0`
|
41
|
+
|
42
|
+
* Add `ruby-3.0.0` to travis testing
|
43
|
+
|
44
|
+
* Document all `v3` nodes
|
45
|
+
|
46
|
+
## 3.0.0.rc0 (2021-01-14)
|
14
47
|
|
15
48
|
* Add `Schemacop::Schema3`
|
16
49
|
|
data/README.md
CHANGED
@@ -8,6 +8,16 @@ against schema definitions described by a simple DSL. It is also able to
|
|
8
8
|
generate [JSON Schema](https://json-schema.org) compliant JSON output, i.e. for
|
9
9
|
use in conjunction with [OpenAPI](https://swagger.io/specification/).
|
10
10
|
|
11
|
+
## Compatibility
|
12
|
+
|
13
|
+
Schemacop is tested with the following ruby versions:
|
14
|
+
|
15
|
+
* 2.6.2
|
16
|
+
* 2.7.1
|
17
|
+
* 3.0.0
|
18
|
+
|
19
|
+
For these versions, the automated CI tests are ran on travis. Other ruby versions might work, but stick to these versions for best results.
|
20
|
+
|
11
21
|
## Basic example
|
12
22
|
|
13
23
|
```ruby
|
@@ -84,11 +94,11 @@ actual JSON string.
|
|
84
94
|
|
85
95
|
Schemacop will throw one of the following checked exceptions:
|
86
96
|
|
87
|
-
*
|
97
|
+
* `Schemacop::Exceptions::InvalidSchemaError`
|
88
98
|
|
89
99
|
This exception is thrown when the given schema definition format is invalid.
|
90
100
|
|
91
|
-
*
|
101
|
+
* `Schemacop::Exceptions::ValidationError`
|
92
102
|
|
93
103
|
This exception is thrown when the given data does not comply with the given
|
94
104
|
schema definition.
|
@@ -108,4 +118,4 @@ To run tests:
|
|
108
118
|
|
109
119
|
## Copyright
|
110
120
|
|
111
|
-
Copyright
|
121
|
+
Copyright © 2016 - 2021 Sitrox. See `LICENSE` for further details.
|
data/README_V3.md
CHANGED
@@ -1,15 +1,11 @@
|
|
1
1
|
# Schemacop schema V3
|
2
2
|
|
3
|
-
|
3
|
+
## Table of Contents
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
2. [Validation](#validation)
|
10
|
-
3. [Exceptions](#exceptions)
|
11
|
-
4. [Generic Keywords](#generic-keywords)
|
12
|
-
5. [Nodes](#nodes)
|
5
|
+
1. [Validation](#validation)
|
6
|
+
2. [Exceptions](#exceptions)
|
7
|
+
3. [Generic Keywords](#generic-keywords)
|
8
|
+
4. [Nodes](#nodes)
|
13
9
|
1. [String](#string)
|
14
10
|
2. [Integer](#integer)
|
15
11
|
3. [Number](#number)
|
@@ -23,16 +19,12 @@ Use at your own discretion.
|
|
23
19
|
11. [OneOf](#oneOf)
|
24
20
|
12. [IsNot](#isNot)
|
25
21
|
13. [Reference](#reference)
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
## Introcution
|
30
|
-
|
31
|
-
TODO: Write short section about using schemacop V3
|
22
|
+
5. [Context](#context)
|
23
|
+
6. [External schemas](#external-schemas)
|
32
24
|
|
33
25
|
## Validation
|
34
26
|
|
35
|
-
Using
|
27
|
+
Using Schemacop, you can either choose to validate your data either using the
|
36
28
|
graceful `validate` method, or the bang variant, `validate!`.
|
37
29
|
|
38
30
|
The `validate` method on a schema with some supplied data will return a
|
@@ -74,19 +66,120 @@ schema.validate!('Foo') # => Schemacop::Exceptions::ValidationError: /:
|
|
74
66
|
|
75
67
|
## Exceptions
|
76
68
|
|
77
|
-
|
69
|
+
Schemacop can raise the following exceptions:
|
70
|
+
|
71
|
+
* `Schemacop::Exceptions::ValidationError`: This exception is raised when the
|
72
|
+
`validate!` method is used, and the data that was passed in is invalid. The
|
73
|
+
exception message contains additional information why the validation failed.
|
74
|
+
|
75
|
+
Example:
|
78
76
|
|
79
|
-
|
80
|
-
|
77
|
+
```ruby
|
78
|
+
schema = Schemacop::Schema3.new do
|
79
|
+
int! :foo
|
80
|
+
end
|
81
|
+
|
82
|
+
schema.validate!(foo: 'bar')
|
83
|
+
# => Schemacop::Exceptions::ValidationError: /foo: Invalid type, expected "integer".
|
84
|
+
```
|
85
|
+
|
86
|
+
* `Schemacop::Exceptions::InvalidSchemaError`: This exception is raised when the
|
87
|
+
schema itself is not valid. The exception message contains additional
|
88
|
+
information why the validation failed.
|
89
|
+
|
90
|
+
Example:
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
Schemacop::Schema3.new :hash do
|
94
|
+
int!
|
95
|
+
end
|
96
|
+
|
97
|
+
# => Schemacop::Exceptions::InvalidSchemaError: Child nodes must have a name.
|
98
|
+
```
|
81
99
|
|
82
100
|
## Generic Keywords
|
83
101
|
|
84
|
-
|
102
|
+
The nodes in Schemacop v3 also support generic keywords, similar to JSON schema:
|
103
|
+
|
104
|
+
* `title`: Short string, should be self-explanatory
|
105
|
+
* `description`: Description of the schema
|
106
|
+
* `examples`: Here, you can provide examples which will be valid for the schema
|
107
|
+
* `enum`: Here, you may enumerate values which will be valid, if the provided
|
108
|
+
value is not in the array, the validation will fail
|
109
|
+
* `default`: You may provide a default value for items that will be set if the
|
110
|
+
value is not given
|
111
|
+
|
112
|
+
The three keywords `title`, `description` and `examples` aren't used for validation,
|
113
|
+
but can be used to document the schema. They will be included in the JSON output
|
114
|
+
when you use the `as_json` method:
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
schema = Schemacop::Schema3.new :hash do
|
118
|
+
str! :name, title: 'Name', description: 'Holds the name of the user', examples: ['Joe', 'Anna']
|
119
|
+
end
|
120
|
+
|
121
|
+
schema.as_json
|
122
|
+
|
123
|
+
# => {"properties"=>{"name"=>{"type"=>"string", "title"=>"Name", "examples"=>["Joe", "Anna"], "description"=>"Holds the name of the user"}}, "additionalProperties"=>false, "required"=>["name"], "type"=>"object"}
|
124
|
+
```
|
125
|
+
|
126
|
+
The `enum` keyword can be used to only allow a subset of values:
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
schema = Schemacop::Schema3.new :string, enum: ['foo', 'bar']
|
130
|
+
|
131
|
+
schema.validate!('foo') # => "foo"
|
132
|
+
schema.validate!('bar') # => "bar"
|
133
|
+
schema.validate!('baz') # => Schemacop::Exceptions::ValidationError: /: Value not included in enum ["foo", "bar"].
|
134
|
+
```
|
135
|
+
|
136
|
+
Please note that you can also specify values in the enum that are not valid for
|
137
|
+
the schema. This means that the validation will still fail:
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
schema = Schemacop::Schema3.new :string, enum: ['foo', 'bar', 42]
|
141
|
+
|
142
|
+
schema.validate!('foo') # => "foo"
|
143
|
+
schema.validate!('bar') # => "bar"
|
144
|
+
schema.validate!(42) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "string".
|
145
|
+
```
|
146
|
+
|
147
|
+
The enum will also be provided in the json output:
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
schema = Schemacop::Schema3.new :string, enum: ['foo', 'bar']
|
151
|
+
|
152
|
+
schema.as_json
|
153
|
+
# => {"type"=>"string", "enum"=>["foo", "bar", 42]}
|
154
|
+
```
|
155
|
+
|
156
|
+
And finally, the `default` keyword lets you set a default value to use when no
|
157
|
+
value is provided:
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
schema = Schemacop::Schema3.new :string, default: 'Schemacop'
|
161
|
+
|
162
|
+
schema.validate!('foo') # => "foo"
|
163
|
+
schema.validate!(nil) # => "Schemacop"
|
164
|
+
```
|
165
|
+
|
166
|
+
The default value will also be provided in the json output:
|
167
|
+
|
168
|
+
```ruby
|
169
|
+
schema = Schemacop::Schema3.new :string, default: 'Schemacop'
|
170
|
+
|
171
|
+
schema.as_json
|
172
|
+
# => {"type"=>"string", "default"=>"Schemacop"}
|
173
|
+
```
|
85
174
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
175
|
+
Note that the default value you use is also validated against the schema:
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
schema = Schemacop::Schema3.new :string, default: 42
|
179
|
+
|
180
|
+
schema.validate!('foo') # => "foo"
|
181
|
+
schema.validate!(nil) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "string".
|
182
|
+
```
|
90
183
|
|
91
184
|
## Nodes
|
92
185
|
|
@@ -102,9 +195,9 @@ transformed into various types.
|
|
102
195
|
#### Options
|
103
196
|
|
104
197
|
* `min_length`
|
105
|
-
Defines the minimum required string length
|
198
|
+
Defines the (inclusive) minimum required string length
|
106
199
|
* `max_length`
|
107
|
-
Defines the maximum required string length
|
200
|
+
Defines the (inclusive) maximum required string length
|
108
201
|
* `pattern`
|
109
202
|
Defines a (ruby) regex pattern the value will be matched against. Must be a
|
110
203
|
string and should generally start with `^` and end with `$` so as to evaluate
|
@@ -136,7 +229,7 @@ transformed into various types.
|
|
136
229
|
|
137
230
|
* `boolean`
|
138
231
|
The string must be either `true` or `false`. This value will be casted to
|
139
|
-
|
232
|
+
Ruby's `TrueClass` or `FalseClass`.
|
140
233
|
|
141
234
|
* `binary`
|
142
235
|
The string is expected to contain binary contents. No casting or additional
|
@@ -287,7 +380,7 @@ schema.validate!(1234) # => Schemacop::Exceptions::ValidationError: /: Invali
|
|
287
380
|
### Array
|
288
381
|
|
289
382
|
Type: `:array`\
|
290
|
-
DSL: `
|
383
|
+
DSL: `ary`
|
291
384
|
|
292
385
|
The array type represents a ruby `Array`.
|
293
386
|
It consists of one or multiple values, which can be validated using arbitrary nodes.
|
@@ -307,6 +400,42 @@ It consists of one or multiple values, which can be validated using arbitrary no
|
|
307
400
|
each other, or if there may be duplicate values. By default, this is false,
|
308
401
|
i.e. duplicate values are allowed
|
309
402
|
|
403
|
+
#### Contains
|
404
|
+
|
405
|
+
The `array` node features the *contains* node, which you can use with the DSL
|
406
|
+
method `cont`. With that DSL method, you can specify a schema which at least one
|
407
|
+
item in the array needs to validate against.
|
408
|
+
|
409
|
+
One use case for example could be that you want an array of integers, from which
|
410
|
+
at least one must be 5 or larger:
|
411
|
+
|
412
|
+
```ruby
|
413
|
+
schema = Schemacop::Schema3.new :array do
|
414
|
+
list :integer
|
415
|
+
cont :integer, minimum: 5
|
416
|
+
end
|
417
|
+
|
418
|
+
schema.validate!([]) # => Schemacop::Exceptions::ValidationError: /: At least one entry must match schema {"type"=>"integer", "minimum"=>5}.
|
419
|
+
schema.validate!([1, 5]) # => [1, 5]
|
420
|
+
schema.validate!(['foo']) # => Schemacop::Exceptions::ValidationError: /[0]: Invalid type, expected "integer". /: At least one entry must match schema {"type"=>"integer", "minimum"=>5}
|
421
|
+
```
|
422
|
+
|
423
|
+
You can also use it with the tuple validation (see below), e.g. if you want
|
424
|
+
an array of 3 integers, from which at least one needs to be 5 or larger:
|
425
|
+
|
426
|
+
```ruby
|
427
|
+
schema = Schemacop::Schema3.new :array do
|
428
|
+
int
|
429
|
+
int
|
430
|
+
int
|
431
|
+
cont :integer, minimum: 5
|
432
|
+
end
|
433
|
+
|
434
|
+
schema.validate!([]) # => /: Array has 0 items but must have exactly 3. /: At least one entry must match schema {"type"=>"integer", "minimum"=>5}.
|
435
|
+
schema.validate!([1, 2, 3]) # => Schemacop::Exceptions::ValidationError: /: At least one entry must match schema {"type"=>"integer", "minimum"=>5}.
|
436
|
+
schema.validate!([1, 3, 5]) # => [1, 3, 5]
|
437
|
+
```
|
438
|
+
|
310
439
|
#### Specifying properties
|
311
440
|
|
312
441
|
Array nodes support a block in which you can specify the required array contents.
|
@@ -317,9 +446,10 @@ how you specify your array contents.
|
|
317
446
|
|
318
447
|
List validation validates a sequence of arbitrary length where each item matches
|
319
448
|
the same schema. Unless you specify a `min_items` count on the array node, an
|
320
|
-
empty array will also
|
321
|
-
|
322
|
-
|
449
|
+
empty array will also suffice. To specify a list validation, use the `list` DSL
|
450
|
+
method, and specify the type you want to validate against. Here, you need to
|
451
|
+
specify the type of the element using the long `type` name (e.g. `integer` and
|
452
|
+
not `int`).
|
323
453
|
|
324
454
|
For example, you can specify that you want an array with only integers between 1 and 5:
|
325
455
|
|
@@ -412,8 +542,8 @@ schema.validate!([1, 'foo', 'bar']) # => Schemacop::Exceptions::ValidationError:
|
|
412
542
|
schema.validate!([1, 'foo', 2, 3]) # => [1, "foo", 2, 3]
|
413
543
|
```
|
414
544
|
|
415
|
-
Please note
|
416
|
-
an exception:
|
545
|
+
Please note that you cannot use multiple `add` in the same array schema, this
|
546
|
+
will result in an exception:
|
417
547
|
|
418
548
|
```ruby
|
419
549
|
schema = Schemacop::Schema3.new :array do
|
@@ -425,9 +555,10 @@ end
|
|
425
555
|
# => Schemacop::Exceptions::InvalidSchemaError: You can only use "add" once to specify additional items.
|
426
556
|
```
|
427
557
|
|
428
|
-
If you want to specify that your schema accept multiple additional types, use
|
429
|
-
type (see below for more infos). The correct way to specify that
|
430
|
-
items, which may be an integer or a string is as
|
558
|
+
If you want to specify that your schema accept multiple additional types, use
|
559
|
+
the `one_of` type (see below for more infos). The correct way to specify that
|
560
|
+
you want to allow additional items, which may be an integer or a string is as
|
561
|
+
follows:
|
431
562
|
|
432
563
|
```ruby
|
433
564
|
schema = Schemacop::Schema3.new :array do
|
@@ -437,11 +568,12 @@ schema = Schemacop::Schema3.new :array do
|
|
437
568
|
str
|
438
569
|
end
|
439
570
|
end
|
440
|
-
```
|
441
571
|
|
442
|
-
|
443
|
-
|
444
|
-
|
572
|
+
schema.validate!([]) # => Schemacop::Exceptions::ValidationError: /: Array has 0 items but must have exactly 1.
|
573
|
+
schema.validate!([1, 2]) # => [1, 2]
|
574
|
+
schema.validate!([1, 'foo']) # => [1, "foo"]
|
575
|
+
schema.validate!([1, :bar]) # => Schemacop::Exceptions::ValidationError: /[1]: Matches 0 definitions but should match exactly 1.
|
576
|
+
```
|
445
577
|
|
446
578
|
### Hash
|
447
579
|
|
@@ -453,15 +585,16 @@ It consists of key-value-pairs that can be validated using arbitrary nodes.
|
|
453
585
|
|
454
586
|
#### Options
|
455
587
|
|
456
|
-
* `additional_properties`
|
588
|
+
* `additional_properties`
|
457
589
|
This option specifies whether additional, unspecified properties are allowed
|
458
|
-
(`true`) or not (`false`). By default, this is `
|
459
|
-
|
590
|
+
(`true`) or not (`false`). By default, this is `false`, i.e. you need to
|
591
|
+
explicitly set it to `true` if you want to allow arbitrary additional properties,
|
592
|
+
or use the `add` DSL method (see below) to specify additional properties.
|
460
593
|
|
461
594
|
* `property_names`
|
462
595
|
This option allows to specify a regexp pattern (as string) which validates the
|
463
596
|
keys of any properties that are not specified in the hash. This option only
|
464
|
-
makes sense if `additional_properties` is enabled.
|
597
|
+
makes sense if `additional_properties` is enabled. See below for more information.
|
465
598
|
|
466
599
|
* `min_properties`
|
467
600
|
Specifies the (inclusive) minimum number of properties a hash must contain.
|
@@ -480,28 +613,140 @@ property, which specifies whether a property is required (`!`) or optional
|
|
480
613
|
(`?`).
|
481
614
|
|
482
615
|
```ruby
|
483
|
-
|
484
|
-
|
616
|
+
schema = Schemacop::Schema3.new :hash do
|
617
|
+
str! :foo # Is a required property
|
618
|
+
int? :bar # Is an optional property
|
619
|
+
end
|
620
|
+
|
621
|
+
schema.validate!({}) # => Schemacop::Exceptions::ValidationError: /foo: Value must be given.
|
622
|
+
schema.validate!({foo: 'str'}) # => {"foo"=>"str"}
|
623
|
+
schema.validate!({foo: 'str', bar: 42}) # => {"foo"=>"str", "bar"=>42}
|
624
|
+
schema.validate!({bar: 42}) # => Schemacop::Exceptions::ValidationError: /foo: Value must be given.
|
625
|
+
```
|
626
|
+
|
627
|
+
The name of the properties may either be a string or a symbol, and you can pass
|
628
|
+
in the property either identified by a symbol or a string:
|
629
|
+
|
630
|
+
The following two schemas are equal:
|
631
|
+
|
632
|
+
```ruby
|
633
|
+
schema = Schemacop::Schema3.new :hash do
|
634
|
+
int! :foo
|
635
|
+
end
|
636
|
+
|
637
|
+
schema.validate!(foo: 42) # => {"foo"=>42}
|
638
|
+
schema.validate!('foo' => 42) # => {"foo"=>42}
|
639
|
+
|
640
|
+
schema = Schemacop::Schema3.new :hash do
|
641
|
+
int! 'foo'
|
642
|
+
end
|
643
|
+
|
644
|
+
schema.validate!(foo: 42) # => {"foo"=>42}
|
645
|
+
schema.validate!('foo' => 42) # => {"foo"=>42}
|
485
646
|
```
|
486
647
|
|
487
|
-
|
648
|
+
The result in both cases will be a
|
649
|
+
[HashWithIndifferentAccess](https://api.rubyonrails.org/classes/ActiveSupport/HashWithIndifferentAccess.html),
|
650
|
+
which means that you can access the data in the hash with the symbol as well as
|
651
|
+
the string representation:
|
488
652
|
|
489
|
-
|
653
|
+
```ruby
|
654
|
+
schema = Schemacop::Schema3.new :hash do
|
655
|
+
int! :foo
|
656
|
+
end
|
657
|
+
|
658
|
+
result = schema.validate!(foo: 42)
|
659
|
+
|
660
|
+
result.class # => ActiveSupport::HashWithIndifferentAccess
|
661
|
+
result[:foo] # => 42
|
662
|
+
result['foo'] # 42
|
663
|
+
```
|
664
|
+
|
665
|
+
Please note that if you specify the value twice in the data you want to
|
666
|
+
validate, once with the key being a symbol and once being a string, Schemacop
|
667
|
+
will raise an error:
|
490
668
|
|
491
669
|
```ruby
|
492
|
-
Schemacop::Schema3.new do
|
493
|
-
|
670
|
+
schema = Schemacop::Schema3.new :hash do
|
671
|
+
int! :foo
|
672
|
+
end
|
494
673
|
|
495
|
-
|
496
|
-
|
497
|
-
|
674
|
+
schema.validate!(foo: 42, 'foo' => 43) # => Schemacop::Exceptions::ValidationError: /: Has 1 ambiguous properties: [:foo].
|
675
|
+
```
|
676
|
+
|
677
|
+
In addition to the normal node options (which vary from type to type, check
|
678
|
+
the respective nodes for details), properties also support the `as` option.
|
679
|
+
|
680
|
+
With this, you can "rename" properties in the output:
|
681
|
+
|
682
|
+
```ruby
|
683
|
+
schema = Schemacop::Schema3.new :hash do
|
684
|
+
int! :foo, as: :bar
|
685
|
+
end
|
686
|
+
|
687
|
+
schema.validate!({foo: 42}) # => {"bar"=>42}
|
688
|
+
```
|
689
|
+
|
690
|
+
Please note that if you specify a node with the same property name multiple
|
691
|
+
times, or use the `as` option to rename a node to the same name of another
|
692
|
+
node, the last specified node will be used:
|
693
|
+
|
694
|
+
```ruby
|
695
|
+
schema = Schemacop::Schema3.new :hash do
|
696
|
+
int? :foo
|
697
|
+
str? :foo
|
698
|
+
end
|
699
|
+
|
700
|
+
schema.validate!({foo: 1}) # => Schemacop::Exceptions::ValidationError: /foo: Invalid type, expected "string".
|
701
|
+
schema.validate!({foo: 'bar'}) # => {"foo"=>"bar"}
|
702
|
+
```
|
703
|
+
|
704
|
+
As well as:
|
705
|
+
|
706
|
+
```ruby
|
707
|
+
schema = Schemacop::Schema3.new :hash do
|
708
|
+
int? :foo
|
709
|
+
int? :bar, as: :foo
|
498
710
|
end
|
711
|
+
|
712
|
+
schema.validate!({foo: 1}) # => {"foo"=>1}
|
713
|
+
schema.validate!({foo: 1, bar: 2}) # => {"foo"=>2}
|
714
|
+
schema.validate!({bar: 2}) # => {"foo"=>2}
|
499
715
|
```
|
500
716
|
|
501
|
-
|
717
|
+
If you want to specify a node which may be one of multiple types, use the `one_of`
|
718
|
+
node (see further down for more details):
|
502
719
|
|
503
720
|
```ruby
|
504
|
-
|
721
|
+
schema = Schemacop::Schema3.new :hash do
|
722
|
+
one_of! :foo do
|
723
|
+
int
|
724
|
+
str
|
725
|
+
end
|
726
|
+
end
|
727
|
+
|
728
|
+
schema.validate!({foo: 1}) # => {"foo"=>1}
|
729
|
+
schema.validate!({foo: 'bar'}) # => {"foo"=>"bar"}
|
730
|
+
```
|
731
|
+
|
732
|
+
##### Pattern properties
|
733
|
+
|
734
|
+
In addition to symbols, property keys can also be a regular expression. Here,
|
735
|
+
you may only use the optional `?` suffix for the property. This allows any
|
736
|
+
property, which matches the type and the name of the property matches the
|
737
|
+
regular expression.
|
738
|
+
|
739
|
+
```ruby
|
740
|
+
schema = Schemacop::Schema3.new :hash do
|
741
|
+
# The following statement allows any number of integer properties of which the
|
742
|
+
# name starts with `id_`.
|
743
|
+
int? /^id_.*$/
|
744
|
+
end
|
745
|
+
|
746
|
+
schema.validate!({}) # => {}
|
747
|
+
schema.validate!({id_foo: 1}) # => {"id_foo"=>1}
|
748
|
+
schema.validate!({id_foo: 1, id_bar: 2}) # => {"id_foo"=>1, "id_bar"=>2}
|
749
|
+
schema.validate!({foo: 3}) # => Schemacop::Exceptions::ValidationError: /: Obsolete property "foo".
|
505
750
|
```
|
506
751
|
|
507
752
|
##### Additional properties & property names
|
@@ -511,19 +756,32 @@ additional, unspecified properties. By default, this is turned off if you have
|
|
511
756
|
defined at least one standard property.
|
512
757
|
|
513
758
|
When it comes to additional properties, you have the choice to either just
|
514
|
-
enable all of them by enabling the option `additional_properties
|
515
|
-
|
516
|
-
|
759
|
+
enable all of them by enabling the option `additional_properties`:
|
760
|
+
|
761
|
+
```ruby
|
762
|
+
# This schema will accept any additional properties
|
763
|
+
schema = Schemacop::Schema3.new :hash, additional_properties: true
|
764
|
+
|
765
|
+
schema.validate!({}) # => {}
|
766
|
+
schema.validate!({foo: :bar, baz: 42}) # => {"foo"=>:bar, "baz"=>42}
|
767
|
+
```
|
768
|
+
|
769
|
+
Using the DSL method `add` in the hash-node's body however, you can specify
|
770
|
+
an additional schema to which additional properties must adhere:
|
771
|
+
|
517
772
|
|
518
773
|
```ruby
|
519
|
-
Schemacop::Schema3.new do
|
774
|
+
Schemacop::Schema3.new :hash do
|
520
775
|
int! :id
|
521
776
|
|
522
777
|
# Allow any additional properties besides `id`, but their value must be a
|
523
|
-
# string.
|
524
|
-
|
525
|
-
add :str
|
778
|
+
# string.
|
779
|
+
add :string
|
526
780
|
end
|
781
|
+
|
782
|
+
schema.validate!({id: 1}) # => {"id"=>1}
|
783
|
+
schema.validate!({id: 1, foo: 'bar'}) # => {"id"=>1, "foo"=>"bar"}
|
784
|
+
schema.validate!({id: 1, foo: 42}) # => Schemacop::Exceptions::ValidationError: /foo: Invalid type, expected "string".
|
527
785
|
```
|
528
786
|
|
529
787
|
Using the option `property_names`, you can additionaly specify a pattern that
|
@@ -532,13 +790,23 @@ any additional property **keys** must adhere to:
|
|
532
790
|
```ruby
|
533
791
|
# The following schema allows any number of properties, but all keys must
|
534
792
|
# consist of downcase letters from a-z.
|
535
|
-
Schemacop::Schema3.new additional_properties:
|
793
|
+
schema = Schemacop::Schema3.new :hash, additional_properties: true, property_names: '^[a-z]+$'
|
794
|
+
|
795
|
+
|
796
|
+
schema.validate!({}) # => {}
|
797
|
+
schema.validate!({foo: 123}) # => {"foo"=>123}
|
798
|
+
schema.validate!({Foo: 'bar'}) # => Schemacop::Exceptions::ValidationError: /: Property name "Foo" does not match "^[a-z]+$".
|
536
799
|
|
537
800
|
# The following schema allows any number of properties, but all keys must
|
538
801
|
# consist of downcase letters from a-z AND the properties must be arrays.
|
539
|
-
Schemacop::Schema3.new additional_properties:
|
802
|
+
schema = Schemacop::Schema3.new :hash, additional_properties: true, property_names: '^[a-z]+$' do
|
540
803
|
add :array
|
541
804
|
end
|
805
|
+
|
806
|
+
schema.validate!({}) # => {}
|
807
|
+
schema.validate!({foo: [1, 2, 3]}) # => {"foo"=>[1, 2, 3]}
|
808
|
+
schema.validate!({foo: :bar}) # => Schemacop::Exceptions::ValidationError: /foo: Invalid type, expected "array".
|
809
|
+
schema.validate!({Foo: :bar}) # => Schemacop::Exceptions::ValidationError: /: Property name :Foo does not match "^[a-z]+$". /Foo: Invalid type, expected "array".
|
542
810
|
```
|
543
811
|
|
544
812
|
##### Dependencies
|
@@ -549,7 +817,7 @@ Using the DSL method `dep`, you can specifiy (non-nested) property dependencies:
|
|
549
817
|
# In this example, `billing_address` and `phone_number` are required if
|
550
818
|
# `credit_card` is given, and `credit_card` is required if `billing_address` is
|
551
819
|
# given.
|
552
|
-
Schemacop::Schema3.new do
|
820
|
+
schema = Schemacop::Schema3.new :hash do
|
553
821
|
str! :name
|
554
822
|
str? :credit_card
|
555
823
|
str? :billing_address
|
@@ -558,126 +826,428 @@ Schemacop::Schema3.new do
|
|
558
826
|
dep :credit_card, :billing_address, :phone_number
|
559
827
|
dep :billing_address, :credit_card
|
560
828
|
end
|
829
|
+
|
830
|
+
schema.validate!({}) # => Schemacop::Exceptions::ValidationError: /name: Value must be given.
|
831
|
+
schema.validate!({name: 'Joe Doe'}) # => {"name"=>"Joe Doe"}
|
832
|
+
schema.validate!({
|
833
|
+
name: 'Joe Doe',
|
834
|
+
billing_address: 'Street 42'
|
835
|
+
})
|
836
|
+
# => Schemacop::Exceptions::ValidationError: /: Missing property "credit_card" because "billing_address" is given.
|
837
|
+
|
838
|
+
schema.validate!({
|
839
|
+
name: 'Joe Doe',
|
840
|
+
credit_card: 'XXXX XXXX XXXX XXXX X'
|
841
|
+
})
|
842
|
+
# => Schemacop::Exceptions::ValidationError: /: Missing property "billing_address" because "credit_card" is given. /: Missing property "phone_number" because "credit_card" is given.
|
843
|
+
|
844
|
+
schema.validate!({
|
845
|
+
name: 'Joe Doe',
|
846
|
+
billing_address: 'Street 42',
|
847
|
+
phone_number: '000-000-00-00',
|
848
|
+
credit_card: 'XXXX XXXX XXXX XXXX X'
|
849
|
+
})
|
850
|
+
# => {"name"=>"Joe Doe", "credit_card"=>"XXXX XXXX XXXX XXXX X", "billing_address"=>"Street 42", "phone_number"=>"000-000-00-00"}
|
561
851
|
```
|
562
852
|
|
563
|
-
|
564
|
-
```ruby
|
565
|
-
schema = Schemacop::Schema3.new do
|
566
|
-
# Define built-in schema 'address' for re-use
|
567
|
-
scm :address do
|
568
|
-
str! :street
|
569
|
-
int! :number
|
570
|
-
str! :zip
|
571
|
-
end
|
853
|
+
### Object
|
572
854
|
|
573
|
-
|
574
|
-
|
855
|
+
Type: `:object`\
|
856
|
+
DSL: `obj`
|
575
857
|
|
576
|
-
|
577
|
-
|
858
|
+
The object type represents a Ruby `Object`. Please note that the `as_json`
|
859
|
+
method on nodes of this type will just return `{}` (an empty JSON object), as
|
860
|
+
there isn't a useful way to represent a Ruby object without conflicting with the
|
861
|
+
`Hash` type. If you want to represent a JSON object, you should use the `Hash`
|
862
|
+
node.
|
578
863
|
|
579
|
-
|
580
|
-
# in key `additional_addresses`
|
581
|
-
ary! :additional_addresses, default: [] do
|
582
|
-
ref :address
|
583
|
-
end
|
584
|
-
ary? :comments, :array, default: [] { str }
|
864
|
+
In the most basic form, this node will accept anything:
|
585
865
|
|
586
|
-
|
587
|
-
|
588
|
-
hsh! :jobs, min_properties: 1 do
|
589
|
-
str? /^[0-9]+$/
|
590
|
-
end
|
591
|
-
end
|
866
|
+
```ruby
|
867
|
+
schema = Schemacop::Schema3.new :object
|
592
868
|
|
593
|
-
schema.
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
number: 4,
|
599
|
-
zip: '38234C'
|
600
|
-
},
|
601
|
-
additional_addresses: [
|
602
|
-
{ street: 'Example street', number: 42, zip: '8048' }
|
603
|
-
],
|
604
|
-
comments: [
|
605
|
-
'This is a comment'
|
606
|
-
],
|
607
|
-
jobs: {
|
608
|
-
2020 => 'Software Engineer'
|
609
|
-
}
|
610
|
-
) # => true
|
869
|
+
schema.validate!(nil) # => nil
|
870
|
+
schema.validate!(true) # => true
|
871
|
+
schema.validate!(false) # => false
|
872
|
+
schema.validate!(Object.new) # => #<Object:0x0000556ab4f58dd0>
|
873
|
+
schema.validate!('foo') # => "foo"
|
611
874
|
```
|
612
875
|
|
876
|
+
If you want to limit the allowed classes, you can so so by specifying an array
|
877
|
+
of allowed classes:
|
878
|
+
|
613
879
|
```ruby
|
614
|
-
|
615
|
-
# being a nested hash.
|
616
|
-
Schemacop::Schema3.new do
|
617
|
-
int? :id # Optional integer with key 'id'
|
618
|
-
str! :name # Required string with name 'name'
|
619
|
-
hsh! :options do # Required hash with name `options`
|
620
|
-
boo! :enabled # Required boolean with name `enabled`
|
621
|
-
end
|
622
|
-
end
|
880
|
+
schema = Schemacop::Schema3.new :object, classes: [String]
|
623
881
|
|
624
|
-
#
|
625
|
-
Schemacop::
|
882
|
+
schema.validate!(nil) # => nil
|
883
|
+
schema.validate!(true) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "String".
|
884
|
+
schema.validate!(Object.new) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "String".
|
885
|
+
schema.validate!('foo') # => "foo"
|
886
|
+
schema.validate!('foo'.html_safe) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "String".
|
887
|
+
```
|
626
888
|
|
627
|
-
|
628
|
-
|
629
|
-
Schemacop::Schema3.new(additional_properties: true) do
|
630
|
-
int! :id
|
631
|
-
end
|
889
|
+
Here, the node checks if the given value is an instance of any of the given
|
890
|
+
classes with `instance_of?`, i.e. the exact class and not a subclass.
|
632
891
|
|
633
|
-
|
634
|
-
# key starts with `k_` and of any value type are allowed.
|
635
|
-
Schemacop::Schema3.new(additional_properties: true, property_names: '^k_.*$') do
|
636
|
-
int! :id
|
637
|
-
end
|
892
|
+
If you want to allow subclasses, you can specify this by using the `strict` option:
|
638
893
|
|
639
|
-
|
640
|
-
|
641
|
-
Schemacop::Schema3.new(additional_properties: true, property_names: '^k_.*$') do
|
642
|
-
int! :id
|
643
|
-
add :string
|
644
|
-
end
|
894
|
+
```ruby
|
895
|
+
schema = Schemacop::Schema3.new :object, classes: [String], strict: false
|
645
896
|
|
646
|
-
#
|
647
|
-
#
|
648
|
-
#
|
649
|
-
|
650
|
-
|
651
|
-
str! /^k_.*$/
|
652
|
-
end
|
897
|
+
schema.validate!(nil) # => nil
|
898
|
+
schema.validate!(true) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "String".
|
899
|
+
schema.validate!(Object.new) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "String".
|
900
|
+
schema.validate!('foo') # => "foo"
|
901
|
+
schema.validate!('foo'.html_safe) # => "foo"
|
653
902
|
```
|
654
903
|
|
655
|
-
|
656
|
-
|
657
|
-
Type: `:object`\
|
658
|
-
DSL: `obj`
|
904
|
+
If you set the `strict` option to `false`, the check is done using `is_a?` instead of
|
905
|
+
`instance_of?`, which also allows subclasses
|
659
906
|
|
660
907
|
### AllOf
|
661
908
|
|
662
909
|
Type: `:all_of`\
|
663
910
|
DSL: `all_of`
|
664
911
|
|
912
|
+
With the AllOf node you can specify multiple schemas, for which the given value
|
913
|
+
needs to validate against every one:
|
914
|
+
|
915
|
+
```ruby
|
916
|
+
schema = Schemacop::Schema3.new :all_of do
|
917
|
+
str min_length: 2
|
918
|
+
str max_length: 4
|
919
|
+
end
|
920
|
+
|
921
|
+
schema.validate!('foo') # => "foo"
|
922
|
+
schema.validate!('foooo') # => Schemacop::Exceptions::ValidationError: /: Does not match all allOf conditions.
|
923
|
+
```
|
924
|
+
|
925
|
+
Please note that it's possible to create nonsensical schemas with this node, as
|
926
|
+
you can combine multiple schemas which contradict each other:
|
927
|
+
|
928
|
+
```ruby
|
929
|
+
schema = Schemacop::Schema3.new :all_of do
|
930
|
+
str min_length: 4
|
931
|
+
str max_length: 1
|
932
|
+
end
|
933
|
+
|
934
|
+
schema.validate!('foo') # => Schemacop::Exceptions::ValidationError: /: Does not match all allOf conditions.
|
935
|
+
schema.validate!('foooo') # => Schemacop::Exceptions::ValidationError: /: Does not match all allOf conditions.
|
936
|
+
```
|
937
|
+
|
665
938
|
### AnyOf
|
666
939
|
|
667
940
|
Type: `:any_of`\
|
668
941
|
DSL: `any_of`
|
669
942
|
|
943
|
+
Similar to the `all_of` node, you can specify multiple schemas, for which the
|
944
|
+
given value needs to validate against at least one of the schemas.
|
945
|
+
|
946
|
+
For example, your value needs to be either a string which is at least 2
|
947
|
+
characters long, or an integer:
|
948
|
+
|
949
|
+
```ruby
|
950
|
+
schema = Schemacop::Schema3.new :any_of do
|
951
|
+
str min_length: 2
|
952
|
+
int
|
953
|
+
end
|
954
|
+
|
955
|
+
schema.validate!('f') # => Schemacop::Exceptions::ValidationError: /: Does not match any anyOf condition.
|
956
|
+
schema.validate!('foo') # => "foo"
|
957
|
+
schema.validate!(42) # => 42
|
958
|
+
```
|
959
|
+
|
960
|
+
Please note that you need to specify at least one item in the `any_of` node:
|
961
|
+
|
962
|
+
```ruby
|
963
|
+
Schemacop::Schema3.new :any_of # => Schemacop::Exceptions::InvalidSchemaError: Node "any_of" makes only sense with at least 1 item.
|
964
|
+
```
|
965
|
+
|
670
966
|
### OneOf
|
671
967
|
|
672
968
|
Type: `:one_of`\
|
673
969
|
DSL: `one_of`
|
674
970
|
|
971
|
+
Similar to the `all_of` node, you can specify multiple schemas, for which the
|
972
|
+
given value needs to validate against exaclty one of the schemas. If the given
|
973
|
+
value validates against multiple schemas, the value is invalid.
|
974
|
+
|
975
|
+
For example, if you want an integer which is either a multiple of 2 or 3,
|
976
|
+
but not both (i.e. no multiple of 6), you could do it as follows:
|
977
|
+
|
978
|
+
```ruby
|
979
|
+
schema = Schemacop::Schema3.new :one_of do
|
980
|
+
int multiple_of: 2
|
981
|
+
int multiple_of: 3
|
982
|
+
end
|
983
|
+
|
984
|
+
schema.validate!(2) # => 2
|
985
|
+
schema.validate!(3) # => 3
|
986
|
+
schema.validate!(4) # => 4
|
987
|
+
schema.validate!(5) # => Schemacop::Exceptions::ValidationError: /: Matches 0 definitions but should match exactly 1.
|
988
|
+
schema.validate!(6) # => Schemacop::Exceptions::ValidationError: /: Matches 2 definitions but should match exactly 1.
|
989
|
+
```
|
990
|
+
|
991
|
+
Again, as previously with the AllOf node, you're allowed to create schemas
|
992
|
+
which will not work for any input, e.g. by specifying the same schema twice:
|
993
|
+
|
994
|
+
```ruby
|
995
|
+
schema = Schemacop::Schema3.new :one_of do
|
996
|
+
int multiple_of: 2
|
997
|
+
int multiple_of: 2
|
998
|
+
end
|
999
|
+
|
1000
|
+
schema.validate!(2) # => Schemacop::Exceptions::ValidationError: /: Matches 2 definitions but should match exactly 1.
|
1001
|
+
schema.validate!(3) # => Schemacop::Exceptions::ValidationError: /: Matches 0 definitions but should match exactly 1.
|
1002
|
+
schema.validate!(4) # => Schemacop::Exceptions::ValidationError: /: Matches 2 definitions but should match exactly 1.
|
1003
|
+
schema.validate!(5) # => Schemacop::Exceptions::ValidationError: /: Matches 0 definitions but should match exactly 1.
|
1004
|
+
schema.validate!(6) # => Schemacop::Exceptions::ValidationError: /: Matches 2 definitions but should match exactly 1.
|
1005
|
+
```
|
1006
|
+
|
675
1007
|
### IsNot
|
676
1008
|
|
677
1009
|
Type: `:is_not`\
|
678
1010
|
DSL: `is_not`
|
679
1011
|
|
1012
|
+
With the `is_not` node, you can specify a schema which the given value must not
|
1013
|
+
validate against, i.e. every value which matches the schema will make this node
|
1014
|
+
invalid.
|
1015
|
+
|
1016
|
+
For example, you want anything but the numbers between 3 and 5:
|
1017
|
+
|
1018
|
+
```ruby
|
1019
|
+
schema = Schemacop::Schema3.new :is_not do
|
1020
|
+
int minimum: 3, maximum: 5
|
1021
|
+
end
|
1022
|
+
|
1023
|
+
schema.validate!(nil) # => nil
|
1024
|
+
schema.validate!(1) # => 1
|
1025
|
+
schema.validate!(2) # => 2
|
1026
|
+
schema.validate!(3) # => Schemacop::Exceptions::ValidationError: /: Must not match schema: {"type"=>"integer", "minimum"=>3, "maximum"=>5}.
|
1027
|
+
schema.validate!('foo') # => "foo"
|
1028
|
+
```
|
1029
|
+
|
1030
|
+
Note that a `is_not` node needs exactly one item:
|
1031
|
+
|
1032
|
+
```ruby
|
1033
|
+
schema = Schemacop::Schema3.new :is_not # => Schemacop::Exceptions::InvalidSchemaError: Node "is_not" only allows exactly one item.
|
1034
|
+
```
|
1035
|
+
|
680
1036
|
### Reference
|
681
1037
|
|
682
|
-
|
1038
|
+
**Referencing**
|
1039
|
+
DSL: `ref`\
|
1040
|
+
Type: `reference`
|
1041
|
+
|
1042
|
+
**Definition**
|
1043
|
+
DSL: `scm`
|
1044
|
+
|
1045
|
+
Finally, with the *Reference* node, you can define schemas and then later reference
|
1046
|
+
them for usage, e.g. when you have a rather long schema which you need at multiple
|
1047
|
+
places.
|
683
1048
|
|
1049
|
+
#### Examples
|
1050
|
+
|
1051
|
+
For example, let's define an object with an schema called `Address`, which we'll
|
1052
|
+
reference multiple times:
|
1053
|
+
|
1054
|
+
```ruby
|
1055
|
+
schema = Schemacop::Schema3.new :hash do
|
1056
|
+
scm :Address do
|
1057
|
+
str! :street
|
1058
|
+
str! :zip_code
|
1059
|
+
str! :location
|
1060
|
+
str! :country
|
1061
|
+
end
|
1062
|
+
|
1063
|
+
ref! :shipping_address, :Address
|
1064
|
+
ref! :billing_address, :Address
|
1065
|
+
end
|
1066
|
+
|
1067
|
+
schema.validate!({}) # => Schemacop::Exceptions::ValidationError: /shipping_address: Value must be given. /billing_address: Value must be given.
|
1068
|
+
schema.validate!({
|
1069
|
+
shipping_address: 'foo',
|
1070
|
+
billing_address: 42
|
1071
|
+
})
|
1072
|
+
# => Schemacop::Exceptions::ValidationError: /shipping_address: Invalid type, expected "object". /billing_address: Invalid type, expected "object".
|
1073
|
+
|
1074
|
+
schema.validate!({
|
1075
|
+
shipping_address: {
|
1076
|
+
street: 'Example Street 42',
|
1077
|
+
zip_code: '12345',
|
1078
|
+
location: 'London',
|
1079
|
+
country: 'United Kingdom'
|
1080
|
+
},
|
1081
|
+
billing_address: {
|
1082
|
+
street: 'Main St.',
|
1083
|
+
zip_code: '54321',
|
1084
|
+
location: 'Washington DC',
|
1085
|
+
country: 'USA'
|
1086
|
+
}
|
1087
|
+
})
|
1088
|
+
|
1089
|
+
# => {"shipping_address"=>{"street"=>"Example Street 42", "zip_code"=>"12345", "location"=>"London", "country"=>"United Kingdom"}, "billing_address"=>{"street"=>"Main St.", "zip_code"=>"54321", "location"=>"Washington DC", "country"=>"USA"}}
|
1090
|
+
```
|
1091
|
+
|
1092
|
+
Note that if you use the reference node with the long type name `reference`,
|
1093
|
+
e.g. in an array, you need to specify the "name" of the schema in the
|
1094
|
+
`path` option:
|
1095
|
+
|
1096
|
+
```ruby
|
1097
|
+
schema = Schemacop::Schema3.new :array do
|
1098
|
+
scm :User do
|
1099
|
+
str! :first_name
|
1100
|
+
str! :last_name
|
1101
|
+
end
|
1102
|
+
|
1103
|
+
list :reference, path: :User
|
1104
|
+
end
|
1105
|
+
|
1106
|
+
schema.validate!([]) # => []
|
1107
|
+
schema.validate!([{first_name: 'Joe', last_name: 'Doe'}]) # => [{"first_name"=>"Joe", "last_name"=>"Doe"}]
|
1108
|
+
schema.validate!([id: 42, first_name: 'Joe']) # => Schemacop::Exceptions::ValidationError: /[0]/last_name: Value must be given. /[0]: Obsolete property "id".
|
1109
|
+
```
|
1110
|
+
|
1111
|
+
## Context
|
1112
|
+
|
1113
|
+
Schemacop also features the concept of a `Context`. You can define schemas in a
|
1114
|
+
context, and then reference them in other schemas in that context. This is e.g.
|
1115
|
+
useful if you need a part of the schema to be different depending on the
|
1116
|
+
business action.
|
1117
|
+
|
1118
|
+
Examples:
|
1119
|
+
|
1120
|
+
```ruby
|
1121
|
+
# Define a new context
|
1122
|
+
context = Schemacop::V3::Context.new
|
1123
|
+
|
1124
|
+
# Define the :Person schema in that context
|
1125
|
+
context.schema :Person do
|
1126
|
+
str! :first_name
|
1127
|
+
str! :last_name
|
1128
|
+
ref? :info, :PersonInfo
|
1129
|
+
end
|
1130
|
+
|
1131
|
+
# And also define the :PersonInfo schema in that context
|
1132
|
+
context.schema :PersonInfo do
|
1133
|
+
str! :born_at, format: :date
|
1134
|
+
end
|
1135
|
+
|
1136
|
+
# Now we can define our general schema, where we reference the :Person schema.
|
1137
|
+
# Note that at this point, we don't know what's in the :Person schema.
|
1138
|
+
schema = Schemacop::Schema3.new :reference, path: :Person
|
1139
|
+
|
1140
|
+
# Validate the data in the context we defined before, where we need the first_name
|
1141
|
+
# and last_name of a person, as well as an optional info hash with the born_at date
|
1142
|
+
# of the person.
|
1143
|
+
Schemacop.with_context context do
|
1144
|
+
schema.validate!({first_name: 'Joe', last_name: 'Doe', info: { born_at: '1980-01-01'} })
|
1145
|
+
# => {"first_name"=>"Joe", "last_name"=>"Doe", "info"=>{"born_at"=>Tue, 01 Jan 1980}}
|
1146
|
+
end
|
1147
|
+
|
1148
|
+
# Now we might want another context, where the person is more anonymous, and as
|
1149
|
+
# such, we need another schema
|
1150
|
+
other_context = Schemacop::V3::Context.new
|
1151
|
+
|
1152
|
+
# Here, we only want the nickname of the person
|
1153
|
+
other_context.schema :Person do
|
1154
|
+
str! :nickname
|
1155
|
+
end
|
1156
|
+
|
1157
|
+
# Finally, validate the data in the new context. We do not want the real name or
|
1158
|
+
# birth date of the person, instead only the nickname is allowed.
|
1159
|
+
Schemacop.with_context other_context do
|
1160
|
+
schema.validate!({first_name: 'Joe', last_name: 'Doe', info: { born_at: '1980-01-01'} })
|
1161
|
+
# => Schemacop::Exceptions::ValidationError: /nickname: Value must be given.
|
1162
|
+
# /: Obsolete property "first_name".
|
1163
|
+
# /: Obsolete property "last_name".
|
1164
|
+
# /: Obsolete property "info".
|
1165
|
+
|
1166
|
+
schema.validate!({nickname: 'J.'}) # => {"nickname"=>"J."}
|
1167
|
+
end
|
1168
|
+
```
|
1169
|
+
|
1170
|
+
As one can see, we validated the data against the same schema, but because we
|
1171
|
+
defined the referenced schemas differently in the two contexts, we were able
|
1172
|
+
to use other data in the second context than in the first.
|
1173
|
+
|
1174
|
+
## External schemas
|
1175
|
+
|
1176
|
+
Finally, Schemacop features the possibility to specify schemas in seperate
|
1177
|
+
files. This is especially useful is you have schemas in your application which
|
1178
|
+
are used multiple times throughout the application.
|
1179
|
+
|
1180
|
+
For each schema, you define the schema in a separate file, and after loading the
|
1181
|
+
schemas, you can reference them in other schemas.
|
1182
|
+
|
1183
|
+
The default load path is `'app/schemas'`, but this can be configured by setting
|
1184
|
+
the value of the `load_paths` attribute of the `Schemacop` module.
|
1185
|
+
|
1186
|
+
Please note that the following predescence order is used for the schemas:
|
1187
|
+
|
1188
|
+
```
|
1189
|
+
local schemas > context schemas > global schemas
|
1190
|
+
```
|
1191
|
+
|
1192
|
+
Where:
|
1193
|
+
|
1194
|
+
* local schemas: Defined by using the DSL method? `scm`
|
1195
|
+
* context schemas: Defined in the current context using `context.schema`
|
1196
|
+
* global schemas: Defined in a ruby file in the load path
|
1197
|
+
|
1198
|
+
### Rails applications
|
1199
|
+
|
1200
|
+
In Rails applications, your schemas are automatically eager-laoded from the load
|
1201
|
+
path `'app/schemas'` when your application is started.
|
1202
|
+
|
1203
|
+
After starting your application, you can reference them like normally defined
|
1204
|
+
reference schemas, with the name being relative to the load path.
|
1205
|
+
|
1206
|
+
Example:
|
1207
|
+
|
1208
|
+
You defined the following two schemas in the `'app/schemas'` directory:
|
1209
|
+
|
1210
|
+
```ruby
|
1211
|
+
# app/schemas/user.rb
|
1212
|
+
schema :hash do
|
1213
|
+
str! :first_name
|
1214
|
+
str! :last_name
|
1215
|
+
ary? :groups do
|
1216
|
+
list :reference, path: 'nested/group'
|
1217
|
+
end
|
1218
|
+
end
|
1219
|
+
```
|
1220
|
+
|
1221
|
+
```ruby
|
1222
|
+
# app/schemas/nested/user.rb
|
1223
|
+
schema :hash do
|
1224
|
+
str! :name
|
1225
|
+
end
|
1226
|
+
```
|
1227
|
+
|
1228
|
+
To use the schema, you then can simply reference the schema as with normal
|
1229
|
+
reference schemas:
|
1230
|
+
|
1231
|
+
```ruby
|
1232
|
+
schema = Schemacop::Schema3.new :hash do
|
1233
|
+
ref! :usr, :user
|
1234
|
+
end
|
1235
|
+
|
1236
|
+
schema.validate!({usr: {first_name: 'Joe', last_name: 'Doe'}})
|
1237
|
+
# => {"usr"=>{"first_name"=>"Joe", "last_name"=>"Doe"}}
|
1238
|
+
|
1239
|
+
schema.validate!({usr: {first_name: 'Joe', last_name: 'Doe', groups: []}})
|
1240
|
+
# => {"usr"=>{"first_name"=>"Joe", "last_name"=>"Doe", "groups"=>[]}}
|
1241
|
+
|
1242
|
+
schema.validate!({usr: {first_name: 'Joe', last_name: 'Doe', groups: [{name: 'foo'}, {name: 'bar'}]}})
|
1243
|
+
# => {"usr"=>{"first_name"=>"Joe", "last_name"=>"Doe", "groups"=>[{"name"=>"foo"}, {"name"=>"bar"}]}}
|
1244
|
+
```
|
1245
|
+
|
1246
|
+
### Non-Rails applications
|
1247
|
+
|
1248
|
+
Usage in non-Rails applications is the same as with usage in Rails applications,
|
1249
|
+
however you need to eager load the schemas yourself:
|
1250
|
+
|
1251
|
+
```ruby
|
1252
|
+
Schemacop::V3::GlobalContext.eager_load!
|
1253
|
+
```
|