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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d5b9526bd5d67ae7a463ab9f5592fe0d23ae06090061b391f0e3f0e1cea5c6cd
4
- data.tar.gz: d34ec2afccc0ddf874e4875babdbe3e733b5cd2b71cc85993920ca4d473e1e9c
3
+ metadata.gz: 5caf1b6b11b5c690dc05694cab67e34fb6ecc326381f9d7b63b9c016b5249d9c
4
+ data.tar.gz: 8b819ad6e32dd2329bd20c0869c216c677b766a5d7374a49f6a9c146d0e743d5
5
5
  SHA512:
6
- metadata.gz: 8ebed3cb5eaf93f6f60f23231cfb3e88f49c00082641a3ba79531692c77802aa5e59eeb619a7f18c71cb23e8ea3438a5aef95c14769dd62dfd5f3b6d3fd50ec7
7
- data.tar.gz: 184241911ffd97e75d4b9ecedc5d796b0b7269d63bff0b869e011011a382ab3f98ca75fbbdb25d507bab66dc9c0c56213df7212e49616210c15b94f1844f7cd7
6
+ metadata.gz: 5db8ca31cdf29cea9744529fec11af70dc1c534dc970639931e3a9c287f3005cc578e2d7e9b0525ceefdbcb17d5f01d46d42c0a5b5868b62cabafe2779464ccc
7
+ data.tar.gz: a12d18800342da6d1d9fdc39bffbec18877872c0cffb62c9b4a41cbd8954b0915c68afeb0b9e03c7e3bdd21f5803fa57daa7deb9cfd49a14363b67586e4e2fc0
@@ -2,6 +2,7 @@ language: ruby
2
2
  rvm:
3
3
  - 2.6.2
4
4
  - 2.7.1
5
+ - 3.0.0
5
6
  script:
6
7
  - bundle install
7
8
  - bundle exec rake test
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
- * {Schemacop::Exceptions::InvalidSchemaError}
97
+ * `Schemacop::Exceptions::InvalidSchemaError`
88
98
 
89
99
  This exception is thrown when the given schema definition format is invalid.
90
100
 
91
- * {Schemacop::Exceptions::ValidationError}
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.
@@ -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. [Introcution](#Introcution)
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
- ## Introcution
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
- TODO: Describe `cont` DSL method
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` TODO: Check this
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
- str! :my_required_property
484
- int! :my_optional_property
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! /^id_.*$/
542
+ int? /^id_.*$/
498
543
  end
499
- ```
500
544
 
501
- For example, the above example would successfully validate the following hash:
502
-
503
- ```ruby
504
- { name: 'John Doe', id_a: 42, id_b: 42 }
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`. Using the DSL
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
- Schemacop::Schema3.new do
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. Note that using the `add` node, the option `additional_properties`
524
- # is automatically enabled.
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: :true, property_names: '^[a-z]+$' do
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
- #### Examples
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
- int? :id
574
- str! :name
654
+ Type: `:object`\
655
+ DSL: `obj`
575
656
 
576
- # Reference above defined schema 'address' and use it for key 'address'
577
- ref! :address, :address
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
- # Reference above defined schema 'address' and use it as contents for array
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
- # Define a hash with key `jobs` that needs at least one property, and all
587
- # properties must be valid integers and their values must be strings.
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.valid?(
594
- id: 42,
595
- name: 'John Doe',
596
- address: {
597
- street: 'Silver Street',
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
- # The following schema supports exactly the properties defined below, `options`
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
- # Allow any hash with any contents.
625
- Schemacop::Schema3.new(additional_properties: true)
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
- # Allow a hash where `id` is given, but any additional properties of any name
628
- # and any type are supported as well.
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
- # Allow a hash where `id` is given, but any additional properties of which the
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
- # Allow a hash where `id` is given, but any additional properties of which the
640
- # key starts with `k_` and the additional value is a string are allowed.
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
- # Allow a hash where `id` is given, and any additional string properties that start
647
- # with `k_` are allowed. At least one string with key `k_*` must be given though
648
- # as this property is required.
649
- Schemacop::Schema3.new(property_names: '^k_.*$') do
650
- int! :id
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
- ### Object
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
- DSL: `ref`
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.rc0
1
+ 3.0.0.rc1
@@ -1,9 +1,9 @@
1
1
  module Schemacop
2
2
  class Schema2 < BaseSchema
3
- def initialize(*args, &block)
3
+ def initialize(*args, **kwargs, &block)
4
4
  super()
5
5
  @root = V2::HashValidator.new do
6
- req :root, *args, &block
6
+ req :root, *args, **kwargs, &block
7
7
  end
8
8
  end
9
9
 
@@ -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
- field(*args, required: true, allow_nil: true, &block)
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
- field(*args, required: true, allow_nil: false, &block)
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
- field(*args, required: false, allow_nil: true, &block)
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
- field(*args, required: false, allow_nil: false, &block)
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, required:, allow_nil:, &block)
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, {}, &block
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
- # TODO: How to handle regex / etc.?
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
@@ -117,7 +117,7 @@ module Schemacop
117
117
  @schemas[name] = create(type, **options, &block)
118
118
  end
119
119
 
120
- def dsl_node(node)
120
+ def dsl_node(node, *_args, **_kwargs)
121
121
  add_child node
122
122
  end
123
123
 
@@ -1,14 +1,14 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: schemacop 3.0.0.rc0 ruby lib
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.rc0"
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-14"
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
- # def test_external_schemas
290
- # context = Context.new
289
+ def test_external_schemas
290
+ context = Context.new
291
291
 
292
- # context.schema :Person do
293
- # str! :first_name
294
- # str! :last_name
295
- # ref? :info, :PersonInfo
296
- # end
292
+ context.schema :Person do
293
+ str! :first_name
294
+ str! :last_name
295
+ ref? :info, :PersonInfo
296
+ end
297
297
 
298
- # context.schema :PersonInfo do
299
- # str! :born_at, format: :date
300
- # end
298
+ context.schema :PersonInfo do
299
+ str! :born_at, format: :date
300
+ end
301
301
 
302
- # schema :reference, path: :Person
302
+ schema :reference, path: :Person
303
303
 
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
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
- # with_context context do
312
- # schema do
313
- # ref! :person, :Person
314
- # end
311
+ with_context context do
312
+ schema do
313
+ ref! :person, :Person
314
+ end
315
315
 
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
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
- # def test_defaults
329
- # schema do
330
- # scm :Person do
331
- # str? :foo, default: 'bar'
332
- # end
333
- # ref? :person, :Person, default: {}
334
- # end
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
- # assert_cast({}, person: { foo: 'bar' })
337
- # end
336
+ assert_cast({}, person: { foo: 'bar' })
337
+ end
338
338
 
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
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
- # assert_cast({ person: { born_at: '1990-01-13' } }, person: { born_at: Date.new(1990, 1, 13) })
348
- # end
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.rc0
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-14 00:00:00.000000000 Z
11
+ date: 2021-01-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport