schemacop 2.4.5 → 3.0.0.rc2
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/.gitignore +3 -0
- data/.rubocop.yml +25 -1
- data/.travis.yml +3 -1
- data/CHANGELOG.md +32 -1
- data/README.md +53 -710
- data/README_V2.md +775 -0
- data/README_V3.md +1195 -0
- data/Rakefile +8 -12
- data/VERSION +1 -1
- data/lib/schemacop.rb +35 -36
- data/lib/schemacop/base_schema.rb +37 -0
- data/lib/schemacop/railtie.rb +10 -0
- data/lib/schemacop/schema.rb +1 -60
- data/lib/schemacop/schema2.rb +22 -0
- data/lib/schemacop/schema3.rb +21 -0
- data/lib/schemacop/scoped_env.rb +25 -13
- data/lib/schemacop/v2.rb +26 -0
- data/lib/schemacop/{caster.rb → v2/caster.rb} +16 -2
- data/lib/schemacop/{collector.rb → v2/collector.rb} +5 -2
- data/lib/schemacop/{dupper.rb → v2/dupper.rb} +1 -1
- data/lib/schemacop/{field_node.rb → v2/field_node.rb} +4 -3
- data/lib/schemacop/v2/node.rb +142 -0
- data/lib/schemacop/{node_resolver.rb → v2/node_resolver.rb} +1 -1
- data/lib/schemacop/v2/node_supporting_field.rb +70 -0
- data/lib/schemacop/{node_supporting_type.rb → v2/node_supporting_type.rb} +14 -11
- data/lib/schemacop/{node_with_block.rb → v2/node_with_block.rb} +3 -2
- data/lib/schemacop/v2/root_node.rb +6 -0
- data/lib/schemacop/v2/validator/array_validator.rb +32 -0
- data/lib/schemacop/{validator → v2/validator}/boolean_validator.rb +1 -1
- data/lib/schemacop/v2/validator/float_validator.rb +7 -0
- data/lib/schemacop/v2/validator/hash_validator.rb +37 -0
- data/lib/schemacop/v2/validator/integer_validator.rb +7 -0
- data/lib/schemacop/{validator → v2/validator}/nil_validator.rb +1 -1
- data/lib/schemacop/v2/validator/number_validator.rb +21 -0
- data/lib/schemacop/v2/validator/object_validator.rb +29 -0
- data/lib/schemacop/v2/validator/string_validator.rb +39 -0
- data/lib/schemacop/{validator → v2/validator}/symbol_validator.rb +1 -1
- data/lib/schemacop/v3.rb +45 -0
- data/lib/schemacop/v3/all_of_node.rb +27 -0
- data/lib/schemacop/v3/any_of_node.rb +28 -0
- data/lib/schemacop/v3/array_node.rb +218 -0
- data/lib/schemacop/v3/boolean_node.rb +16 -0
- data/lib/schemacop/v3/combination_node.rb +45 -0
- data/lib/schemacop/v3/context.rb +17 -0
- data/lib/schemacop/v3/dsl_scope.rb +46 -0
- data/lib/schemacop/v3/global_context.rb +114 -0
- data/lib/schemacop/v3/hash_node.rb +256 -0
- data/lib/schemacop/v3/integer_node.rb +13 -0
- data/lib/schemacop/v3/is_not_node.rb +32 -0
- data/lib/schemacop/v3/node.rb +215 -0
- data/lib/schemacop/v3/node_registry.rb +49 -0
- data/lib/schemacop/v3/number_node.rb +18 -0
- data/lib/schemacop/v3/numeric_node.rb +76 -0
- data/lib/schemacop/v3/object_node.rb +40 -0
- data/lib/schemacop/v3/one_of_node.rb +28 -0
- data/lib/schemacop/v3/reference_node.rb +49 -0
- data/lib/schemacop/v3/result.rb +58 -0
- data/lib/schemacop/v3/string_node.rb +124 -0
- data/lib/schemacop/v3/symbol_node.rb +13 -0
- data/schemacop.gemspec +24 -27
- data/test/lib/test_helper.rb +152 -0
- data/test/schemas/nested/group.rb +6 -0
- data/test/schemas/user.rb +7 -0
- data/test/unit/schemacop/v2/casting_test.rb +120 -0
- data/test/unit/schemacop/v2/collector_test.rb +47 -0
- data/test/unit/schemacop/v2/custom_check_test.rb +95 -0
- data/test/unit/schemacop/v2/custom_if_test.rb +97 -0
- data/test/unit/schemacop/v2/defaults_test.rb +95 -0
- data/test/unit/schemacop/v2/empty_test.rb +16 -0
- data/test/unit/schemacop/v2/nil_dis_allow_test.rb +43 -0
- data/test/unit/schemacop/v2/node_resolver_test.rb +28 -0
- data/test/unit/schemacop/v2/short_forms_test.rb +351 -0
- data/test/unit/schemacop/v2/types_test.rb +88 -0
- data/test/unit/schemacop/v2/validator_array_test.rb +99 -0
- data/test/unit/schemacop/v2/validator_boolean_test.rb +17 -0
- data/test/unit/schemacop/v2/validator_float_test.rb +59 -0
- data/test/unit/schemacop/v2/validator_hash_test.rb +95 -0
- data/test/unit/schemacop/v2/validator_integer_test.rb +48 -0
- data/test/unit/schemacop/v2/validator_nil_test.rb +15 -0
- data/test/unit/schemacop/v2/validator_number_test.rb +62 -0
- data/test/unit/schemacop/v2/validator_object_test.rb +141 -0
- data/test/unit/schemacop/v2/validator_string_test.rb +78 -0
- data/test/unit/schemacop/v2/validator_symbol_test.rb +18 -0
- data/test/unit/schemacop/v3/all_of_node_test.rb +198 -0
- data/test/unit/schemacop/v3/any_of_node_test.rb +218 -0
- data/test/unit/schemacop/v3/array_node_test.rb +815 -0
- data/test/unit/schemacop/v3/boolean_node_test.rb +126 -0
- data/test/unit/schemacop/v3/global_context_test.rb +164 -0
- data/test/unit/schemacop/v3/hash_node_test.rb +884 -0
- data/test/unit/schemacop/v3/integer_node_test.rb +323 -0
- data/test/unit/schemacop/v3/is_not_node_test.rb +173 -0
- data/test/unit/schemacop/v3/node_test.rb +148 -0
- data/test/unit/schemacop/v3/number_node_test.rb +292 -0
- data/test/unit/schemacop/v3/object_node_test.rb +170 -0
- data/test/unit/schemacop/v3/one_of_node_test.rb +187 -0
- data/test/unit/schemacop/v3/reference_node_test.rb +351 -0
- data/test/unit/schemacop/v3/string_node_test.rb +334 -0
- data/test/unit/schemacop/v3/symbol_node_test.rb +75 -0
- metadata +152 -145
- data/doc/Schemacop.html +0 -146
- data/doc/Schemacop/ArrayValidator.html +0 -329
- data/doc/Schemacop/BooleanValidator.html +0 -145
- data/doc/Schemacop/Caster.html +0 -379
- data/doc/Schemacop/Collector.html +0 -787
- data/doc/Schemacop/Dupper.html +0 -214
- data/doc/Schemacop/Exceptions.html +0 -115
- data/doc/Schemacop/Exceptions/InvalidSchemaError.html +0 -124
- data/doc/Schemacop/Exceptions/ValidationError.html +0 -124
- data/doc/Schemacop/FieldNode.html +0 -421
- data/doc/Schemacop/FloatValidator.html +0 -158
- data/doc/Schemacop/HashValidator.html +0 -293
- data/doc/Schemacop/IntegerValidator.html +0 -158
- data/doc/Schemacop/NilValidator.html +0 -145
- data/doc/Schemacop/Node.html +0 -1438
- data/doc/Schemacop/NodeResolver.html +0 -258
- data/doc/Schemacop/NodeSupportingField.html +0 -590
- data/doc/Schemacop/NodeSupportingType.html +0 -612
- data/doc/Schemacop/NodeWithBlock.html +0 -289
- data/doc/Schemacop/NumberValidator.html +0 -232
- data/doc/Schemacop/ObjectValidator.html +0 -298
- data/doc/Schemacop/RootNode.html +0 -171
- data/doc/Schemacop/Schema.html +0 -699
- data/doc/Schemacop/StringValidator.html +0 -295
- data/doc/Schemacop/SymbolValidator.html +0 -145
- data/doc/ScopedEnv.html +0 -351
- data/doc/_index.html +0 -379
- data/doc/class_list.html +0 -51
- data/doc/css/common.css +0 -1
- data/doc/css/full_list.css +0 -58
- data/doc/css/style.css +0 -496
- data/doc/file.README.html +0 -833
- data/doc/file_list.html +0 -56
- data/doc/frames.html +0 -17
- data/doc/index.html +0 -833
- data/doc/inheritance.graphml +0 -524
- data/doc/inheritance.pdf +0 -825
- data/doc/js/app.js +0 -303
- data/doc/js/full_list.js +0 -216
- data/doc/js/jquery.js +0 -4
- data/doc/method_list.html +0 -587
- data/doc/top-level-namespace.html +0 -112
- data/lib/schemacop/node.rb +0 -139
- data/lib/schemacop/node_supporting_field.rb +0 -58
- data/lib/schemacop/root_node.rb +0 -4
- data/lib/schemacop/validator/array_validator.rb +0 -30
- data/lib/schemacop/validator/float_validator.rb +0 -5
- data/lib/schemacop/validator/hash_validator.rb +0 -35
- data/lib/schemacop/validator/integer_validator.rb +0 -5
- data/lib/schemacop/validator/number_validator.rb +0 -19
- data/lib/schemacop/validator/object_validator.rb +0 -27
- data/lib/schemacop/validator/string_validator.rb +0 -37
- data/test/casting_test.rb +0 -90
- data/test/collector_test.rb +0 -45
- data/test/custom_check_test.rb +0 -93
- data/test/custom_if_test.rb +0 -95
- data/test/defaults_test.rb +0 -93
- data/test/empty_test.rb +0 -14
- data/test/nil_dis_allow_test.rb +0 -41
- data/test/node_resolver_test.rb +0 -26
- data/test/short_forms_test.rb +0 -349
- data/test/test_helper.rb +0 -13
- data/test/types_test.rb +0 -84
- data/test/validator_array_test.rb +0 -97
- data/test/validator_boolean_test.rb +0 -15
- data/test/validator_float_test.rb +0 -57
- data/test/validator_hash_test.rb +0 -93
- data/test/validator_integer_test.rb +0 -46
- data/test/validator_nil_test.rb +0 -13
- data/test/validator_number_test.rb +0 -60
- data/test/validator_object_test.rb +0 -139
- data/test/validator_string_test.rb +0 -76
- data/test/validator_symbol_test.rb +0 -16
data/README_V3.md
ADDED
@@ -0,0 +1,1195 @@
|
|
1
|
+
# Schemacop schema V3
|
2
|
+
|
3
|
+
## Table of Contents
|
4
|
+
|
5
|
+
1. [Validation](#validation)
|
6
|
+
2. [Exceptions](#exceptions)
|
7
|
+
3. [Generic Keywords](#generic-keywords)
|
8
|
+
4. [Nodes](#nodes)
|
9
|
+
1. [String](#string)
|
10
|
+
2. [Integer](#integer)
|
11
|
+
3. [Number](#number)
|
12
|
+
4. [Symbol](#symbol)
|
13
|
+
5. [Boolean](#boolean)
|
14
|
+
6. [Array](#array)
|
15
|
+
7. [Hash](#hash)
|
16
|
+
8. [Object](#object)
|
17
|
+
9. [AllOf](#allOf)
|
18
|
+
10. [AnyOf](#anyOf)
|
19
|
+
11. [OneOf](#oneOf)
|
20
|
+
12. [IsNot](#isNot)
|
21
|
+
13. [Reference](#reference)
|
22
|
+
5. [Context](#context)
|
23
|
+
6. [External schemas](#external-schemas)
|
24
|
+
|
25
|
+
## Validation
|
26
|
+
|
27
|
+
Using schemacop, you can either choose to validate the data either using the
|
28
|
+
graceful `validate` method, or the bang variant, `validate!`.
|
29
|
+
|
30
|
+
The `validate` method on a schema with some supplied data will return a
|
31
|
+
`Schemacop::Result` object, which has some useful methods to work with the
|
32
|
+
data you validated.
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
schema = Schemacop::Schema3.new :string, format: :date
|
36
|
+
result = schema.validate('2020-01-01')
|
37
|
+
result.class # => Schemacop::Result
|
38
|
+
```
|
39
|
+
|
40
|
+
With the `data` method, you can access the casted version of your data:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
schema = Schemacop::Schema3.new :string, format: :date
|
44
|
+
result = schema.validate('2020-01-01')
|
45
|
+
result.data # => Wed, 01 Jan 2020
|
46
|
+
```
|
47
|
+
|
48
|
+
And with the `valid?` method, you can check if the supplied data validates
|
49
|
+
against the schema:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
schema = Schemacop::Schema3.new :string, format: :date
|
53
|
+
result = schema.validate('2020-01-01')
|
54
|
+
result.valid? # => true
|
55
|
+
```
|
56
|
+
|
57
|
+
On the other hand, the `validate!` method either returns the casted data if the
|
58
|
+
validation was successful, or if the validation failed, raises a
|
59
|
+
`Schemacop::Exceptions::ValidationError` exception:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
schema = Schemacop::Schema3.new :string, format: :date
|
63
|
+
schema.validate!('2020-01-01') # => Wed, 01 Jan 2020
|
64
|
+
schema.validate!('Foo') # => Schemacop::Exceptions::ValidationError: /: String does not match format "date".
|
65
|
+
```
|
66
|
+
|
67
|
+
## Exceptions
|
68
|
+
|
69
|
+
Schemacop can raise the following exceptions:
|
70
|
+
|
71
|
+
* `Schemacop::Exceptions::ValidationError`: This exception is raised when the `validate!`
|
72
|
+
method is used, and the data that was passed in is invalid. The exception message contains
|
73
|
+
additional informations why the validation failed.
|
74
|
+
|
75
|
+
Example:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
schema = Schemacop::Schema3.new :hash 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 schema
|
87
|
+
itself is not valid. The exception message contains additional informations why the
|
88
|
+
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
|
+
```
|
99
|
+
|
100
|
+
## Generic Keywords
|
101
|
+
|
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
|
+
|
113
|
+
The three keywords `title`, `description` and `examples` aren't used for validation,
|
114
|
+
but can be used to document the schema. They will be included in the JSON output
|
115
|
+
when you use the `as_json` method:
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
schema = Schemacop::Schema3.new :hash do
|
119
|
+
str! :name, title: 'Name', description: 'Holds the name of the user', examples: ['Joe', 'Anna']
|
120
|
+
end
|
121
|
+
|
122
|
+
schema.as_json
|
123
|
+
|
124
|
+
# => {"properties"=>{"name"=>{"type"=>"string", "title"=>"Name", "examples"=>["Joe", "Anna"], "description"=>"Holds the name of the user"}}, "additionalProperties"=>false, "required"=>["name"], "type"=>"object"}
|
125
|
+
```
|
126
|
+
|
127
|
+
The `enum` keyword can be used to only allow a subset of values:
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
schema = Schemacop::Schema3.new :string, enum: ['foo', 'bar']
|
131
|
+
|
132
|
+
schema.validate!('foo') # => "foo"
|
133
|
+
schema.validate!('bar') # => "bar"
|
134
|
+
schema.validate!('baz') # => Schemacop::Exceptions::ValidationError: /: Value not included in enum ["foo", "bar"].
|
135
|
+
```
|
136
|
+
|
137
|
+
Please note, that you can also specify values in the enum that are not valid for
|
138
|
+
the schema. This means that the validation will still fail:
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
schema = Schemacop::Schema3.new :string, enum: ['foo', 'bar', 42]
|
142
|
+
|
143
|
+
schema.validate!('foo') # => "foo"
|
144
|
+
schema.validate!('bar') # => "bar"
|
145
|
+
schema.validate!(42) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "string".
|
146
|
+
```
|
147
|
+
|
148
|
+
The enum will also be provided in the json output:
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
schema = Schemacop::Schema3.new :string, enum: ['foo', 'bar']
|
152
|
+
|
153
|
+
schema.as_json
|
154
|
+
# => {"type"=>"string", "enum"=>["foo", "bar", 42]}
|
155
|
+
```
|
156
|
+
|
157
|
+
And finally, the `default` keyword lets you set a default value to use when no
|
158
|
+
value is provided:
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
schema = Schemacop::Schema3.new :string, default: 'Schemacop'
|
162
|
+
|
163
|
+
schema.validate!('foo') # => "foo"
|
164
|
+
schema.validate!(nil) # => "Schemacop"
|
165
|
+
```
|
166
|
+
|
167
|
+
The default value will also be provided in the json output:
|
168
|
+
|
169
|
+
```ruby
|
170
|
+
schema = Schemacop::Schema3.new :string, default: 'Schemacop'
|
171
|
+
|
172
|
+
schema.as_json
|
173
|
+
# => {"type"=>"string", "default"=>"Schemacop"}
|
174
|
+
```
|
175
|
+
|
176
|
+
Note that the default value you use is also validated against the schema:
|
177
|
+
|
178
|
+
```ruby
|
179
|
+
schema = Schemacop::Schema3.new :string, default: 42
|
180
|
+
|
181
|
+
schema.validate!('foo') # => "foo"
|
182
|
+
schema.validate!(nil) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "string".
|
183
|
+
```
|
184
|
+
|
185
|
+
## Nodes
|
186
|
+
|
187
|
+
### String
|
188
|
+
|
189
|
+
Type: `:string`\
|
190
|
+
DSL: `str`
|
191
|
+
|
192
|
+
The string type is used for strings of text and must be a ruby `String` object
|
193
|
+
or a subclass. Using the option `format`, strings can be validated against and
|
194
|
+
transformed into various types.
|
195
|
+
|
196
|
+
#### Options
|
197
|
+
|
198
|
+
* `min_length`
|
199
|
+
Defines the minimum required string length
|
200
|
+
* `max_length`
|
201
|
+
Defines the maximum required string length
|
202
|
+
* `pattern`
|
203
|
+
Defines a (ruby) regex pattern the value will be matched against. Must be a
|
204
|
+
string and should generally start with `^` and end with `$` so as to evaluate
|
205
|
+
the entire string. It should not be enclosed in `/` characters.
|
206
|
+
* `format`
|
207
|
+
The `format` option allows for basic semantic validation on certain kinds of
|
208
|
+
string values that are commonly used. See section *formats* for more
|
209
|
+
information on the available formats. Note that strings with a format are also
|
210
|
+
**casted** into that format.
|
211
|
+
|
212
|
+
#### Formats
|
213
|
+
|
214
|
+
* `date`
|
215
|
+
A date according to [ RFC 3339, section
|
216
|
+
5.6.](https://json-schema.org/latest/json-schema-validation.html#RFC3339) date
|
217
|
+
format, i.e. `2018-11-13`. Strings with this format will be
|
218
|
+
casted to a ruby `Date` object.
|
219
|
+
|
220
|
+
* `date_time`
|
221
|
+
A date time according to [RFC 3339, section
|
222
|
+
5.6.](https://json-schema.org/latest/json-schema-validation.html#RFC3339) date
|
223
|
+
format, i.e. `2018-11-13T20:20:39+00:00`. Strings with this format will be
|
224
|
+
casted to a ruby `DateTime` object. The time zones will be inferred by the
|
225
|
+
string.
|
226
|
+
|
227
|
+
* `email`
|
228
|
+
Validates for a valid email address. There is no casting involved since email
|
229
|
+
addresses do not have their own ruby type.
|
230
|
+
|
231
|
+
* `boolean`
|
232
|
+
The string must be either `true` or `false`. This value will be casted to
|
233
|
+
ruby's `TrueClass` or `FalseClass`.
|
234
|
+
|
235
|
+
* `binary`
|
236
|
+
The string is expected to contain binary contents. No casting or additional
|
237
|
+
validation is performed.
|
238
|
+
|
239
|
+
* `integer`
|
240
|
+
The string must be an integer and will be casted to a ruby `Integer` object.
|
241
|
+
|
242
|
+
* `number`
|
243
|
+
The string must be a number and will be casted to a ruby `Float` object.
|
244
|
+
|
245
|
+
#### Examples
|
246
|
+
|
247
|
+
```ruby
|
248
|
+
# By using a format, string values are casted to that respective format
|
249
|
+
schema = Schemacop::Schema3.new(:string, format: :date)
|
250
|
+
result = schema.validate('1980-01-13')
|
251
|
+
result.data # => Date<"Sun, 13 Jan 1980">
|
252
|
+
```
|
253
|
+
|
254
|
+
### Integer
|
255
|
+
|
256
|
+
Type: `:integer`\
|
257
|
+
DSL: `int`
|
258
|
+
|
259
|
+
The integer type is used for whole numbers and must be a ruby `Integer` or a
|
260
|
+
subclass. With the various available options, validations on the value of the
|
261
|
+
integer can be done.
|
262
|
+
|
263
|
+
#### Options
|
264
|
+
|
265
|
+
* `minimum`
|
266
|
+
Defines an (inclusive) minimum, i.e. the number has to be equal or larger than the
|
267
|
+
given number
|
268
|
+
* `exclusive_minimum`
|
269
|
+
Defines an exclusive minimum, i.e. the number has to larger than the given number
|
270
|
+
* `maximum`
|
271
|
+
Defines an (inclusive) maximum, i.e. the number has to be equal or smaller than the
|
272
|
+
given number
|
273
|
+
* `exclusive_maximum`
|
274
|
+
Defines an exclusive maximum, i.e. the number has to smaller than the given number
|
275
|
+
* `multiple_of`
|
276
|
+
The received number has to be a multiple of the given number for the validation to
|
277
|
+
pass.
|
278
|
+
|
279
|
+
#### Examples
|
280
|
+
|
281
|
+
```ruby
|
282
|
+
# Validates that the input is an even number between 0 and 100 (inclusive)
|
283
|
+
schema = Schemacop::Schema3.new(:integer, minimum: 0, maximum: 100, multiple_of: 2)
|
284
|
+
schema.validate!(42) # => 42
|
285
|
+
schema.validate!(43) # => Schemacop::Exceptions::ValidationError: /: Value must be a multiple of 2.
|
286
|
+
schema.validate!(-2) # => Schemacop::Exceptions::ValidationError: /: Value must have a minimum of 0.
|
287
|
+
schema.validate!(102) # => Schemacop::Exceptions::ValidationError: /: Value must have a maximum of 100.
|
288
|
+
schema.validate!(42.1) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "integer".
|
289
|
+
schema.validate!(4r) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "integer".
|
290
|
+
schema.validate!((4 + 0i)) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "integer".
|
291
|
+
schema.validate!(BigDecimal(5)) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "integer".
|
292
|
+
```
|
293
|
+
|
294
|
+
### Number
|
295
|
+
|
296
|
+
Type: `:number`\
|
297
|
+
DSL: `num`
|
298
|
+
|
299
|
+
The number type is used to validate various number classes. The following ruby classes
|
300
|
+
and subclasses are valid:
|
301
|
+
|
302
|
+
* `Integer`
|
303
|
+
* `Float`
|
304
|
+
* `Rational`
|
305
|
+
* `BigDecimal`
|
306
|
+
|
307
|
+
As some subclasses of `Numeric`, such as `Complex` don't support all required oeprations,
|
308
|
+
only the above list is supported. If you need support for additional number classes, please
|
309
|
+
contact the Gem maintainers.
|
310
|
+
|
311
|
+
With the various available options, validations on the value of the number can be done.
|
312
|
+
|
313
|
+
#### Options
|
314
|
+
|
315
|
+
* `minimum`
|
316
|
+
Defines an (inclusive) minimum, i.e. the number has to be equal or larger than the
|
317
|
+
given number
|
318
|
+
* `exclusive_minimum`
|
319
|
+
Defines an exclusive minimum, i.e. the number has to larger than the given number
|
320
|
+
* `maximum`
|
321
|
+
Defines an (inclusive) maximum, i.e. the number has to be equal or smaller than the
|
322
|
+
given number
|
323
|
+
* `exclusive_maximum`
|
324
|
+
Defines an exclusive maximum, i.e. the number has to smaller than the given number
|
325
|
+
* `multiple_of`
|
326
|
+
The received number has to be a multiple of the given number for the validation to
|
327
|
+
pass.
|
328
|
+
|
329
|
+
#### Examples
|
330
|
+
|
331
|
+
```ruby
|
332
|
+
# Validates that the input is a number between 0 and 50 (inclusive) and a multiple of 0.5
|
333
|
+
schema = Schemacop::Schema3.new(:number, minimum: 0.0, maximum: (50r), multiple_of: BigDecimal('0.5'))
|
334
|
+
schema.validate!(42) # => 42
|
335
|
+
schema.validate!(42.2) # => Schemacop::Exceptions::ValidationError: /: Value must be a multiple of 0.5.
|
336
|
+
schema.validate!(-2) # => Schemacop::Exceptions::ValidationError: /: Value must have a minimum of 0.0.
|
337
|
+
schema.validate!(51) # => Schemacop::Exceptions::ValidationError: /: Value must have a maximum of 50/1.
|
338
|
+
schema.validate!(42.5) # => 42.5
|
339
|
+
schema.validate!(1.5r) # => (3/2)
|
340
|
+
schema.validate!(BigDecimal(5)) # => 0.5e1
|
341
|
+
schema.validate!((4 + 0i)) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "big_decimal" or "float" or "integer" or "rational".
|
342
|
+
```
|
343
|
+
|
344
|
+
### Symbol
|
345
|
+
|
346
|
+
Type: `:symbol`\
|
347
|
+
DSL: `sym`
|
348
|
+
|
349
|
+
The symbol type is used to validate elements for the Ruby `Symbol` class.
|
350
|
+
|
351
|
+
#### Examples
|
352
|
+
|
353
|
+
```ruby
|
354
|
+
# Validates that the input is a symbol
|
355
|
+
schema = Schemacop::Schema3.new(:symbol)
|
356
|
+
schema.validate!(:foo) # => :foo
|
357
|
+
schema.validate!('foo') # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "Symbol".
|
358
|
+
schema.validate!(123) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "Symbol".
|
359
|
+
schema.validate!(false) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "Symbol".
|
360
|
+
```
|
361
|
+
|
362
|
+
### Boolean
|
363
|
+
|
364
|
+
Type: `:boolean`\
|
365
|
+
DSL: `boo`
|
366
|
+
|
367
|
+
The boolean type is used to validate Ruby booleans, i.e. the `TrueClass` and `FalseClass`
|
368
|
+
|
369
|
+
#### Examples
|
370
|
+
|
371
|
+
```ruby
|
372
|
+
# Validates that the input is a boolean
|
373
|
+
schema = Schemacop::Schema3.new(:boolean)
|
374
|
+
schema.validate!(true) # => true
|
375
|
+
schema.validate!(false) # => false
|
376
|
+
schema.validate!(:false) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "boolean".
|
377
|
+
schema.validate!('false') # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "boolean".
|
378
|
+
schema.validate!(1234) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "boolean".
|
379
|
+
```
|
380
|
+
|
381
|
+
### Array
|
382
|
+
|
383
|
+
Type: `:array`\
|
384
|
+
DSL: `arr`
|
385
|
+
|
386
|
+
The array type represents a ruby `Array`.
|
387
|
+
It consists of one or multiple values, which can be validated using arbitrary nodes.
|
388
|
+
|
389
|
+
#### Options
|
390
|
+
|
391
|
+
* `min_items`
|
392
|
+
This option specifies the (inclusive) minimum number of elements the array
|
393
|
+
must contain to pass the validation.
|
394
|
+
|
395
|
+
* `max_items`
|
396
|
+
This option specifies the (inclusive) maximum number of elements the array
|
397
|
+
must contain to pass the validation.
|
398
|
+
|
399
|
+
* `unique_items`
|
400
|
+
This option specifies wether the items in the array must all be distinct from
|
401
|
+
each other, or if there may be duplicate values. By default, this is false,
|
402
|
+
i.e. duplicate values are allowed
|
403
|
+
|
404
|
+
#### Contains
|
405
|
+
|
406
|
+
The `array` node features the contains node, which you can use with the DSL
|
407
|
+
method `cont`. With that DSL method, you can specify a schema which at least
|
408
|
+
one item in the array needs to validate against.
|
409
|
+
|
410
|
+
One usecase for example could be that you want an array of integers, from which
|
411
|
+
at least one must be 5 or larger:
|
412
|
+
|
413
|
+
```ruby
|
414
|
+
schema = Schemacop::Schema3.new :array do
|
415
|
+
list :integer
|
416
|
+
cont :integer, minimum: 5
|
417
|
+
end
|
418
|
+
|
419
|
+
schema.validate!([]) # => Schemacop::Exceptions::ValidationError: /: At least one entry must match schema {"type"=>"integer", "minimum"=>5}.
|
420
|
+
schema.validate!([1, 5]) # => [1, 5]
|
421
|
+
schema.validate!(['foo']) # => Schemacop::Exceptions::ValidationError: /[0]: Invalid type, expected "integer". /: At least one entry must match schema {"type"=>"integer", "minimum"=>5}
|
422
|
+
```
|
423
|
+
|
424
|
+
You can also use it with the tuple validation (see below), e.g. if you want
|
425
|
+
an array of 3 integers, from which at least one needs to be 5 or larger:
|
426
|
+
|
427
|
+
```ruby
|
428
|
+
schema = Schemacop::Schema3.new :array do
|
429
|
+
int
|
430
|
+
int
|
431
|
+
int
|
432
|
+
cont :integer, minimum: 5
|
433
|
+
end
|
434
|
+
|
435
|
+
schema.validate!([]) # => /: Array has 0 items but must have exactly 3. /: At least one entry must match schema {"type"=>"integer", "minimum"=>5}.
|
436
|
+
schema.validate!([1, 2, 3]) # => Schemacop::Exceptions::ValidationError: /: At least one entry must match schema {"type"=>"integer", "minimum"=>5}.
|
437
|
+
schema.validate!([1, 3, 5]) # => [1, 3, 5]
|
438
|
+
```
|
439
|
+
|
440
|
+
#### Specifying properties
|
441
|
+
|
442
|
+
Array nodes support a block in which you can specify the required array contents.
|
443
|
+
The array nodes support either list validation, or tuple validation, depending on
|
444
|
+
how you specify your array contents.
|
445
|
+
|
446
|
+
##### List validation
|
447
|
+
|
448
|
+
List validation validates a sequence of arbitrary length where each item matches
|
449
|
+
the same schema. Unless you specify a `min_items` count on the array node, an
|
450
|
+
empty array will also validate. To specify a list validation, use the `list`
|
451
|
+
DSL method, and specify the type you want to validate against. Here, you need
|
452
|
+
to specify the type of the element using the long `type` name (e.g. `integer` and not `int`).
|
453
|
+
|
454
|
+
For example, you can specify that you want an array with only integers between 1 and 5:
|
455
|
+
|
456
|
+
```ruby
|
457
|
+
schema = Schemacop::Schema3.new :array do
|
458
|
+
list :integer, minimum: 1, maximum: 5
|
459
|
+
end
|
460
|
+
|
461
|
+
schema.validate!([]) # => []
|
462
|
+
schema.validate!([1, 3]) # => [1, 3]
|
463
|
+
schema.validate!([0, 6]) # => Schemacop::Exceptions::ValidationError: /[0]: Value must have a minimum of 1. /[1]: Value must have a maximum of 5.
|
464
|
+
schema.validate! ['foo'] # => Schemacop::Exceptions::ValidationError: /[0]: Invalid type, expected "integer".
|
465
|
+
```
|
466
|
+
|
467
|
+
You can also build more complex structures, e.g. an array containing an arbitrary
|
468
|
+
number of integer arrays:
|
469
|
+
|
470
|
+
```ruby
|
471
|
+
schema = Schemacop::Schema3.new :array do
|
472
|
+
list :array do
|
473
|
+
list :integer
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
schema.validate!([]) # => []
|
478
|
+
schema.validate!([[1], [2, 3]]) # => [[1], [2, 3]]
|
479
|
+
schema.validate!([['foo'], [2, 3]]) # => Schemacop::Exceptions::ValidationError: /[0]/[0]: Invalid type, expected "integer".
|
480
|
+
```
|
481
|
+
|
482
|
+
Please note that you can only specify *one* `list` item:
|
483
|
+
|
484
|
+
```ruby
|
485
|
+
schema = Schemacop::Schema3.new :array do
|
486
|
+
list :integer
|
487
|
+
list :string
|
488
|
+
end
|
489
|
+
|
490
|
+
# => Schemacop::Exceptions::InvalidSchemaError: You can only use "list" once.
|
491
|
+
```
|
492
|
+
|
493
|
+
##### Tuple validation
|
494
|
+
|
495
|
+
On the other hand, tuple validation validates a sequence of fixed length, where
|
496
|
+
each item has its own schema that it has to match. Here, the order of the items
|
497
|
+
is relevant for the validation.
|
498
|
+
|
499
|
+
For example, we want a tuple with an int, followed by a string:
|
500
|
+
|
501
|
+
```ruby
|
502
|
+
schema = Schemacop::Schema3.new :array do
|
503
|
+
int
|
504
|
+
str
|
505
|
+
end
|
506
|
+
|
507
|
+
schema.validate!([]) # => Schemacop::Exceptions::ValidationError: /: Array has 0 items but must have exactly 2.
|
508
|
+
schema.validate!([1, 'foo']) # => [1, "foo"]
|
509
|
+
schema.validate!([1, 'foo', 'bar']) # => Schemacop::Exceptions::ValidationError: /: Array has 3 items but must have exactly 2.
|
510
|
+
```
|
511
|
+
|
512
|
+
When using tuple validation, you can also allow additional items in the array
|
513
|
+
*after* the specified items, either with the option `additional_items` or the
|
514
|
+
DSL method `add`. With the option `additional_items` set to `true`, you can
|
515
|
+
allow any additional items:
|
516
|
+
|
517
|
+
```ruby
|
518
|
+
schema = Schemacop::Schema3.new :array, additional_items: true do
|
519
|
+
int
|
520
|
+
str
|
521
|
+
end
|
522
|
+
|
523
|
+
schema.validate!([]) # => Schemacop::Exceptions::ValidationError: /: Array has 0 items but must have exactly 2.
|
524
|
+
schema.validate!([1, 'foo']) # => [1, "foo"]
|
525
|
+
schema.validate!([1, 'foo', 'bar']) # => [1, "foo", "bar"]
|
526
|
+
```
|
527
|
+
|
528
|
+
You can also use the dsl method `add` to specify more exactly what type the
|
529
|
+
of the additional items may be. As with any other dsl method, you may specify
|
530
|
+
and valid schema which the additional items will be validated against:
|
531
|
+
|
532
|
+
```ruby
|
533
|
+
schema = Schemacop::Schema3.new :array do
|
534
|
+
int
|
535
|
+
str
|
536
|
+
add :integer
|
537
|
+
end
|
538
|
+
|
539
|
+
schema.validate!([]) # => Schemacop::Exceptions::ValidationError: /: Array has 0 items but must have exactly 2.
|
540
|
+
schema.validate!([1, 'foo']) # => [1, "foo"]
|
541
|
+
schema.validate!([1, 'foo', 'bar']) # => Schemacop::Exceptions::ValidationError: /[2]: Invalid type, expected "integer".
|
542
|
+
schema.validate!([1, 'foo', 2, 3]) # => [1, "foo", 2, 3]
|
543
|
+
```
|
544
|
+
|
545
|
+
Please note, that you cannot use multiple `add` in the same array schema, this will result in
|
546
|
+
an exception:
|
547
|
+
|
548
|
+
```ruby
|
549
|
+
schema = Schemacop::Schema3.new :array do
|
550
|
+
int
|
551
|
+
add :integer
|
552
|
+
add :string
|
553
|
+
end
|
554
|
+
|
555
|
+
# => Schemacop::Exceptions::InvalidSchemaError: You can only use "add" once to specify additional items.
|
556
|
+
```
|
557
|
+
|
558
|
+
If you want to specify that your schema accept multiple additional types, use the `one_of`
|
559
|
+
type (see below for more infos). The correct way to specify that you want to allow additional
|
560
|
+
items, which may be an integer or a string is as follows:
|
561
|
+
|
562
|
+
```ruby
|
563
|
+
schema = Schemacop::Schema3.new :array do
|
564
|
+
int
|
565
|
+
add :one_of do
|
566
|
+
int
|
567
|
+
str
|
568
|
+
end
|
569
|
+
end
|
570
|
+
|
571
|
+
schema.validate!([]) # => Schemacop::Exceptions::ValidationError: /: Array has 0 items but must have exactly 1.
|
572
|
+
schema.validate!([1, 2]) # => [1, 2]
|
573
|
+
schema.validate!([1, 'foo']) # => [1, "foo"]
|
574
|
+
schema.validate!([1, :bar]) # => Schemacop::Exceptions::ValidationError: /[1]: Matches 0 definitions but should match exactly 1.
|
575
|
+
```
|
576
|
+
|
577
|
+
### Hash
|
578
|
+
|
579
|
+
Type: `:hash`\
|
580
|
+
DSL: `hsh`
|
581
|
+
|
582
|
+
The hash type represents a ruby `Hash` or an `object` in JSON schema language.
|
583
|
+
It consists of key-value-pairs that can be validated using arbitrary nodes.
|
584
|
+
|
585
|
+
#### Options
|
586
|
+
|
587
|
+
* `additional_properties`
|
588
|
+
This option specifies whether additional, unspecified properties are allowed
|
589
|
+
(`true`) or not (`false`). By default, this is `true` if no properties are
|
590
|
+
specified and `false` if you have specified at least one property.
|
591
|
+
|
592
|
+
* `property_names`
|
593
|
+
This option allows to specify a regexp pattern (as string) which validates the
|
594
|
+
keys of any properties that are not specified in the hash. This option only
|
595
|
+
makes sense if `additional_properties` is enabled. See below for more informations.
|
596
|
+
|
597
|
+
* `min_properties`
|
598
|
+
Specifies the (inclusive) minimum number of properties a hash must contain.
|
599
|
+
|
600
|
+
* `max_properties`
|
601
|
+
Specifies the (inclusive) maximum number of properties a hash must contain.
|
602
|
+
|
603
|
+
#### Specifying properties
|
604
|
+
|
605
|
+
Hash nodes support a block in which you can specify the required hash contents.
|
606
|
+
|
607
|
+
##### Standard properties
|
608
|
+
|
609
|
+
It supports all type nodes, but requires the suffix `?` or `!` for each
|
610
|
+
property, which specifies whether a property is required (`!`) or optional
|
611
|
+
(`?`).
|
612
|
+
|
613
|
+
```ruby
|
614
|
+
schema = Schemacop::Schema3.new :hash do
|
615
|
+
str! :foo # Is a required property
|
616
|
+
int? :bar # Is an optional property
|
617
|
+
end
|
618
|
+
|
619
|
+
schema.validate!({}) # => Schemacop::Exceptions::ValidationError: /foo: Value must be given.
|
620
|
+
schema.validate!({foo: 'str'}) # => {"foo"=>"str"}
|
621
|
+
schema.validate!({foo: 'str', bar: 42}) # => {"foo"=>"str", "bar"=>42}
|
622
|
+
schema.validate!({bar: 42}) # => Schemacop::Exceptions::ValidationError: /foo: Value must be given.
|
623
|
+
```
|
624
|
+
|
625
|
+
The name of the properties may either be a string or a symbol, and you can pass
|
626
|
+
in the property either identified by a symbol or a string:
|
627
|
+
|
628
|
+
The following two schemas are equal:
|
629
|
+
|
630
|
+
```ruby
|
631
|
+
schema = Schemacop::Schema3.new :hash do
|
632
|
+
int! :foo
|
633
|
+
end
|
634
|
+
|
635
|
+
schema.validate!(foo: 42) # => {"foo"=>42}
|
636
|
+
schema.validate!('foo' => 42) # => {"foo"=>42}
|
637
|
+
|
638
|
+
schema = Schemacop::Schema3.new :hash do
|
639
|
+
int! 'foo'
|
640
|
+
end
|
641
|
+
|
642
|
+
schema.validate!(foo: 42) # => {"foo"=>42}
|
643
|
+
schema.validate!('foo' => 42) # => {"foo"=>42}
|
644
|
+
```
|
645
|
+
|
646
|
+
The result in both cases will be a
|
647
|
+
[HashWithIndifferentAccess](https://api.rubyonrails.org/classes/ActiveSupport/HashWithIndifferentAccess.html),
|
648
|
+
which means that you can access the data in the hash with the symbol as well
|
649
|
+
as the string representation:
|
650
|
+
|
651
|
+
```ruby
|
652
|
+
schema = Schemacop::Schema3.new :hash do
|
653
|
+
int! :foo
|
654
|
+
end
|
655
|
+
|
656
|
+
result = schema.validate!(foo: 42)
|
657
|
+
|
658
|
+
result.class # => ActiveSupport::HashWithIndifferentAccess
|
659
|
+
result[:foo] # => 42
|
660
|
+
result['foo'] # 42
|
661
|
+
```
|
662
|
+
|
663
|
+
Please note, that if you specify the value twice in the data you want to validate,
|
664
|
+
once with the key being a symbol and once being a string, Schemacop will raise an
|
665
|
+
error:
|
666
|
+
|
667
|
+
```ruby
|
668
|
+
schema = Schemacop::Schema3.new :hash do
|
669
|
+
int! :foo
|
670
|
+
end
|
671
|
+
|
672
|
+
schema.validate!(foo: 42, 'foo' => 43) # => Schemacop::Exceptions::ValidationError: /: Has 1 ambiguous properties: [:foo].
|
673
|
+
```
|
674
|
+
|
675
|
+
##### Pattern properties
|
676
|
+
|
677
|
+
In addition to symbols, property keys can also be a regular expression. Here,
|
678
|
+
you may only use the optional `?` suffix for the property. This allows any
|
679
|
+
property, which matches the type and the name of the property matches the
|
680
|
+
regular expression.
|
681
|
+
|
682
|
+
```ruby
|
683
|
+
schema = Schemacop::Schema3.new :hash do
|
684
|
+
# The following statement allows any number of integer properties of which the
|
685
|
+
# name starts with `id_`.
|
686
|
+
int? /^id_.*$/
|
687
|
+
end
|
688
|
+
|
689
|
+
schema.validate!({}) # => {}
|
690
|
+
schema.validate!({id_foo: 1}) # => {"id_foo"=>1}
|
691
|
+
schema.validate!({id_foo: 1, id_bar: 2}) # => {"id_foo"=>1, "id_bar"=>2}
|
692
|
+
schema.validate!({foo: 3}) # => Schemacop::Exceptions::ValidationError: /: Obsolete property "foo".
|
693
|
+
```
|
694
|
+
|
695
|
+
##### Additional properties & property names
|
696
|
+
|
697
|
+
In addition to standard properties, you can allow the hash to contain
|
698
|
+
additional, unspecified properties. By default, this is turned off if you have
|
699
|
+
defined at least one standard property.
|
700
|
+
|
701
|
+
When it comes to additional properties, you have the choice to either just
|
702
|
+
enable all of them by enabling the option `additional_properties`:
|
703
|
+
|
704
|
+
```ruby
|
705
|
+
# This schema will accept any additional properties
|
706
|
+
schema = Schemacop::Schema3.new :hash, additional_properties: true
|
707
|
+
|
708
|
+
schema.validate!({}) # => {}
|
709
|
+
schema.validate!({foo: :bar, baz: 42}) # => {"foo"=>:bar, "baz"=>42}
|
710
|
+
```
|
711
|
+
|
712
|
+
Using the DSL method `add` in the hash-node's body however, you can specify
|
713
|
+
an additional schema to which additional properties must adhere:
|
714
|
+
|
715
|
+
|
716
|
+
```ruby
|
717
|
+
Schemacop::Schema3.new :hash do
|
718
|
+
int! :id
|
719
|
+
|
720
|
+
# Allow any additional properties besides `id`, but their value must be a
|
721
|
+
# string.
|
722
|
+
add :string
|
723
|
+
end
|
724
|
+
|
725
|
+
schema.validate!({id: 1}) # => {"id"=>1}
|
726
|
+
schema.validate!({id: 1, foo: 'bar'}) # => {"id"=>1, "foo"=>"bar"}
|
727
|
+
schema.validate!({id: 1, foo: 42}) # => Schemacop::Exceptions::ValidationError: /foo: Invalid type, expected "string".
|
728
|
+
```
|
729
|
+
|
730
|
+
Using the option `property_names`, you can additionaly specify a pattern that
|
731
|
+
any additional property **keys** must adhere to:
|
732
|
+
|
733
|
+
```ruby
|
734
|
+
# The following schema allows any number of properties, but all keys must
|
735
|
+
# consist of downcase letters from a-z.
|
736
|
+
schema = Schemacop::Schema3.new :hash, additional_properties: true, property_names: '^[a-z]+$'
|
737
|
+
|
738
|
+
|
739
|
+
schema.validate!({}) # => {}
|
740
|
+
schema.validate!({foo: 123}) # => {"foo"=>123}
|
741
|
+
schema.validate!({Foo: 'bar'}) # => Schemacop::Exceptions::ValidationError: /: Property name "Foo" does not match "^[a-z]+$".
|
742
|
+
|
743
|
+
# The following schema allows any number of properties, but all keys must
|
744
|
+
# consist of downcase letters from a-z AND the properties must be arrays.
|
745
|
+
schema = Schemacop::Schema3.new :hash, additional_properties: true, property_names: '^[a-z]+$' do
|
746
|
+
add :array
|
747
|
+
end
|
748
|
+
|
749
|
+
schema.validate!({}) # => {}
|
750
|
+
schema.validate!({foo: [1, 2, 3]}) # => {"foo"=>[1, 2, 3]}
|
751
|
+
schema.validate!({foo: :bar}) # => Schemacop::Exceptions::ValidationError: /foo: Invalid type, expected "array".
|
752
|
+
schema.validate!({Foo: :bar}) # => Schemacop::Exceptions::ValidationError: /: Property name :Foo does not match "^[a-z]+$". /Foo: Invalid type, expected "array".
|
753
|
+
```
|
754
|
+
|
755
|
+
##### Dependencies
|
756
|
+
|
757
|
+
Using the DSL method `dep`, you can specifiy (non-nested) property dependencies:
|
758
|
+
|
759
|
+
```ruby
|
760
|
+
# In this example, `billing_address` and `phone_number` are required if
|
761
|
+
# `credit_card` is given, and `credit_card` is required if `billing_address` is
|
762
|
+
# given.
|
763
|
+
schema = Schemacop::Schema3.new :hash do
|
764
|
+
str! :name
|
765
|
+
str? :credit_card
|
766
|
+
str? :billing_address
|
767
|
+
str? :phone_number
|
768
|
+
|
769
|
+
dep :credit_card, :billing_address, :phone_number
|
770
|
+
dep :billing_address, :credit_card
|
771
|
+
end
|
772
|
+
|
773
|
+
schema.validate!({}) # => Schemacop::Exceptions::ValidationError: /name: Value must be given.
|
774
|
+
schema.validate!({name: 'Joe Doe'}) # => {"name"=>"Joe Doe"}
|
775
|
+
schema.validate!({
|
776
|
+
name: 'Joe Doe',
|
777
|
+
billing_address: 'Street 42'
|
778
|
+
})
|
779
|
+
# => Schemacop::Exceptions::ValidationError: /: Missing property "credit_card" because "billing_address" is given.
|
780
|
+
|
781
|
+
schema.validate!({
|
782
|
+
name: 'Joe Doe',
|
783
|
+
credit_card: 'XXXX XXXX XXXX XXXX X'
|
784
|
+
})
|
785
|
+
# => Schemacop::Exceptions::ValidationError: /: Missing property "billing_address" because "credit_card" is given. /: Missing property "phone_number" because "credit_card" is given.
|
786
|
+
|
787
|
+
schema.validate!({
|
788
|
+
name: 'Joe Doe',
|
789
|
+
billing_address: 'Street 42',
|
790
|
+
phone_number: '000-000-00-00',
|
791
|
+
credit_card: 'XXXX XXXX XXXX XXXX X'
|
792
|
+
})
|
793
|
+
# => {"name"=>"Joe Doe", "credit_card"=>"XXXX XXXX XXXX XXXX X", "billing_address"=>"Street 42", "phone_number"=>"000-000-00-00"}
|
794
|
+
```
|
795
|
+
|
796
|
+
### Object
|
797
|
+
|
798
|
+
Type: `:object`\
|
799
|
+
DSL: `obj`
|
800
|
+
|
801
|
+
The object type represents a ruby `Object`. Please note that the `as_json` method
|
802
|
+
on nodes of this type will just return `{}` (an empty JSON object), as there isn't
|
803
|
+
a useful way to represent a ruby object without conflicting with the `Hash` type.
|
804
|
+
If you want to represent an JSON object, you should use the `Hash` node.
|
805
|
+
|
806
|
+
In the most basic form, this node will accept anything:
|
807
|
+
|
808
|
+
```ruby
|
809
|
+
schema = Schemacop::Schema3.new :object
|
810
|
+
|
811
|
+
schema.validate!(nil) # => nil
|
812
|
+
schema.validate!(true) # => true
|
813
|
+
schema.validate!(false) # => false
|
814
|
+
schema.validate!(Object.new) # => #<Object:0x0000556ab4f58dd0>
|
815
|
+
schema.validate!('foo') # => "foo"
|
816
|
+
```
|
817
|
+
|
818
|
+
If you want to limit the allowed classes, you can so so by specifying an array
|
819
|
+
of allowed classes:
|
820
|
+
|
821
|
+
```ruby
|
822
|
+
schema = Schemacop::Schema3.new :object, classes: [String]
|
823
|
+
|
824
|
+
schema.validate!(nil) # => nil
|
825
|
+
schema.validate!(true) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "String".
|
826
|
+
schema.validate!(Object.new) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "String".
|
827
|
+
schema.validate!('foo') # => "foo"
|
828
|
+
schema.validate!('foo'.html_safe) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "String".
|
829
|
+
```
|
830
|
+
|
831
|
+
Here, the node checks if the given value is an instance of any of the given
|
832
|
+
classes with `instance_of?`, i.e. the exact class and not a subclass.
|
833
|
+
|
834
|
+
If you want to allow subclasses, you can specify this by using the `strict` option:
|
835
|
+
|
836
|
+
```ruby
|
837
|
+
schema = Schemacop::Schema3.new :object, classes: [String], strict: false
|
838
|
+
|
839
|
+
schema.validate!(nil) # => nil
|
840
|
+
schema.validate!(true) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "String".
|
841
|
+
schema.validate!(Object.new) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "String".
|
842
|
+
schema.validate!('foo') # => "foo"
|
843
|
+
schema.validate!('foo'.html_safe) # => "foo"
|
844
|
+
```
|
845
|
+
|
846
|
+
If you set the `strict` option to `false`, the check is done using `is_a?` instead of
|
847
|
+
`instance_of?`, which also allows subclasses
|
848
|
+
|
849
|
+
### AllOf
|
850
|
+
|
851
|
+
Type: `:all_of`\
|
852
|
+
DSL: `all_of`
|
853
|
+
|
854
|
+
With the AllOf node you can specify multiple schemas, for which the given value
|
855
|
+
needs to validate against every one:
|
856
|
+
|
857
|
+
```ruby
|
858
|
+
schema = Schemacop::Schema3.new :all_of do
|
859
|
+
str min_length: 2
|
860
|
+
str max_length: 4
|
861
|
+
end
|
862
|
+
|
863
|
+
schema.validate!('foo') # => "foo"
|
864
|
+
schema.validate!('foooo') # => Schemacop::Exceptions::ValidationError: /: Does not match all allOf conditions.
|
865
|
+
```
|
866
|
+
|
867
|
+
Please note that it's possible to create nonsensical schemas with this node, as
|
868
|
+
you can combine multiple schemas which contradict each other:
|
869
|
+
|
870
|
+
```ruby
|
871
|
+
schema = Schemacop::Schema3.new :all_of do
|
872
|
+
str min_length: 4
|
873
|
+
str max_length: 1
|
874
|
+
end
|
875
|
+
|
876
|
+
schema.validate!('foo') # => Schemacop::Exceptions::ValidationError: /: Does not match all allOf conditions.
|
877
|
+
schema.validate!('foooo') # => Schemacop::Exceptions::ValidationError: /: Does not match all allOf conditions.
|
878
|
+
```
|
879
|
+
|
880
|
+
### AnyOf
|
881
|
+
|
882
|
+
Type: `:any_of`\
|
883
|
+
DSL: `any_of`
|
884
|
+
|
885
|
+
Similar to the AllOf node, you can specify multiple schemas, for which the
|
886
|
+
given value needs to validate against at least one of the schemas.
|
887
|
+
|
888
|
+
For example, your value needs to be either a string which is at least 2
|
889
|
+
characters long, or an integer:
|
890
|
+
|
891
|
+
```ruby
|
892
|
+
schema = Schemacop::Schema3.new :any_of do
|
893
|
+
str min_length: 2
|
894
|
+
int
|
895
|
+
end
|
896
|
+
|
897
|
+
schema.validate!('f') # => Schemacop::Exceptions::ValidationError: /: Does not match any anyOf condition.
|
898
|
+
schema.validate!('foo') # => "foo"
|
899
|
+
schema.validate!(42) # => 42
|
900
|
+
```
|
901
|
+
|
902
|
+
Please note that you need to specify at least one item in the AllOf node:
|
903
|
+
|
904
|
+
```ruby
|
905
|
+
Schemacop::Schema3.new :any_of # => Schemacop::Exceptions::InvalidSchemaError: Node "any_of" makes only sense with at least 1 item.
|
906
|
+
```
|
907
|
+
|
908
|
+
### OneOf
|
909
|
+
|
910
|
+
Type: `:one_of`\
|
911
|
+
DSL: `one_of`
|
912
|
+
|
913
|
+
Similar to the AllOf node, you can specify multiple schemas, for which the
|
914
|
+
given value needs to validate against at exaclty one of the schemas. If the
|
915
|
+
given value validates against multiple schemas, the value is invalid.
|
916
|
+
|
917
|
+
For example, if you want an integer which is either a multiple of 2 or 3,
|
918
|
+
but not both (i.e. no multiple of 6), you could do it as follows:
|
919
|
+
|
920
|
+
```ruby
|
921
|
+
schema = Schemacop::Schema3.new :one_of do
|
922
|
+
int multiple_of: 2
|
923
|
+
int multiple_of: 3
|
924
|
+
end
|
925
|
+
|
926
|
+
schema.validate!(2) # => 2
|
927
|
+
schema.validate!(3) # => 3
|
928
|
+
schema.validate!(4) # => 4
|
929
|
+
schema.validate!(5) # => Schemacop::Exceptions::ValidationError: /: Matches 0 definitions but should match exactly 1.
|
930
|
+
schema.validate!(6) # => Schemacop::Exceptions::ValidationError: /: Matches 2 definitions but should match exactly 1.
|
931
|
+
```
|
932
|
+
|
933
|
+
Again, as previously with the AllOf node, you're allowed to create schemas
|
934
|
+
which will not work for any input, e.g. by specifying the same schema twice:
|
935
|
+
|
936
|
+
```ruby
|
937
|
+
schema = Schemacop::Schema3.new :one_of do
|
938
|
+
int multiple_of: 2
|
939
|
+
int multiple_of: 2
|
940
|
+
end
|
941
|
+
|
942
|
+
schema.validate!(2) # => Schemacop::Exceptions::ValidationError: /: Matches 2 definitions but should match exactly 1.
|
943
|
+
schema.validate!(3) # => Schemacop::Exceptions::ValidationError: /: Matches 0 definitions but should match exactly 1.
|
944
|
+
schema.validate!(4) # => Schemacop::Exceptions::ValidationError: /: Matches 2 definitions but should match exactly 1.
|
945
|
+
schema.validate!(5) # => Schemacop::Exceptions::ValidationError: /: Matches 0 definitions but should match exactly 1.
|
946
|
+
schema.validate!(6) # => Schemacop::Exceptions::ValidationError: /: Matches 2 definitions but should match exactly 1.
|
947
|
+
```
|
948
|
+
|
949
|
+
### IsNot
|
950
|
+
|
951
|
+
Type: `:is_not`\
|
952
|
+
DSL: `is_not`
|
953
|
+
|
954
|
+
With the IsNot node, you can specify a schema which the given value must not
|
955
|
+
validate against, i.e. every value which matches the schema will make this node
|
956
|
+
invalid.
|
957
|
+
|
958
|
+
For example, you want anything but the numbers between 3 and 5:
|
959
|
+
|
960
|
+
```ruby
|
961
|
+
schema = Schemacop::Schema3.new :is_not do
|
962
|
+
int minimum: 3, maximum: 5
|
963
|
+
end
|
964
|
+
|
965
|
+
schema.validate!(nil) # => nil
|
966
|
+
schema.validate!(1) # => 1
|
967
|
+
schema.validate!(2) # => 2
|
968
|
+
schema.validate!(3) # => Schemacop::Exceptions::ValidationError: /: Must not match schema: {"type"=>"integer", "minimum"=>3, "maximum"=>5}.
|
969
|
+
schema.validate!('foo') # => "foo"
|
970
|
+
```
|
971
|
+
|
972
|
+
Note that a IsNot node needs exactly one item:
|
973
|
+
|
974
|
+
```ruby
|
975
|
+
schema = Schemacop::Schema3.new :is_not # => Schemacop::Exceptions::InvalidSchemaError: Node "is_not" only allows exactly one item.
|
976
|
+
```
|
977
|
+
|
978
|
+
### Reference
|
979
|
+
|
980
|
+
**Referencing**
|
981
|
+
DSL: `ref`\
|
982
|
+
Type: `reference`
|
983
|
+
|
984
|
+
**Definition**
|
985
|
+
DSL: `scm`
|
986
|
+
|
987
|
+
Finally, with the Reference node, you can define schemas and then later reference
|
988
|
+
them for usage, e.g. when you have a rather long schema which you need at multiple
|
989
|
+
places.
|
990
|
+
|
991
|
+
#### Examples
|
992
|
+
|
993
|
+
For example, let's define an object with an schema called `Address`, which we'll
|
994
|
+
reference multiple times:
|
995
|
+
|
996
|
+
```ruby
|
997
|
+
schema = Schemacop::Schema3.new :hash do
|
998
|
+
scm :Address do
|
999
|
+
str! :street
|
1000
|
+
str! :zip_code
|
1001
|
+
str! :location
|
1002
|
+
str! :country
|
1003
|
+
end
|
1004
|
+
|
1005
|
+
ref! :shipping_address, :Address
|
1006
|
+
ref! :billing_address, :Address
|
1007
|
+
end
|
1008
|
+
|
1009
|
+
schema.validate!({}) # => Schemacop::Exceptions::ValidationError: /shipping_address: Value must be given. /billing_address: Value must be given.
|
1010
|
+
schema.validate!({
|
1011
|
+
shipping_address: 'foo',
|
1012
|
+
billing_address: 42
|
1013
|
+
})
|
1014
|
+
# => Schemacop::Exceptions::ValidationError: /shipping_address: Invalid type, expected "object". /billing_address: Invalid type, expected "object".
|
1015
|
+
|
1016
|
+
schema.validate!({
|
1017
|
+
shipping_address: {
|
1018
|
+
street: 'Example Street 42',
|
1019
|
+
zip_code: '12345',
|
1020
|
+
location: 'London',
|
1021
|
+
country: 'United Kingdom'
|
1022
|
+
},
|
1023
|
+
billing_address: {
|
1024
|
+
street: 'Main St.',
|
1025
|
+
zip_code: '54321',
|
1026
|
+
location: 'Washington DC',
|
1027
|
+
country: 'USA'
|
1028
|
+
}
|
1029
|
+
})
|
1030
|
+
|
1031
|
+
# => {"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"}}
|
1032
|
+
```
|
1033
|
+
|
1034
|
+
Note that if you use the reference node with the long type name `reference`,
|
1035
|
+
e.g. in an array, you need to specify the "name" of the schema in the
|
1036
|
+
`path` option:
|
1037
|
+
|
1038
|
+
```ruby
|
1039
|
+
schema = Schemacop::Schema3.new :array do
|
1040
|
+
scm :User do
|
1041
|
+
str! :first_name
|
1042
|
+
str! :last_name
|
1043
|
+
end
|
1044
|
+
|
1045
|
+
list :reference, path: :User
|
1046
|
+
end
|
1047
|
+
|
1048
|
+
schema.validate!([]) # => []
|
1049
|
+
schema.validate!([{first_name: 'Joe', last_name: 'Doe'}]) # => [{"first_name"=>"Joe", "last_name"=>"Doe"}]
|
1050
|
+
schema.validate!([id: 42, first_name: 'Joe']) # => Schemacop::Exceptions::ValidationError: /[0]/last_name: Value must be given. /[0]: Obsolete property "id".
|
1051
|
+
```
|
1052
|
+
|
1053
|
+
## Context
|
1054
|
+
|
1055
|
+
Schemacop als features the concept of a `Context`. You can define schemas in a
|
1056
|
+
context, and then reference them in other schemas in that context. This is e.g.
|
1057
|
+
useful if you need a part of the schema to be different depending on the
|
1058
|
+
business action.
|
1059
|
+
|
1060
|
+
Examples:
|
1061
|
+
|
1062
|
+
```ruby
|
1063
|
+
# Define a new context
|
1064
|
+
context = Schemacop::V3::Context.new
|
1065
|
+
|
1066
|
+
# Define the :Person schema in that context
|
1067
|
+
context.schema :Person do
|
1068
|
+
str! :first_name
|
1069
|
+
str! :last_name
|
1070
|
+
ref? :info, :PersonInfo
|
1071
|
+
end
|
1072
|
+
|
1073
|
+
# And also define the :PersonInfo schema in that context
|
1074
|
+
context.schema :PersonInfo do
|
1075
|
+
str! :born_at, format: :date
|
1076
|
+
end
|
1077
|
+
|
1078
|
+
# Now we can define our general schema, where we reference the :Person schema.
|
1079
|
+
# Note that at this point, we don't know what's in the :Person sche,a
|
1080
|
+
schema = Schemacop::Schema3.new :reference, path: :Person
|
1081
|
+
|
1082
|
+
# Validate the data in the context we defined before, where we need the first_name
|
1083
|
+
# and last_name of a person, as well as an optional info hash with the born_at date
|
1084
|
+
# of the person.
|
1085
|
+
Schemacop.with_context context do
|
1086
|
+
schema.validate!({first_name: 'Joe', last_name: 'Doe', info: { born_at: '1980-01-01'} })
|
1087
|
+
# => {"first_name"=>"Joe", "last_name"=>"Doe", "info"=>{"born_at"=>Tue, 01 Jan 1980}}
|
1088
|
+
end
|
1089
|
+
|
1090
|
+
# Now we might want another context, where the person is more anonymous, and as
|
1091
|
+
# such, we need another schema
|
1092
|
+
other_context = Schemacop::V3::Context.new
|
1093
|
+
|
1094
|
+
# Here, we only want the nickname of the person
|
1095
|
+
other_context.schema :Person do
|
1096
|
+
str! :nickname
|
1097
|
+
end
|
1098
|
+
|
1099
|
+
# Finally, validate the data in the new context. We do not want the real name or
|
1100
|
+
# birth date of the person, instead only the nickname is allowed
|
1101
|
+
Schemacop.with_context other_context do
|
1102
|
+
schema.validate!({first_name: 'Joe', last_name: 'Doe', info: { born_at: '1980-01-01'} })
|
1103
|
+
# => Schemacop::Exceptions::ValidationError: /nickname: Value must be given.
|
1104
|
+
# /: Obsolete property "first_name".
|
1105
|
+
# /: Obsolete property "last_name".
|
1106
|
+
# /: Obsolete property "info".
|
1107
|
+
|
1108
|
+
schema.validate!({nickname: 'J.'}) # => {"nickname"=>"J."}
|
1109
|
+
end
|
1110
|
+
```
|
1111
|
+
|
1112
|
+
As one can see, we validated the data against the same schema, but because we
|
1113
|
+
defined the referenced schemas differently in the two contexts, we were able
|
1114
|
+
to use other data in the second context than in the first.
|
1115
|
+
|
1116
|
+
## External schemas
|
1117
|
+
|
1118
|
+
Finally, schemacop features the possibilit to specify schemas in seperate files.
|
1119
|
+
This is especially useful is you have schemas in your application which are used
|
1120
|
+
multiple times through the application.
|
1121
|
+
|
1122
|
+
For each schema, you define the schema in a single file, and after loading the
|
1123
|
+
schemas, you can reference them in other schemas.
|
1124
|
+
|
1125
|
+
The default load path is `'app/schemas'`, but this can be configured by setting
|
1126
|
+
the value of the `load_paths` attribute of the `Schemacop` module.
|
1127
|
+
|
1128
|
+
Please note that the following predescence order is used for the schemas:
|
1129
|
+
|
1130
|
+
```
|
1131
|
+
local schemas > context schemas > global schemas
|
1132
|
+
```
|
1133
|
+
|
1134
|
+
Where:
|
1135
|
+
|
1136
|
+
* local schemas: Defined by using the DSL method? `scm`
|
1137
|
+
* context schemas: Defined in the current context using `context.schema`
|
1138
|
+
* global schemas: Defined in a ruby file in the load path
|
1139
|
+
|
1140
|
+
### Rails applications
|
1141
|
+
|
1142
|
+
In Rails applications, your schemas are automatically eager-laoded from the load
|
1143
|
+
path `'app/schemas'` when your application is started.
|
1144
|
+
|
1145
|
+
After starting your application, you can reference them like normally defined
|
1146
|
+
reference schemas, with the name being relative to the load path.
|
1147
|
+
|
1148
|
+
Example:
|
1149
|
+
|
1150
|
+
You defined the following two schemas in the `'app/schemas'` directory:
|
1151
|
+
|
1152
|
+
```ruby
|
1153
|
+
# app/schemas/user.rb
|
1154
|
+
schema :hash do
|
1155
|
+
str! :first_name
|
1156
|
+
str! :last_name
|
1157
|
+
ary? :groups do
|
1158
|
+
list :reference, path: 'nested/group'
|
1159
|
+
end
|
1160
|
+
end
|
1161
|
+
```
|
1162
|
+
|
1163
|
+
```ruby
|
1164
|
+
# app/schemas/nested/user.rb
|
1165
|
+
schema :hash do
|
1166
|
+
str! :name
|
1167
|
+
end
|
1168
|
+
```
|
1169
|
+
|
1170
|
+
To use the schema, you then can simply reference the schema as with normal
|
1171
|
+
reference schemas:
|
1172
|
+
|
1173
|
+
```ruby
|
1174
|
+
schema = Schemacop::Schema3.new :hash do
|
1175
|
+
ref! :usr, :user
|
1176
|
+
end
|
1177
|
+
|
1178
|
+
schema.validate!({usr: {first_name: 'Joe', last_name: 'Doe'}})
|
1179
|
+
# => {"usr"=>{"first_name"=>"Joe", "last_name"=>"Doe"}}
|
1180
|
+
|
1181
|
+
schema.validate!({usr: {first_name: 'Joe', last_name: 'Doe', groups: []}})
|
1182
|
+
# => {"usr"=>{"first_name"=>"Joe", "last_name"=>"Doe", "groups"=>[]}}
|
1183
|
+
|
1184
|
+
schema.validate!({usr: {first_name: 'Joe', last_name: 'Doe', groups: [{name: 'foo'}, {name: 'bar'}]}})
|
1185
|
+
# => {"usr"=>{"first_name"=>"Joe", "last_name"=>"Doe", "groups"=>[{"name"=>"foo"}, {"name"=>"bar"}]}}
|
1186
|
+
```
|
1187
|
+
|
1188
|
+
### Non-Rails applications
|
1189
|
+
|
1190
|
+
Usage in non-Rails applications is the same as with usage in Rails applications,
|
1191
|
+
however you need to eager load the schemas yourself:
|
1192
|
+
|
1193
|
+
```ruby
|
1194
|
+
Schemacop::V3::GlobalContext.eager_load!
|
1195
|
+
```
|