schemacop 3.0.0.rc0 → 3.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/README.md +12 -2
- data/README_V3.md +480 -112
- data/VERSION +1 -1
- data/lib/schemacop/schema2.rb +2 -2
- data/lib/schemacop/scoped_env.rb +3 -3
- data/lib/schemacop/v2/node_supporting_field.rb +25 -11
- data/lib/schemacop/v2/node_supporting_type.rb +2 -2
- data/lib/schemacop/v3/hash_node.rb +23 -2
- data/lib/schemacop/v3/node.rb +1 -1
- data/schemacop.gemspec +3 -3
- data/test/unit/schemacop/v3/hash_node_test.rb +51 -0
- data/test/unit/schemacop/v3/reference_node_test.rb +50 -50
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5caf1b6b11b5c690dc05694cab67e34fb6ecc326381f9d7b63b9c016b5249d9c
|
4
|
+
data.tar.gz: 8b819ad6e32dd2329bd20c0869c216c677b766a5d7374a49f6a9c146d0e743d5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5db8ca31cdf29cea9744529fec11af70dc1c534dc970639931e3a9c287f3005cc578e2d7e9b0525ceefdbcb17d5f01d46d42c0a5b5868b62cabafe2779464ccc
|
7
|
+
data.tar.gz: a12d18800342da6d1d9fdc39bffbec18877872c0cffb62c9b4a41cbd8954b0915c68afeb0b9e03c7e3bdd21f5803fa57daa7deb9cfd49a14363b67586e4e2fc0
|
data/.travis.yml
CHANGED
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.
|
data/README_V3.md
CHANGED
@@ -5,7 +5,7 @@ Please note that Schemacop v3 is still a work in progress, especially the docume
|
|
5
5
|
Use at your own discretion.
|
6
6
|
|
7
7
|
# Table of Contents
|
8
|
-
1. [
|
8
|
+
1. [Introduction](#Introduction)
|
9
9
|
2. [Validation](#validation)
|
10
10
|
3. [Exceptions](#exceptions)
|
11
11
|
4. [Generic Keywords](#generic-keywords)
|
@@ -26,7 +26,7 @@ Use at your own discretion.
|
|
26
26
|
6. [Context](#context)
|
27
27
|
7. [External schemas](#external-schemas)
|
28
28
|
|
29
|
-
##
|
29
|
+
## Introduction
|
30
30
|
|
31
31
|
TODO: Write short section about using schemacop V3
|
32
32
|
|
@@ -307,6 +307,42 @@ It consists of one or multiple values, which can be validated using arbitrary no
|
|
307
307
|
each other, or if there may be duplicate values. By default, this is false,
|
308
308
|
i.e. duplicate values are allowed
|
309
309
|
|
310
|
+
#### Contains
|
311
|
+
|
312
|
+
The `array` node features the contains node, which you can use with the DSL
|
313
|
+
method `cont`. With that DSL method, you can specify a schema which at least
|
314
|
+
one item in the array needs to validate against.
|
315
|
+
|
316
|
+
One usecase for example could be that you want an array of integers, from which
|
317
|
+
at least one must be 5 or larger:
|
318
|
+
|
319
|
+
```ruby
|
320
|
+
schema = Schemacop::Schema3.new :array do
|
321
|
+
list :integer
|
322
|
+
cont :integer, minimum: 5
|
323
|
+
end
|
324
|
+
|
325
|
+
schema.validate!([]) # => Schemacop::Exceptions::ValidationError: /: At least one entry must match schema {"type"=>"integer", "minimum"=>5}.
|
326
|
+
schema.validate!([1, 5]) # => [1, 5]
|
327
|
+
schema.validate!(['foo']) # => Schemacop::Exceptions::ValidationError: /[0]: Invalid type, expected "integer". /: At least one entry must match schema {"type"=>"integer", "minimum"=>5}
|
328
|
+
```
|
329
|
+
|
330
|
+
You can also use it with the tuple validation (see below), e.g. if you want
|
331
|
+
an array of 3 integers, from which at least one needs to be 5 or larger:
|
332
|
+
|
333
|
+
```ruby
|
334
|
+
schema = Schemacop::Schema3.new :array do
|
335
|
+
int
|
336
|
+
int
|
337
|
+
int
|
338
|
+
cont :integer, minimum: 5
|
339
|
+
end
|
340
|
+
|
341
|
+
schema.validate!([]) # => /: Array has 0 items but must have exactly 3. /: At least one entry must match schema {"type"=>"integer", "minimum"=>5}.
|
342
|
+
schema.validate!([1, 2, 3]) # => Schemacop::Exceptions::ValidationError: /: At least one entry must match schema {"type"=>"integer", "minimum"=>5}.
|
343
|
+
schema.validate!([1, 3, 5]) # => [1, 3, 5]
|
344
|
+
```
|
345
|
+
|
310
346
|
#### Specifying properties
|
311
347
|
|
312
348
|
Array nodes support a block in which you can specify the required array contents.
|
@@ -437,11 +473,12 @@ schema = Schemacop::Schema3.new :array do
|
|
437
473
|
str
|
438
474
|
end
|
439
475
|
end
|
440
|
-
```
|
441
|
-
|
442
|
-
#### Contains
|
443
476
|
|
444
|
-
|
477
|
+
schema.validate!([]) # => Schemacop::Exceptions::ValidationError: /: Array has 0 items but must have exactly 1.
|
478
|
+
schema.validate!([1, 2]) # => [1, 2]
|
479
|
+
schema.validate!([1, 'foo']) # => [1, "foo"]
|
480
|
+
schema.validate!([1, :bar]) # => Schemacop::Exceptions::ValidationError: /[1]: Matches 0 definitions but should match exactly 1.
|
481
|
+
```
|
445
482
|
|
446
483
|
### Hash
|
447
484
|
|
@@ -453,7 +490,7 @@ It consists of key-value-pairs that can be validated using arbitrary nodes.
|
|
453
490
|
|
454
491
|
#### Options
|
455
492
|
|
456
|
-
* `additional_properties`
|
493
|
+
* `additional_properties`
|
457
494
|
This option specifies whether additional, unspecified properties are allowed
|
458
495
|
(`true`) or not (`false`). By default, this is `true` if no properties are
|
459
496
|
specified and `false` if you have specified at least one property.
|
@@ -461,7 +498,7 @@ It consists of key-value-pairs that can be validated using arbitrary nodes.
|
|
461
498
|
* `property_names`
|
462
499
|
This option allows to specify a regexp pattern (as string) which validates the
|
463
500
|
keys of any properties that are not specified in the hash. This option only
|
464
|
-
makes sense if `additional_properties` is enabled.
|
501
|
+
makes sense if `additional_properties` is enabled. See below for more informations.
|
465
502
|
|
466
503
|
* `min_properties`
|
467
504
|
Specifies the (inclusive) minimum number of properties a hash must contain.
|
@@ -480,28 +517,35 @@ property, which specifies whether a property is required (`!`) or optional
|
|
480
517
|
(`?`).
|
481
518
|
|
482
519
|
```ruby
|
483
|
-
|
484
|
-
|
520
|
+
schema = Schemacop::Schema3.new :hash do
|
521
|
+
str! :foo # Is a required property
|
522
|
+
int? :bar # Is an optional property
|
523
|
+
end
|
524
|
+
|
525
|
+
schema.validate!({}) # => Schemacop::Exceptions::ValidationError: /foo: Value must be given.
|
526
|
+
schema.validate!({foo: 'str'}) # => {:foo=>"str"}
|
527
|
+
schema.validate!({foo: 'str', bar: 42}) # => {:foo=>"str", :bar=>42}
|
528
|
+
schema.validate!({bar: 42}) # => Schemacop::Exceptions::ValidationError: /foo: Value must be given.
|
485
529
|
```
|
486
530
|
|
487
531
|
##### Pattern properties
|
488
532
|
|
489
|
-
In addition to symbols, property keys can also be a regular expression
|
533
|
+
In addition to symbols, property keys can also be a regular expression. Here,
|
534
|
+
you may only use the optional `?` suffix for the property. This allows any
|
535
|
+
property, which matches the type and the name of the property matches the
|
536
|
+
regular expression.
|
490
537
|
|
491
538
|
```ruby
|
492
|
-
Schemacop::Schema3.new do
|
493
|
-
str! :name
|
494
|
-
|
539
|
+
schema = Schemacop::Schema3.new :hash do
|
495
540
|
# The following statement allows any number of integer properties of which the
|
496
541
|
# name starts with `id_`.
|
497
|
-
int
|
542
|
+
int? /^id_.*$/
|
498
543
|
end
|
499
|
-
```
|
500
544
|
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
{
|
545
|
+
schema.validate!({}) # => {}
|
546
|
+
schema.validate!({id_foo: 1}) # => {:id_foo=>1}
|
547
|
+
schema.validate!({id_foo: 1, id_bar: 2}) # => {:id_foo=>1, :id_bar=>2}
|
548
|
+
schema.validate!({foo: 3}) # => Schemacop::Exceptions::ValidationError: /: Obsolete property "foo".
|
505
549
|
```
|
506
550
|
|
507
551
|
##### Additional properties & property names
|
@@ -511,19 +555,32 @@ additional, unspecified properties. By default, this is turned off if you have
|
|
511
555
|
defined at least one standard property.
|
512
556
|
|
513
557
|
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
|
-
method `add` in the hash-node's body however, you can specify an additional
|
516
|
-
schema to which additional properties must adhere:
|
558
|
+
enable all of them by enabling the option `additional_properties`:
|
517
559
|
|
518
560
|
```ruby
|
519
|
-
|
561
|
+
# This schema will accept any additional properties
|
562
|
+
schema = Schemacop::Schema3.new :hash, additional_properties: true
|
563
|
+
|
564
|
+
schema.validate!({}) # => {}
|
565
|
+
schema.validate!({foo: :bar, baz: 42}) # => {:foo=>:bar, :baz=>42}
|
566
|
+
```
|
567
|
+
|
568
|
+
Using the DSL method `add` in the hash-node's body however, you can specify
|
569
|
+
an additional schema to which additional properties must adhere:
|
570
|
+
|
571
|
+
|
572
|
+
```ruby
|
573
|
+
Schemacop::Schema3.new :hash do
|
520
574
|
int! :id
|
521
575
|
|
522
576
|
# Allow any additional properties besides `id`, but their value must be a
|
523
|
-
# string.
|
524
|
-
|
525
|
-
add :str
|
577
|
+
# string.
|
578
|
+
add :string
|
526
579
|
end
|
580
|
+
|
581
|
+
schema.validate!({id: 1}) # => {:id=>1}
|
582
|
+
schema.validate!({id: 1, foo: 'bar'}) # => {:foo=>"bar", :id=>1}
|
583
|
+
schema.validate!({id: 1, foo: 42}) # => Schemacop::Exceptions::ValidationError: /foo: Invalid type, expected "string".
|
527
584
|
```
|
528
585
|
|
529
586
|
Using the option `property_names`, you can additionaly specify a pattern that
|
@@ -532,13 +589,23 @@ any additional property **keys** must adhere to:
|
|
532
589
|
```ruby
|
533
590
|
# The following schema allows any number of properties, but all keys must
|
534
591
|
# consist of downcase letters from a-z.
|
535
|
-
Schemacop::Schema3.new additional_properties: :true, property_names: '^[a-z]+$'
|
592
|
+
schema = Schemacop::Schema3.new :hash, additional_properties: :true, property_names: '^[a-z]+$'
|
593
|
+
|
594
|
+
|
595
|
+
schema.validate!({}) # => {}
|
596
|
+
schema.validate!({foo: 123}) # => {:foo=>123}
|
597
|
+
schema.validate!({Foo: 'bar'}) # => Schemacop::Exceptions::ValidationError: /: Property name :Foo does not match "^[a-z]+$".
|
536
598
|
|
537
599
|
# The following schema allows any number of properties, but all keys must
|
538
600
|
# consist of downcase letters from a-z AND the properties must be arrays.
|
539
|
-
Schemacop::Schema3.new additional_properties:
|
601
|
+
schema = Schemacop::Schema3.new :hash, additional_properties: true, property_names: '^[a-z]+$' do
|
540
602
|
add :array
|
541
603
|
end
|
604
|
+
|
605
|
+
schema.validate!({}) # => {}
|
606
|
+
schema.validate!({foo: [1, 2, 3]}) # => {:foo=>[1, 2, 3]}
|
607
|
+
schema.validate!({foo: :bar}) # => Schemacop::Exceptions::ValidationError: /foo: Invalid type, expected "array".
|
608
|
+
schema.validate!({Foo: :bar}) # => Schemacop::Exceptions::ValidationError: /: Property name :Foo does not match "^[a-z]+$". /Foo: Invalid type, expected "array".
|
542
609
|
```
|
543
610
|
|
544
611
|
##### Dependencies
|
@@ -549,7 +616,7 @@ Using the DSL method `dep`, you can specifiy (non-nested) property dependencies:
|
|
549
616
|
# In this example, `billing_address` and `phone_number` are required if
|
550
617
|
# `credit_card` is given, and `credit_card` is required if `billing_address` is
|
551
618
|
# given.
|
552
|
-
Schemacop::Schema3.new do
|
619
|
+
schema = Schemacop::Schema3.new :hash do
|
553
620
|
str! :name
|
554
621
|
str? :credit_card
|
555
622
|
str? :billing_address
|
@@ -558,126 +625,427 @@ Schemacop::Schema3.new do
|
|
558
625
|
dep :credit_card, :billing_address, :phone_number
|
559
626
|
dep :billing_address, :credit_card
|
560
627
|
end
|
628
|
+
|
629
|
+
schema.validate!({}) # => Schemacop::Exceptions::ValidationError: /name: Value must be given.
|
630
|
+
schema.validate!({name: 'Joe Doe'}) # => {:name=>"Joe Doe"}
|
631
|
+
schema.validate!({
|
632
|
+
name: 'Joe Doe',
|
633
|
+
billing_address: 'Street 42'
|
634
|
+
})
|
635
|
+
# => Schemacop::Exceptions::ValidationError: /: Missing property "credit_card" because "billing_address" is given.
|
636
|
+
|
637
|
+
schema.validate!({
|
638
|
+
name: 'Joe Doe',
|
639
|
+
credit_card: 'XXXX XXXX XXXX XXXX X'
|
640
|
+
})
|
641
|
+
# => Schemacop::Exceptions::ValidationError: /: Missing property "billing_address" because "credit_card" is given. /: Missing property "phone_number" because "credit_card" is given.
|
642
|
+
|
643
|
+
schema.validate!({
|
644
|
+
name: 'Joe Doe',
|
645
|
+
billing_address: 'Street 42',
|
646
|
+
phone_number: '000-000-00-00',
|
647
|
+
credit_card: 'XXXX XXXX XXXX XXXX X'
|
648
|
+
})
|
649
|
+
# => {:name=>"Joe Doe", :credit_card=>"XXXX XXXX XXXX XXXX X", :billing_address=>"Street 42", :phone_number=>"000-000-00-00"}
|
561
650
|
```
|
562
651
|
|
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
|
652
|
+
### Object
|
572
653
|
|
573
|
-
|
574
|
-
|
654
|
+
Type: `:object`\
|
655
|
+
DSL: `obj`
|
575
656
|
|
576
|
-
|
577
|
-
|
657
|
+
The object type represents a ruby `Object`. Please note that the `as_json? method
|
658
|
+
on nodes of this type will just return `{}` (an empty JSON object), as there isn't
|
659
|
+
a useful way to represent a ruby object without conflicting with the `Hash` type.
|
660
|
+
If you want to represent an JSON object, you should use the `Hash` node.
|
578
661
|
|
579
|
-
|
580
|
-
# in key `additional_addresses`
|
581
|
-
ary! :additional_addresses, default: [] do
|
582
|
-
ref :address
|
583
|
-
end
|
584
|
-
ary? :comments, :array, default: [] { str }
|
662
|
+
In the most basic form, this node will accept anything:
|
585
663
|
|
586
|
-
|
587
|
-
|
588
|
-
hsh! :jobs, min_properties: 1 do
|
589
|
-
str? /^[0-9]+$/
|
590
|
-
end
|
591
|
-
end
|
664
|
+
```ruby
|
665
|
+
schema = Schemacop::Schema3.new :object
|
592
666
|
|
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
|
667
|
+
schema.validate!(nil) # => nil
|
668
|
+
schema.validate!(true) # => true
|
669
|
+
schema.validate!(false) # => false
|
670
|
+
schema.validate!(Object.new) # => #<Object:0x0000556ab4f58dd0>
|
671
|
+
schema.validate!('foo') # => "foo"
|
611
672
|
```
|
612
673
|
|
674
|
+
If you want to limit the allowed classes, you can so so by specifying an array
|
675
|
+
of allowed classes:
|
676
|
+
|
613
677
|
```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
|
678
|
+
schema = Schemacop::Schema3.new :object, classes: [String]
|
623
679
|
|
624
|
-
#
|
625
|
-
Schemacop::
|
680
|
+
schema.validate!(nil) # => nil
|
681
|
+
schema.validate!(true) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "String".
|
682
|
+
schema.validate!(Object.new) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "String".
|
683
|
+
schema.validate!('foo') # => "foo"
|
684
|
+
schema.validate!('foo'.html_safe) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "String".
|
685
|
+
```
|
626
686
|
|
627
|
-
|
628
|
-
|
629
|
-
Schemacop::Schema3.new(additional_properties: true) do
|
630
|
-
int! :id
|
631
|
-
end
|
687
|
+
Here, the node checks if the given value is an instance of any of the given
|
688
|
+
classes with `instance_of?`, i.e. the exact class and not a subclass.
|
632
689
|
|
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
|
690
|
+
If you want to allow subclasses, you can specify this by using the `strict` option:
|
638
691
|
|
639
|
-
|
640
|
-
|
641
|
-
Schemacop::Schema3.new(additional_properties: true, property_names: '^k_.*$') do
|
642
|
-
int! :id
|
643
|
-
add :string
|
644
|
-
end
|
692
|
+
```ruby
|
693
|
+
schema = Schemacop::Schema3.new :object, classes: [String], strict: false
|
645
694
|
|
646
|
-
#
|
647
|
-
#
|
648
|
-
#
|
649
|
-
|
650
|
-
|
651
|
-
str! /^k_.*$/
|
652
|
-
end
|
695
|
+
schema.validate!(nil) # => nil
|
696
|
+
schema.validate!(true) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "String".
|
697
|
+
schema.validate!(Object.new) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "String".
|
698
|
+
schema.validate!('foo') # => "foo"
|
699
|
+
schema.validate!('foo'.html_safe) # => "foo"
|
653
700
|
```
|
654
701
|
|
655
|
-
|
656
|
-
|
657
|
-
Type: `:object`\
|
658
|
-
DSL: `obj`
|
702
|
+
If you set the `strict` option to `false`, the check is done using `is_a?` instead of
|
703
|
+
`instance_of?`, which also allows subclasses
|
659
704
|
|
660
705
|
### AllOf
|
661
706
|
|
662
707
|
Type: `:all_of`\
|
663
708
|
DSL: `all_of`
|
664
709
|
|
710
|
+
With the AllOf node you can specify multiple schemas, for which the given value
|
711
|
+
needs to validate against every one:
|
712
|
+
|
713
|
+
```ruby
|
714
|
+
schema = Schemacop::Schema3.new :all_of do
|
715
|
+
str min_length: 2
|
716
|
+
str max_length: 4
|
717
|
+
end
|
718
|
+
|
719
|
+
schema.validate!('foo') # => "foo"
|
720
|
+
schema.validate!('foooo') # => Schemacop::Exceptions::ValidationError: /: Does not match all allOf conditions.
|
721
|
+
```
|
722
|
+
|
723
|
+
Please note that it's possible to create nonsensical schemas with this node, as
|
724
|
+
you can combine multiple schemas which contradict each other:
|
725
|
+
|
726
|
+
```ruby
|
727
|
+
schema = Schemacop::Schema3.new :all_of do
|
728
|
+
str min_length: 4
|
729
|
+
str max_length: 1
|
730
|
+
end
|
731
|
+
|
732
|
+
schema.validate!('foo') # => Schemacop::Exceptions::ValidationError: /: Does not match all allOf conditions.
|
733
|
+
schema.validate!('foooo') # => Schemacop::Exceptions::ValidationError: /: Does not match all allOf conditions.
|
734
|
+
```
|
735
|
+
|
665
736
|
### AnyOf
|
666
737
|
|
667
738
|
Type: `:any_of`\
|
668
739
|
DSL: `any_of`
|
669
740
|
|
741
|
+
Similar to the AllOf node, you can specify multiple schemas, for which the
|
742
|
+
given value needs to validate against at least one of the schemas.
|
743
|
+
|
744
|
+
For example, your value needs to be either a string which is at least 2
|
745
|
+
characters long, or an integer:
|
746
|
+
|
747
|
+
```ruby
|
748
|
+
schema = Schemacop::Schema3.new :any_of do
|
749
|
+
str min_length: 2
|
750
|
+
int
|
751
|
+
end
|
752
|
+
|
753
|
+
schema.validate!('f') # => Schemacop::Exceptions::ValidationError: /: Does not match any anyOf condition.
|
754
|
+
schema.validate!('foo') # => "foo"
|
755
|
+
schema.validate!(42) # => 42
|
756
|
+
```
|
757
|
+
|
758
|
+
Please note that you need to specify at least one item in the AllOf node:
|
759
|
+
|
760
|
+
```ruby
|
761
|
+
Schemacop::Schema3.new :any_of # => Schemacop::Exceptions::InvalidSchemaError: Node "any_of" makes only sense with at least 1 item.
|
762
|
+
```
|
763
|
+
|
670
764
|
### OneOf
|
671
765
|
|
672
766
|
Type: `:one_of`\
|
673
767
|
DSL: `one_of`
|
674
768
|
|
769
|
+
Similar to the AllOf node, you can specify multiple schemas, for which the
|
770
|
+
given value needs to validate against at exaclty one of the schemas. If the
|
771
|
+
given value validates against multiple schemas, the value is invalid.
|
772
|
+
|
773
|
+
For example, if you want an integer which is either a multiple of 2 or 3,
|
774
|
+
but not both (i.e. no multiple of 6), you could do it as follows:
|
775
|
+
|
776
|
+
```ruby
|
777
|
+
schema = Schemacop::Schema3.new :one_of do
|
778
|
+
int multiple_of: 2
|
779
|
+
int multiple_of: 3
|
780
|
+
end
|
781
|
+
|
782
|
+
schema.validate!(2) # => 2
|
783
|
+
schema.validate!(3) # => 3
|
784
|
+
schema.validate!(4) # => 4
|
785
|
+
schema.validate!(5) # => Schemacop::Exceptions::ValidationError: /: Matches 0 definitions but should match exactly 1.
|
786
|
+
schema.validate!(6) # => Schemacop::Exceptions::ValidationError: /: Matches 2 definitions but should match exactly 1.
|
787
|
+
```
|
788
|
+
|
789
|
+
Again, as previously with the AllOf node, you're allowed to create schemas
|
790
|
+
which will not work for any input, e.g. by specifying the same schema twice:
|
791
|
+
|
792
|
+
```ruby
|
793
|
+
schema = Schemacop::Schema3.new :one_of do
|
794
|
+
int multiple_of: 2
|
795
|
+
int multiple_of: 2
|
796
|
+
end
|
797
|
+
|
798
|
+
schema.validate!(2) # => Schemacop::Exceptions::ValidationError: /: Matches 2 definitions but should match exactly 1.
|
799
|
+
schema.validate!(3) # => Schemacop::Exceptions::ValidationError: /: Matches 0 definitions but should match exactly 1.
|
800
|
+
schema.validate!(4) # => Schemacop::Exceptions::ValidationError: /: Matches 2 definitions but should match exactly 1.
|
801
|
+
schema.validate!(5) # => Schemacop::Exceptions::ValidationError: /: Matches 0 definitions but should match exactly 1.
|
802
|
+
schema.validate!(6) # => Schemacop::Exceptions::ValidationError: /: Matches 2 definitions but should match exactly 1.
|
803
|
+
```
|
804
|
+
|
675
805
|
### IsNot
|
676
806
|
|
677
807
|
Type: `:is_not`\
|
678
808
|
DSL: `is_not`
|
679
809
|
|
810
|
+
With the IsNot node, you can specify a schema which the given value must not
|
811
|
+
validate against, i.e. every value which matches the schema will make this node
|
812
|
+
invalid.
|
813
|
+
|
814
|
+
For example, you want anything but the numbers between 3 and 5:
|
815
|
+
|
816
|
+
```ruby
|
817
|
+
schema = Schemacop::Schema3.new :is_not do
|
818
|
+
int minimum: 3, maximum: 5
|
819
|
+
end
|
820
|
+
|
821
|
+
schema.validate!(nil) # => nil
|
822
|
+
schema.validate!(1) # => 1
|
823
|
+
schema.validate!(2) # => 2
|
824
|
+
schema.validate!(3) # => Schemacop::Exceptions::ValidationError: /: Must not match schema: {"type"=>"integer", "minimum"=>3, "maximum"=>5}.
|
825
|
+
schema.validate!('foo') # => "foo"
|
826
|
+
```
|
827
|
+
|
828
|
+
Note that a IsNot node needs exactly one item:
|
829
|
+
|
830
|
+
```ruby
|
831
|
+
schema = Schemacop::Schema3.new :is_not # => Schemacop::Exceptions::InvalidSchemaError: Node "is_not" only allows exactly one item.
|
832
|
+
```
|
833
|
+
|
680
834
|
### Reference
|
681
835
|
|
682
|
-
|
836
|
+
**Referencing**
|
837
|
+
DSL: `ref`\
|
838
|
+
Type: `reference`
|
839
|
+
|
840
|
+
**Definition**
|
841
|
+
DSL: `scm`
|
842
|
+
|
843
|
+
Finally, with the Reference node, you can define schemas and then later reference
|
844
|
+
them for usage, e.g. when you have a rather long schema which you need at multiple
|
845
|
+
places.
|
846
|
+
|
847
|
+
#### Examples
|
848
|
+
|
849
|
+
For example, let's define an object with an schema called `Address`, which we'll
|
850
|
+
reference multiple times:
|
851
|
+
|
852
|
+
```ruby
|
853
|
+
schema = Schemacop::Schema3.new :hash do
|
854
|
+
scm :Address do
|
855
|
+
str! :street
|
856
|
+
str! :zip_code
|
857
|
+
str! :location
|
858
|
+
str! :country
|
859
|
+
end
|
860
|
+
|
861
|
+
ref! :shipping_address, :Address
|
862
|
+
ref! :billing_address, :Address
|
863
|
+
end
|
864
|
+
|
865
|
+
schema.validate!({}) # => Schemacop::Exceptions::ValidationError: /shipping_address: Value must be given. /billing_address: Value must be given.
|
866
|
+
schema.validate!({
|
867
|
+
shipping_address: 'foo',
|
868
|
+
billing_address: 42
|
869
|
+
})
|
870
|
+
# => Schemacop::Exceptions::ValidationError: /shipping_address: Invalid type, expected "object". /billing_address: Invalid type, expected "object".
|
871
|
+
|
872
|
+
schema.validate!({
|
873
|
+
shipping_address: {
|
874
|
+
street: 'Example Street 42',
|
875
|
+
zip_code: '12345',
|
876
|
+
location: 'London',
|
877
|
+
country: 'United Kingdom'
|
878
|
+
},
|
879
|
+
billing_address: {
|
880
|
+
street: 'Main St.',
|
881
|
+
zip_code: '54321',
|
882
|
+
location: 'Washington DC',
|
883
|
+
country: 'USA'
|
884
|
+
}
|
885
|
+
})
|
886
|
+
|
887
|
+
# => {: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"}}
|
888
|
+
```
|
889
|
+
|
890
|
+
Note that if you use the reference node with the long type name `reference`,
|
891
|
+
e.g. in an array, you need to specify the "name" of the schema in the
|
892
|
+
`path` option:
|
893
|
+
|
894
|
+
```ruby
|
895
|
+
schema = Schemacop::Schema3.new :array do
|
896
|
+
scm :User do
|
897
|
+
str! :first_name
|
898
|
+
str! :last_name
|
899
|
+
end
|
900
|
+
|
901
|
+
list :reference, path: :User
|
902
|
+
end
|
903
|
+
|
904
|
+
schema.validate!([]) # => []
|
905
|
+
schema.validate!([{first_name: 'Joe', last_name: 'Doe'}]) # => [{:first_name=>"Joe", :last_name=>"Doe"}]
|
906
|
+
schema.validate!([id: 42, first_name: 'Joe']) # => Schemacop::Exceptions::ValidationError: /[0]/last_name: Value must be given. /[0]: Obsolete property "id".
|
907
|
+
```
|
908
|
+
|
909
|
+
## Context
|
910
|
+
|
911
|
+
Schemacop als features the concept of a `Context`. You can define schemas in a
|
912
|
+
context, and then reference them in other schemas in that context. This is e.g.
|
913
|
+
useful if you need a part of the schema to be different depending on the
|
914
|
+
business action.
|
915
|
+
|
916
|
+
Examples:
|
917
|
+
|
918
|
+
```ruby
|
919
|
+
# Define a new context
|
920
|
+
context = Schemacop::V3::Context.new
|
921
|
+
|
922
|
+
# Define the :Person schema in that context
|
923
|
+
context.schema :Person do
|
924
|
+
str! :first_name
|
925
|
+
str! :last_name
|
926
|
+
ref? :info, :PersonInfo
|
927
|
+
end
|
928
|
+
|
929
|
+
# And also define the :PersonInfo schema in that context
|
930
|
+
context.schema :PersonInfo do
|
931
|
+
str! :born_at, format: :date
|
932
|
+
end
|
933
|
+
|
934
|
+
# Now we can define our general schema, where we reference the :Person schema.
|
935
|
+
# Note that at this point, we don't know what's in the :Person sche,a
|
936
|
+
schema = Schemacop::Schema3.new :reference, path: :Person
|
937
|
+
|
938
|
+
# Validate the data in the context we defined before, where we need the first_name
|
939
|
+
# and last_name of a person, as well as an optional info hash with the born_at date
|
940
|
+
# of the person.
|
941
|
+
Schemacop.with_context context do
|
942
|
+
schema.validate!({first_name: 'Joe', last_name: 'Doe', info: { born_at: '1980-01-01'} })
|
943
|
+
# => {:first_name=>"Joe", :last_name=>"Doe", :info=>{:born_at=>Tue, 01 Jan 1980}}
|
944
|
+
end
|
945
|
+
|
946
|
+
# Now we might want another context, where the person is more anonymous, and as
|
947
|
+
# such, we need another schema
|
948
|
+
other_context = Schemacop::V3::Context.new
|
949
|
+
|
950
|
+
# Here, we only want the nickname of the person
|
951
|
+
other_context.schema :Person do
|
952
|
+
str! :nickname
|
953
|
+
end
|
954
|
+
|
955
|
+
# Finally, validate the data in the new context. We do not want the real name or
|
956
|
+
# birth date of the person, instead only the nickname is allowed
|
957
|
+
Schemacop.with_context other_context do
|
958
|
+
schema.validate!({first_name: 'Joe', last_name: 'Doe', info: { born_at: '1980-01-01'} })
|
959
|
+
# => Schemacop::Exceptions::ValidationError: /nickname: Value must be given.
|
960
|
+
# /: Obsolete property "first_name".
|
961
|
+
# /: Obsolete property "last_name".
|
962
|
+
# /: Obsolete property "info".
|
963
|
+
|
964
|
+
schema.validate!({nickname: 'J.'}) # => {:nickname=>"J."}
|
965
|
+
end
|
966
|
+
```
|
967
|
+
|
968
|
+
As one can see, we validated the data against the same schema, but because we
|
969
|
+
defined the referenced schemas differently in the two contexts, we were able
|
970
|
+
to use other data in the second context than in the first.
|
971
|
+
|
972
|
+
## External schemas
|
973
|
+
|
974
|
+
Finally, schemacop features the possibilit to specify schemas in seperate files.
|
975
|
+
This is especially useful is you have schemas in your application which are used
|
976
|
+
multiple times through the application.
|
977
|
+
|
978
|
+
For each schema, you define the schema in a single file, and after loading the
|
979
|
+
schemas, you can reference them in other schemas.
|
980
|
+
|
981
|
+
The default load path is `'app/schemas'`, but this can be configured by setting
|
982
|
+
the value of the `load_paths` attribute of the `Schemacop` module.
|
983
|
+
|
984
|
+
Please note that the following predescence order is used for the schemas:
|
985
|
+
|
986
|
+
```
|
987
|
+
local schemas > context schemas > global schemas
|
988
|
+
```
|
989
|
+
|
990
|
+
Where:
|
991
|
+
|
992
|
+
* local schemas: Defined by using the DSL method? `scm`
|
993
|
+
* context schemas: Defined in the current context using `context.schema`
|
994
|
+
* global schemas: Defined in a ruby file in the load path
|
995
|
+
|
996
|
+
### Rails applications
|
683
997
|
|
998
|
+
In Rails applications, your schemas are automatically eager-laoded from the load
|
999
|
+
path `'app/schemas'` when your application is started.
|
1000
|
+
|
1001
|
+
After starting your application, you can reference them like normally defined
|
1002
|
+
reference schemas, with the name being relative to the load path.
|
1003
|
+
|
1004
|
+
Example:
|
1005
|
+
|
1006
|
+
You defined the following two schemas in the `'app/schemas'` directory:
|
1007
|
+
|
1008
|
+
```ruby
|
1009
|
+
# app/schemas/user.rb
|
1010
|
+
schema :hash do
|
1011
|
+
str! :first_name
|
1012
|
+
str! :last_name
|
1013
|
+
ary? :groups do
|
1014
|
+
list :reference, path: 'nested/group'
|
1015
|
+
end
|
1016
|
+
end
|
1017
|
+
```
|
1018
|
+
|
1019
|
+
```ruby
|
1020
|
+
# app/schemas/nested/user.rb
|
1021
|
+
schema :hash do
|
1022
|
+
str! :name
|
1023
|
+
end
|
1024
|
+
```
|
1025
|
+
|
1026
|
+
To use the schema, you then can simply reference the schema as with normal
|
1027
|
+
reference schemas:
|
1028
|
+
|
1029
|
+
```ruby
|
1030
|
+
schema = Schemacop::Schema3.new :hash do
|
1031
|
+
ref! :usr, :user
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
schema.validate!({usr: {first_name: 'Joe', last_name: 'Doe'}})
|
1035
|
+
# => {:usr=>{:first_name=>"Joe", :last_name=>"Doe"}}
|
1036
|
+
|
1037
|
+
schema.validate!({usr: {first_name: 'Joe', last_name: 'Doe', groups: []}})
|
1038
|
+
# => {:usr=>{:first_name=>"Joe", :last_name=>"Doe", :groups=>[]}}
|
1039
|
+
|
1040
|
+
schema.validate!({usr: {first_name: 'Joe', last_name: 'Doe', groups: [{name: 'foo'}, {name: 'bar'}]}})
|
1041
|
+
# => {:usr=>{:first_name=>"Joe", :last_name=>"Doe", :groups=>[{:name=>"foo"}, {:name=>"bar"}]}}
|
1042
|
+
```
|
1043
|
+
|
1044
|
+
### Non-Rails applications
|
1045
|
+
|
1046
|
+
Usage in non-Rails applications is the same as with usage in Rails applications,
|
1047
|
+
however you need to eager load the schemas yourself:
|
1048
|
+
|
1049
|
+
```ruby
|
1050
|
+
Schemacop::V3::GlobalContext.eager_load!
|
1051
|
+
```
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.0.0.
|
1
|
+
3.0.0.rc1
|
data/lib/schemacop/schema2.rb
CHANGED
data/lib/schemacop/scoped_env.rb
CHANGED
@@ -7,14 +7,14 @@ module Schemacop
|
|
7
7
|
@prefix = prefix
|
8
8
|
end
|
9
9
|
|
10
|
-
def method_missing(symbol, *args, &block)
|
10
|
+
def method_missing(symbol, *args, **kwargs, &block)
|
11
11
|
symbol = :"#{@prefix}#{symbol}" if @prefix
|
12
12
|
|
13
13
|
if @methods.include?(symbol)
|
14
14
|
if @delegation_object.respond_to?(symbol)
|
15
|
-
@delegation_object.send(symbol, *args, &block)
|
15
|
+
@delegation_object.send(symbol, *args, **kwargs, &block)
|
16
16
|
elsif @backup_binding.respond_to?(symbol)
|
17
|
-
@backup_binding.send(symbol, *args, &block)
|
17
|
+
@backup_binding.send(symbol, *args, **kwargs, &block)
|
18
18
|
else
|
19
19
|
super
|
20
20
|
end
|
@@ -15,36 +15,50 @@ module Schemacop::V2
|
|
15
15
|
exec_block(&block)
|
16
16
|
end
|
17
17
|
|
18
|
-
def req?(*args, &block)
|
19
|
-
|
18
|
+
def req?(*args, **kwargs, &block)
|
19
|
+
kwargs ||= {}
|
20
|
+
kwargs[:required] = true
|
21
|
+
kwargs[:allow_nil] = true
|
22
|
+
field(*args, **kwargs, &block)
|
20
23
|
end
|
21
24
|
|
22
|
-
def req!(*args, &block)
|
23
|
-
|
25
|
+
def req!(*args, **kwargs, &block)
|
26
|
+
kwargs ||= {}
|
27
|
+
kwargs[:required] = true
|
28
|
+
kwargs[:allow_nil] = false
|
29
|
+
field(*args, **kwargs, &block)
|
24
30
|
end
|
25
31
|
|
26
32
|
alias req req!
|
27
33
|
|
28
|
-
def opt?(*args, &block)
|
29
|
-
|
34
|
+
def opt?(*args, **kwargs, &block)
|
35
|
+
kwargs ||= {}
|
36
|
+
kwargs[:required] = false
|
37
|
+
kwargs[:allow_nil] = true
|
38
|
+
field(*args, **kwargs, &block)
|
30
39
|
end
|
31
40
|
|
32
|
-
def opt!(*args, &block)
|
33
|
-
|
41
|
+
def opt!(*args, **kwargs, &block)
|
42
|
+
kwargs ||= {}
|
43
|
+
kwargs[:required] = false
|
44
|
+
kwargs[:allow_nil] = false
|
45
|
+
field(*args, **kwargs, &block)
|
34
46
|
end
|
35
47
|
|
36
48
|
alias opt opt?
|
37
49
|
|
38
50
|
protected
|
39
51
|
|
40
|
-
def field(*args,
|
52
|
+
def field(*args, **kwargs, &block)
|
41
53
|
name = args.shift
|
54
|
+
required = kwargs.delete(:required)
|
55
|
+
allow_nil = kwargs.delete(:allow_nil)
|
42
56
|
|
43
57
|
if @fields[name]
|
44
|
-
@fields[name].type(*args, &block)
|
58
|
+
@fields[name].type(*args, **kwargs, &block)
|
45
59
|
elsif args.any?
|
46
60
|
@fields[name] = FieldNode.new(name, required) do
|
47
|
-
type(*args, &block)
|
61
|
+
type(*args, **kwargs, &block)
|
48
62
|
end
|
49
63
|
else
|
50
64
|
@fields[name] = FieldNode.new(name, required, &block)
|
@@ -33,7 +33,7 @@ module Schemacop::V2
|
|
33
33
|
super
|
34
34
|
rescue NoMethodError
|
35
35
|
@types = []
|
36
|
-
type :hash,
|
36
|
+
type :hash, &block
|
37
37
|
end
|
38
38
|
|
39
39
|
# required signature:
|
@@ -73,7 +73,7 @@ module Schemacop::V2
|
|
73
73
|
end
|
74
74
|
|
75
75
|
child = klass.new do
|
76
|
-
self.type(*subtypes, options, &block)
|
76
|
+
self.type(*subtypes, **options, &block)
|
77
77
|
end
|
78
78
|
|
79
79
|
# child = klass.build(options)
|
@@ -35,7 +35,7 @@ module Schemacop
|
|
35
35
|
@options[:additional_properties] = create(type, **options, &block)
|
36
36
|
end
|
37
37
|
|
38
|
-
def dsl_dep(source, *targets)
|
38
|
+
def dsl_dep(source, *targets, **_kwargs)
|
39
39
|
@options[:dependencies] ||= {}
|
40
40
|
@options[:dependencies][source] = targets
|
41
41
|
end
|
@@ -165,8 +165,14 @@ module Schemacop
|
|
165
165
|
data ||= default
|
166
166
|
return nil if data.nil?
|
167
167
|
|
168
|
-
|
168
|
+
property_patterns = {}
|
169
|
+
|
169
170
|
@properties.each_value do |prop|
|
171
|
+
if prop.name.is_a?(Regexp)
|
172
|
+
property_patterns[prop.name] = prop
|
173
|
+
next
|
174
|
+
end
|
175
|
+
|
170
176
|
result[prop.name] = prop.cast(data[prop.name])
|
171
177
|
|
172
178
|
if result[prop.name].nil? && !data.include?(prop.name)
|
@@ -174,6 +180,18 @@ module Schemacop
|
|
174
180
|
end
|
175
181
|
end
|
176
182
|
|
183
|
+
# Handle regex properties
|
184
|
+
specified_properties = @properties.keys.to_set
|
185
|
+
additional_properties = data.reject { |k, _v| specified_properties.include?(k.to_s.to_sym) }
|
186
|
+
|
187
|
+
if additional_properties.any? && property_patterns.any?
|
188
|
+
additional_properties.each do |name, additional_property|
|
189
|
+
match_key = property_patterns.keys.find { |p| p.match?(name.to_s) }
|
190
|
+
match = property_patterns[match_key]
|
191
|
+
result[name] = match.cast(additional_property)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
177
195
|
# Handle additional properties
|
178
196
|
if options[:additional_properties] == true
|
179
197
|
result = data.merge(result)
|
@@ -197,6 +215,9 @@ module Schemacop
|
|
197
215
|
def init
|
198
216
|
@properties = {}
|
199
217
|
@options[:type] = :object
|
218
|
+
unless @options[:additional_properties].nil? || @options[:additional_properties].is_a?(TrueClass) || @options[:additional_properties].is_a?(FalseClass)
|
219
|
+
fail Schemacop::Exceptions::InvalidSchemaError, 'Option "additional_properties" must be a boolean value'
|
220
|
+
end
|
200
221
|
end
|
201
222
|
|
202
223
|
def validate_self
|
data/lib/schemacop/v3/node.rb
CHANGED
data/schemacop.gemspec
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
# stub: schemacop 3.0.0.
|
2
|
+
# stub: schemacop 3.0.0.rc1 ruby lib
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "schemacop".freeze
|
6
|
-
s.version = "3.0.0.
|
6
|
+
s.version = "3.0.0.rc1"
|
7
7
|
|
8
8
|
s.required_rubygems_version = Gem::Requirement.new("> 1.3.1".freeze) if s.respond_to? :required_rubygems_version=
|
9
9
|
s.require_paths = ["lib".freeze]
|
10
10
|
s.authors = ["Sitrox".freeze]
|
11
|
-
s.date = "2021-01-
|
11
|
+
s.date = "2021-01-22"
|
12
12
|
s.files = [".gitignore".freeze, ".releaser_config".freeze, ".rubocop.yml".freeze, ".travis.yml".freeze, ".yardopts".freeze, "CHANGELOG.md".freeze, "Gemfile".freeze, "LICENSE".freeze, "README.md".freeze, "README_V2.md".freeze, "README_V3.md".freeze, "RUBY_VERSION".freeze, "Rakefile".freeze, "VERSION".freeze, "lib/schemacop.rb".freeze, "lib/schemacop/base_schema.rb".freeze, "lib/schemacop/exceptions.rb".freeze, "lib/schemacop/railtie.rb".freeze, "lib/schemacop/schema.rb".freeze, "lib/schemacop/schema2.rb".freeze, "lib/schemacop/schema3.rb".freeze, "lib/schemacop/scoped_env.rb".freeze, "lib/schemacop/v2.rb".freeze, "lib/schemacop/v2/caster.rb".freeze, "lib/schemacop/v2/collector.rb".freeze, "lib/schemacop/v2/dupper.rb".freeze, "lib/schemacop/v2/field_node.rb".freeze, "lib/schemacop/v2/node.rb".freeze, "lib/schemacop/v2/node_resolver.rb".freeze, "lib/schemacop/v2/node_supporting_field.rb".freeze, "lib/schemacop/v2/node_supporting_type.rb".freeze, "lib/schemacop/v2/node_with_block.rb".freeze, "lib/schemacop/v2/root_node.rb".freeze, "lib/schemacop/v2/validator/array_validator.rb".freeze, "lib/schemacop/v2/validator/boolean_validator.rb".freeze, "lib/schemacop/v2/validator/float_validator.rb".freeze, "lib/schemacop/v2/validator/hash_validator.rb".freeze, "lib/schemacop/v2/validator/integer_validator.rb".freeze, "lib/schemacop/v2/validator/nil_validator.rb".freeze, "lib/schemacop/v2/validator/number_validator.rb".freeze, "lib/schemacop/v2/validator/object_validator.rb".freeze, "lib/schemacop/v2/validator/string_validator.rb".freeze, "lib/schemacop/v2/validator/symbol_validator.rb".freeze, "lib/schemacop/v3.rb".freeze, "lib/schemacop/v3/all_of_node.rb".freeze, "lib/schemacop/v3/any_of_node.rb".freeze, "lib/schemacop/v3/array_node.rb".freeze, "lib/schemacop/v3/boolean_node.rb".freeze, "lib/schemacop/v3/combination_node.rb".freeze, "lib/schemacop/v3/context.rb".freeze, "lib/schemacop/v3/dsl_scope.rb".freeze, "lib/schemacop/v3/global_context.rb".freeze, "lib/schemacop/v3/hash_node.rb".freeze, "lib/schemacop/v3/integer_node.rb".freeze, "lib/schemacop/v3/is_not_node.rb".freeze, "lib/schemacop/v3/node.rb".freeze, "lib/schemacop/v3/node_registry.rb".freeze, "lib/schemacop/v3/number_node.rb".freeze, "lib/schemacop/v3/numeric_node.rb".freeze, "lib/schemacop/v3/object_node.rb".freeze, "lib/schemacop/v3/one_of_node.rb".freeze, "lib/schemacop/v3/reference_node.rb".freeze, "lib/schemacop/v3/result.rb".freeze, "lib/schemacop/v3/string_node.rb".freeze, "lib/schemacop/v3/symbol_node.rb".freeze, "schemacop.gemspec".freeze, "test/lib/test_helper.rb".freeze, "test/schemas/nested/group.rb".freeze, "test/schemas/user.rb".freeze, "test/unit/schemacop/v2/casting_test.rb".freeze, "test/unit/schemacop/v2/collector_test.rb".freeze, "test/unit/schemacop/v2/custom_check_test.rb".freeze, "test/unit/schemacop/v2/custom_if_test.rb".freeze, "test/unit/schemacop/v2/defaults_test.rb".freeze, "test/unit/schemacop/v2/empty_test.rb".freeze, "test/unit/schemacop/v2/nil_dis_allow_test.rb".freeze, "test/unit/schemacop/v2/node_resolver_test.rb".freeze, "test/unit/schemacop/v2/short_forms_test.rb".freeze, "test/unit/schemacop/v2/types_test.rb".freeze, "test/unit/schemacop/v2/validator_array_test.rb".freeze, "test/unit/schemacop/v2/validator_boolean_test.rb".freeze, "test/unit/schemacop/v2/validator_float_test.rb".freeze, "test/unit/schemacop/v2/validator_hash_test.rb".freeze, "test/unit/schemacop/v2/validator_integer_test.rb".freeze, "test/unit/schemacop/v2/validator_nil_test.rb".freeze, "test/unit/schemacop/v2/validator_number_test.rb".freeze, "test/unit/schemacop/v2/validator_object_test.rb".freeze, "test/unit/schemacop/v2/validator_string_test.rb".freeze, "test/unit/schemacop/v2/validator_symbol_test.rb".freeze, "test/unit/schemacop/v3/all_of_node_test.rb".freeze, "test/unit/schemacop/v3/any_of_node_test.rb".freeze, "test/unit/schemacop/v3/array_node_test.rb".freeze, "test/unit/schemacop/v3/boolean_node_test.rb".freeze, "test/unit/schemacop/v3/global_context_test.rb".freeze, "test/unit/schemacop/v3/hash_node_test.rb".freeze, "test/unit/schemacop/v3/integer_node_test.rb".freeze, "test/unit/schemacop/v3/is_not_node_test.rb".freeze, "test/unit/schemacop/v3/node_test.rb".freeze, "test/unit/schemacop/v3/number_node_test.rb".freeze, "test/unit/schemacop/v3/object_node_test.rb".freeze, "test/unit/schemacop/v3/one_of_node_test.rb".freeze, "test/unit/schemacop/v3/reference_node_test.rb".freeze, "test/unit/schemacop/v3/string_node_test.rb".freeze, "test/unit/schemacop/v3/symbol_node_test.rb".freeze]
|
13
13
|
s.homepage = "https://github.com/sitrox/schemacop".freeze
|
14
14
|
s.licenses = ["MIT".freeze]
|
@@ -61,6 +61,7 @@ module Schemacop
|
|
61
61
|
assert_validation({})
|
62
62
|
assert_validation(foo: :bar)
|
63
63
|
assert_validation('foo' => 'bar')
|
64
|
+
assert_validation(Foo: :bar)
|
64
65
|
assert_validation('_foo39sjfdoi 345893(%' => 'bar', 'foo' => 'bar') do
|
65
66
|
error '/', 'Property name "_foo39sjfdoi 345893(%" does not match "^[a-zA-Z0-9]+$".'
|
66
67
|
end
|
@@ -70,6 +71,24 @@ module Schemacop
|
|
70
71
|
additionalProperties: true,
|
71
72
|
propertyNames: '^[a-zA-Z0-9]+$'
|
72
73
|
)
|
74
|
+
|
75
|
+
assert_cast({ foo: 123 }, { foo: 123 })
|
76
|
+
assert_cast({ Foo: 123 }, { Foo: 123 })
|
77
|
+
|
78
|
+
# New schema
|
79
|
+
schema :hash, additional_properties: true, property_names: '^[a-z]+$'
|
80
|
+
|
81
|
+
assert_validation({})
|
82
|
+
assert_validation(foo: :bar)
|
83
|
+
assert_validation('foo' => 'bar')
|
84
|
+
assert_validation(Foo: :bar) do
|
85
|
+
error '/', 'Property name :Foo does not match "^[a-z]+$".'
|
86
|
+
end
|
87
|
+
assert_validation('_foo39sjfdoi 345893(%' => 'bar', 'foo' => 'bar') do
|
88
|
+
error '/', 'Property name "_foo39sjfdoi 345893(%" does not match "^[a-z]+$".'
|
89
|
+
end
|
90
|
+
|
91
|
+
assert_cast({ foo: 123 }, { foo: 123 })
|
73
92
|
end
|
74
93
|
|
75
94
|
def test_required
|
@@ -254,6 +273,31 @@ module Schemacop
|
|
254
273
|
)
|
255
274
|
end
|
256
275
|
|
276
|
+
def test_pattern_properties_casting
|
277
|
+
schema do
|
278
|
+
int?(/^id_.*$/)
|
279
|
+
int?(/^val.*$/)
|
280
|
+
end
|
281
|
+
|
282
|
+
assert_json({
|
283
|
+
type: :object,
|
284
|
+
patternProperties: {
|
285
|
+
'^id_.*$': { type: :integer },
|
286
|
+
'^val.*$': { type: :integer }
|
287
|
+
},
|
288
|
+
additionalProperties: false
|
289
|
+
})
|
290
|
+
|
291
|
+
assert_validation({})
|
292
|
+
assert_validation({ id_foo: 1 })
|
293
|
+
assert_validation({ id_foo: 1, id_bar: 2 })
|
294
|
+
assert_validation({ id_foo: 1, id_bar: 2, value: 4 })
|
295
|
+
|
296
|
+
assert_cast({ id_foo: 1 }, { id_foo: 1 })
|
297
|
+
assert_cast({ id_foo: 1, id_bar: 2 }, { id_foo: 1, id_bar: 2 })
|
298
|
+
assert_cast({ id_foo: 1, id_bar: 2, value: 4 }, { id_foo: 1, id_bar: 2, value: 4 })
|
299
|
+
end
|
300
|
+
|
257
301
|
def test_defaults
|
258
302
|
schema do
|
259
303
|
str? :first_name, default: 'John'
|
@@ -425,6 +469,13 @@ module Schemacop
|
|
425
469
|
validate_self_should_error((4 + 6i))
|
426
470
|
validate_self_should_error('13')
|
427
471
|
validate_self_should_error('Lorem ipsum')
|
472
|
+
|
473
|
+
# rubocop:disable Lint/BooleanSymbol
|
474
|
+
assert_raises_with_message Exceptions::InvalidSchemaError,
|
475
|
+
'Option "additional_properties" must be a boolean value' do
|
476
|
+
schema :hash, additional_properties: :true
|
477
|
+
end
|
478
|
+
# rubocop:enable Lint/BooleanSymbol
|
428
479
|
end
|
429
480
|
|
430
481
|
def test_doc_example
|
@@ -286,66 +286,66 @@ module Schemacop
|
|
286
286
|
end
|
287
287
|
end
|
288
288
|
|
289
|
-
|
290
|
-
|
289
|
+
def test_external_schemas
|
290
|
+
context = Context.new
|
291
291
|
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
292
|
+
context.schema :Person do
|
293
|
+
str! :first_name
|
294
|
+
str! :last_name
|
295
|
+
ref? :info, :PersonInfo
|
296
|
+
end
|
297
297
|
|
298
|
-
|
299
|
-
|
300
|
-
|
298
|
+
context.schema :PersonInfo do
|
299
|
+
str! :born_at, format: :date
|
300
|
+
end
|
301
301
|
|
302
|
-
|
302
|
+
schema :reference, path: :Person
|
303
303
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
304
|
+
with_context context do
|
305
|
+
assert_validation(first_name: 'John', last_name: 'Doe')
|
306
|
+
assert_validation(first_name: 'John', last_name: 42) do
|
307
|
+
error '/last_name', 'Invalid type, expected "string".'
|
308
|
+
end
|
309
|
+
end
|
310
310
|
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
311
|
+
with_context context do
|
312
|
+
schema do
|
313
|
+
ref! :person, :Person
|
314
|
+
end
|
315
315
|
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
316
|
+
assert_validation(person: { first_name: 'John', last_name: 'Doe' })
|
317
|
+
assert_validation(person: { first_name: 'John', last_name: 'Doe', info: { born_at: '1990-01-13' } })
|
318
|
+
assert_validation(person: { first_name_x: 'John', last_name: 'Doe' }) do
|
319
|
+
error '/person', 'Obsolete property "first_name_x".'
|
320
|
+
error '/person/first_name', 'Value must be given.'
|
321
|
+
end
|
322
|
+
assert_validation(person: { first_name: 'John', last_name: 'Doe', info: { born_at: 'never' } }) do
|
323
|
+
error '/person/info/born_at', 'String does not match format "date".'
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
327
|
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
328
|
+
def test_defaults
|
329
|
+
schema do
|
330
|
+
scm :Person do
|
331
|
+
str? :foo, default: 'bar'
|
332
|
+
end
|
333
|
+
ref? :person, :Person, default: {}
|
334
|
+
end
|
335
335
|
|
336
|
-
|
337
|
-
|
336
|
+
assert_cast({}, person: { foo: 'bar' })
|
337
|
+
end
|
338
338
|
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
339
|
+
def test_casting
|
340
|
+
schema do
|
341
|
+
scm :Person do
|
342
|
+
str! :born_at, format: :date
|
343
|
+
end
|
344
|
+
ref? :person, :Person, default: {}
|
345
|
+
end
|
346
346
|
|
347
|
-
|
348
|
-
|
347
|
+
assert_cast({ person: { born_at: '1990-01-13' } }, person: { born_at: Date.new(1990, 1, 13) })
|
348
|
+
end
|
349
349
|
end
|
350
350
|
end
|
351
351
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: schemacop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.0.
|
4
|
+
version: 3.0.0.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sitrox
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-01-
|
11
|
+
date: 2021-01-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|