schemacop 3.0.0.rc0 → 3.0.0.rc1
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/.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
|