json_schemer 0.2.18 → 2.1.1
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/.github/workflows/ci.yml +3 -7
 - data/CHANGELOG.md +69 -0
 - data/Gemfile.lock +28 -10
 - data/README.md +379 -4
 - data/bin/hostname_character_classes +42 -0
 - data/bin/rake +29 -0
 - data/exe/json_schemer +62 -0
 - data/json_schemer.gemspec +6 -12
 - data/lib/json_schemer/cached_resolver.rb +16 -0
 - data/lib/json_schemer/content.rb +18 -0
 - data/lib/json_schemer/draft201909/meta.rb +320 -0
 - data/lib/json_schemer/draft201909/vocab/applicator.rb +104 -0
 - data/lib/json_schemer/draft201909/vocab/core.rb +45 -0
 - data/lib/json_schemer/draft201909/vocab.rb +31 -0
 - data/lib/json_schemer/draft202012/meta.rb +364 -0
 - data/lib/json_schemer/draft202012/vocab/applicator.rb +382 -0
 - data/lib/json_schemer/draft202012/vocab/content.rb +52 -0
 - data/lib/json_schemer/draft202012/vocab/core.rb +160 -0
 - data/lib/json_schemer/draft202012/vocab/format_annotation.rb +23 -0
 - data/lib/json_schemer/draft202012/vocab/format_assertion.rb +23 -0
 - data/lib/json_schemer/draft202012/vocab/meta_data.rb +30 -0
 - data/lib/json_schemer/draft202012/vocab/unevaluated.rb +94 -0
 - data/lib/json_schemer/draft202012/vocab/validation.rb +286 -0
 - data/lib/json_schemer/draft202012/vocab.rb +105 -0
 - data/lib/json_schemer/draft4/meta.rb +161 -0
 - data/lib/json_schemer/draft4/vocab/validation.rb +39 -0
 - data/lib/json_schemer/draft4/vocab.rb +18 -0
 - data/lib/json_schemer/draft6/meta.rb +172 -0
 - data/lib/json_schemer/draft6/vocab.rb +16 -0
 - data/lib/json_schemer/draft7/meta.rb +183 -0
 - data/lib/json_schemer/draft7/vocab/validation.rb +69 -0
 - data/lib/json_schemer/draft7/vocab.rb +30 -0
 - data/lib/json_schemer/ecma_regexp.rb +51 -0
 - data/lib/json_schemer/errors.rb +1 -0
 - data/lib/json_schemer/format/duration.rb +23 -0
 - data/lib/json_schemer/format/email.rb +56 -0
 - data/lib/json_schemer/format/hostname.rb +58 -0
 - data/lib/json_schemer/format/json_pointer.rb +18 -0
 - data/lib/json_schemer/format/uri_template.rb +34 -0
 - data/lib/json_schemer/format.rb +129 -109
 - data/lib/json_schemer/keyword.rb +53 -0
 - data/lib/json_schemer/location.rb +25 -0
 - data/lib/json_schemer/openapi.rb +40 -0
 - data/lib/json_schemer/openapi30/document.rb +1672 -0
 - data/lib/json_schemer/openapi30/meta.rb +32 -0
 - data/lib/json_schemer/openapi30/vocab/base.rb +18 -0
 - data/lib/json_schemer/openapi30/vocab.rb +12 -0
 - data/lib/json_schemer/openapi31/document.rb +1557 -0
 - data/lib/json_schemer/openapi31/meta.rb +136 -0
 - data/lib/json_schemer/openapi31/vocab/base.rb +127 -0
 - data/lib/json_schemer/openapi31/vocab.rb +18 -0
 - data/lib/json_schemer/output.rb +56 -0
 - data/lib/json_schemer/result.rb +229 -0
 - data/lib/json_schemer/schema.rb +423 -0
 - data/lib/json_schemer/version.rb +1 -1
 - data/lib/json_schemer.rb +218 -28
 - metadata +98 -25
 - data/lib/json_schemer/cached_ref_resolver.rb +0 -14
 - data/lib/json_schemer/schema/base.rb +0 -658
 - data/lib/json_schemer/schema/draft4.rb +0 -44
 - data/lib/json_schemer/schema/draft6.rb +0 -25
 - data/lib/json_schemer/schema/draft7.rb +0 -32
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 1981782f2342c7123beb0374680037bc11f3be72a8e4be0a213cfb7691643415
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: e8b6c605ba1ce2a2751ac90167f53c27fad975140ab9a0e8328c2b04ce8fe732
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 4d4c6e6c3660560a602b5610559efe37b5839f1142af6486e350bd207cad49e37a00f397d555878957b14951da95745092240bd14417e2ffd02826f053099952
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 8dc561414463ceeacf7bbd0be95be9c441a50919cdb839ff8366fa0268d96d91e4947985bd7bab406f3c163e913bf7ca923a38e22d777d2549af3329f91c14f2
         
     | 
    
        data/.github/workflows/ci.yml
    CHANGED
    
    | 
         @@ -6,21 +6,17 @@ jobs: 
     | 
|
| 
       6 
6 
     | 
    
         
             
                  fail-fast: false
         
     | 
| 
       7 
7 
     | 
    
         
             
                  matrix:
         
     | 
| 
       8 
8 
     | 
    
         
             
                    os: [ubuntu-latest, windows-latest, macos-latest]
         
     | 
| 
       9 
     | 
    
         
            -
                    ruby: [2. 
     | 
| 
      
 9 
     | 
    
         
            +
                    ruby: [2.5, 2.6, 2.7, 3.0, 3.1, 3.2, head, jruby, jruby-head, truffleruby, truffleruby-head]
         
     | 
| 
       10 
10 
     | 
    
         
             
                    exclude:
         
     | 
| 
       11 
     | 
    
         
            -
                      - os: windows-latest
         
     | 
| 
       12 
     | 
    
         
            -
                        ruby: jruby
         
     | 
| 
       13 
     | 
    
         
            -
                      - os: windows-latest
         
     | 
| 
       14 
     | 
    
         
            -
                        ruby: jruby-head
         
     | 
| 
       15 
11 
     | 
    
         
             
                      - os: windows-latest
         
     | 
| 
       16 
12 
     | 
    
         
             
                        ruby: truffleruby
         
     | 
| 
       17 
13 
     | 
    
         
             
                      - os: windows-latest
         
     | 
| 
       18 
14 
     | 
    
         
             
                        ruby: truffleruby-head
         
     | 
| 
       19 
15 
     | 
    
         
             
                runs-on: ${{ matrix.os }}
         
     | 
| 
       20 
16 
     | 
    
         
             
                steps:
         
     | 
| 
       21 
     | 
    
         
            -
                - uses: actions/checkout@ 
     | 
| 
      
 17 
     | 
    
         
            +
                - uses: actions/checkout@v4
         
     | 
| 
       22 
18 
     | 
    
         
             
                - uses: ruby/setup-ruby@v1
         
     | 
| 
       23 
19 
     | 
    
         
             
                  with:
         
     | 
| 
       24 
20 
     | 
    
         
             
                    ruby-version: ${{ matrix.ruby }}
         
     | 
| 
       25 
21 
     | 
    
         
             
                    bundler-cache: true
         
     | 
| 
       26 
     | 
    
         
            -
                - run:  
     | 
| 
      
 22 
     | 
    
         
            +
                - run: bin/rake test
         
     | 
    
        data/CHANGELOG.md
    ADDED
    
    | 
         @@ -0,0 +1,69 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Changelog
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            ## [2.1.1] - 2023-11-28
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            ### Bug Fixes
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            - Fix refs to/through keyword objects: https://github.com/davishmcclurg/json_schemer/pull/160
         
     | 
| 
      
 8 
     | 
    
         
            +
            - Temporary fix for incorrect `uri-reference` format in OpenAPI 3.x: https://github.com/davishmcclurg/json_schemer/pull/161
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            [2.1.1]: https://github.com/davishmcclurg/json_schemer/releases/tag/v2.1.1
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            ## [2.1.0] - 2023-11-17
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            ### Bug Fixes
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            - Limit anyOf/oneOf discriminator to listed refs: https://github.com/davishmcclurg/json_schemer/pull/145
         
     | 
| 
      
 17 
     | 
    
         
            +
            - Require discriminator `propertyName` property: https://github.com/davishmcclurg/json_schemer/pull/145
         
     | 
| 
      
 18 
     | 
    
         
            +
            - Support `Schema#ref` in subschemas: https://github.com/davishmcclurg/json_schemer/pull/145
         
     | 
| 
      
 19 
     | 
    
         
            +
            - Resolve JSON pointer refs using correct base URI: https://github.com/davishmcclurg/json_schemer/pull/147
         
     | 
| 
      
 20 
     | 
    
         
            +
            - `date` format in OpenAPI 3.0: https://github.com/davishmcclurg/json_schemer/commit/69fe7a815ecf0cfb1c40ac402bf46a789c05e972
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            ### Features
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            - Custom error messages with `x-error` keyword and I18n: https://github.com/davishmcclurg/json_schemer/pull/149
         
     | 
| 
      
 25 
     | 
    
         
            +
            - Custom content encodings and media types: https://github.com/davishmcclurg/json_schemer/pull/148
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
            [2.1.0]: https://github.com/davishmcclurg/json_schemer/releases/tag/v2.1.0
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
            ## [2.0.0] - 2023-08-20
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            For 2.0.0, much of the codebase was rewritten to simplify support for the two new JSON Schema draft versions (2019-09 and 2020-12). The major change is moving each keyword into its own class and organizing them into vocabularies. [Output formats](https://json-schema.org/draft/2020-12/json-schema-core.html#section-12) and [annotations](https://json-schema.org/draft/2020-12/json-schema-core.html#section-7.7) from the new drafts are also supported. The known breaking changes are listed below, but there may be others that haven't been identified.
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
            ### Breaking Changes
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
            - The default meta schema is now Draft 2020-12. Other meta schemas can be specified using `meta_schema`.
         
     | 
| 
      
 36 
     | 
    
         
            +
            - Schemas use `json-schemer://schema` as the default base URI. Relative `$id` and `$ref` values are joined to the default base URI and are always absolute. For example, the schema `{ '$id' => 'foo', '$ref' => 'bar' }` uses `json-schemer://schema/foo` as the base URI and passes `json-schemer://schema/bar` to the ref resolver. For relative refs, `URI#path` can be used in the ref resolver to access the relative portion, ie: `URI('json-schemer://schema/bar').path => "/bar"`.
         
     | 
| 
      
 37 
     | 
    
         
            +
            - Property validation hooks (`before_property_validation` and `after_property_validation`) run immediately before and after `properties` validation. Previously, `before_property_validation` ran before all "object" validations (`dependencies`, `patternProperties`, `additionalProperties`, etc) and `after_property_validation` was called after them.
         
     | 
| 
      
 38 
     | 
    
         
            +
            - `insert_property_defaults` now inserts defaults in conditional subschemas when possible (if there's only one default or if there's only one unique default from a valid subtree).
         
     | 
| 
      
 39 
     | 
    
         
            +
            - Error output
         
     | 
| 
      
 40 
     | 
    
         
            +
              - Special characters in `schema_pointer` are no longer percent encoded (eg, `definitions/foo\"bar` instead of `/definitions/foo%22bar`)
         
     | 
| 
      
 41 
     | 
    
         
            +
              - Keyword validation order changed so errors may be returned in a different order (eg, `items` errors before `contains`).
         
     | 
| 
      
 42 
     | 
    
         
            +
              - Array `dependencies` return `"type": "dependencies"` errors instead of `"required"` and point to the schema that contains the `dependencies` keyword.
         
     | 
| 
      
 43 
     | 
    
         
            +
              - `not` errors point to the schema that contains the `not` keyword (instead of the schema defined by the `not` keyword).
         
     | 
| 
      
 44 
     | 
    
         
            +
              - Custom keyword errors are now always wrapped in regular error hashes. Returned strings are used to set `type`:
         
     | 
| 
      
 45 
     | 
    
         
            +
                  ```
         
     | 
| 
      
 46 
     | 
    
         
            +
                  >> JSONSchemer.schema({ 'x' => 'y' }, :keywords => { 'x' => proc { false } }).validate({}).to_a
         
     | 
| 
      
 47 
     | 
    
         
            +
                  => [{"data"=>{}, "data_pointer"=>"", "schema"=>{"x"=>"y"}, "schema_pointer"=>"", "root_schema"=>{"x"=>"y"}, "type"=>"x"}]
         
     | 
| 
      
 48 
     | 
    
         
            +
                  >> JSONSchemer.schema({ 'x' => 'y' }, :keywords => { 'x' => proc { 'wrong!' } }).validate({}).to_a
         
     | 
| 
      
 49 
     | 
    
         
            +
                  => [{"data"=>{}, "data_pointer"=>"", "schema"=>{"x"=>"y"}, "schema_pointer"=>"", "root_schema"=>{"x"=>"y"}, "type"=>"wrong!"}]
         
     | 
| 
      
 50 
     | 
    
         
            +
                  ```
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
            [2.0.0]: https://github.com/davishmcclurg/json_schemer/releases/tag/v2.0.0
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
            ## [1.0.0] - 2023-05-26
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
            ### Breaking Changes
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
            - Ruby 2.4 is no longer supported.
         
     | 
| 
      
 59 
     | 
    
         
            +
            - The default `regexp_resolver` is now `ruby`, which passes patterns directly to `Regexp`. The previous default, `ecma`, rewrites patterns to behave more like Javascript (ECMA-262) regular expressions:
         
     | 
| 
      
 60 
     | 
    
         
            +
              - Beginning of string: `^` -> `\A`
         
     | 
| 
      
 61 
     | 
    
         
            +
              - End of string: `$` -> `\z`
         
     | 
| 
      
 62 
     | 
    
         
            +
              - Space: `\s` -> `[\t\r\n\f\v\uFEFF\u2029\p{Zs}]`
         
     | 
| 
      
 63 
     | 
    
         
            +
              - Non-space: `\S` -> `[^\t\r\n\f\v\uFEFF\u2029\p{Zs}]`
         
     | 
| 
      
 64 
     | 
    
         
            +
            - Invalid ECMA-262 regular expressions raise `JSONSchemer::InvalidEcmaRegexp` when `regexp_resolver` is set to `ecma`.
         
     | 
| 
      
 65 
     | 
    
         
            +
            - Embedded subschemas (ie, subschemas referenced by `$id`) can only be found under "known" keywords (eg, `definitions`). Previously, the entire schema object was scanned for `$id`.
         
     | 
| 
      
 66 
     | 
    
         
            +
            - Empty fragments are now removed from `$ref` URIs before calling `ref_resolver`.
         
     | 
| 
      
 67 
     | 
    
         
            +
            - Refs that are fragment-only JSON pointers with special characters must use the proper encoding (eg, `"$ref": "#/definitions/some-%7Bid%7D"`).
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
            [1.0.0]: https://github.com/davishmcclurg/json_schemer/releases/tag/v1.0.0
         
     | 
    
        data/Gemfile.lock
    CHANGED
    
    | 
         @@ -1,31 +1,49 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            PATH
         
     | 
| 
       2 
2 
     | 
    
         
             
              remote: .
         
     | 
| 
       3 
3 
     | 
    
         
             
              specs:
         
     | 
| 
       4 
     | 
    
         
            -
                json_schemer ( 
     | 
| 
       5 
     | 
    
         
            -
                  ecma-re-validator (~> 0.3)
         
     | 
| 
      
 4 
     | 
    
         
            +
                json_schemer (2.1.1)
         
     | 
| 
       6 
5 
     | 
    
         
             
                  hana (~> 1.3)
         
     | 
| 
       7 
6 
     | 
    
         
             
                  regexp_parser (~> 2.0)
         
     | 
| 
       8 
     | 
    
         
            -
                   
     | 
| 
      
 7 
     | 
    
         
            +
                  simpleidn (~> 0.2)
         
     | 
| 
       9 
8 
     | 
    
         | 
| 
       10 
9 
     | 
    
         
             
            GEM
         
     | 
| 
       11 
10 
     | 
    
         
             
              remote: https://rubygems.org/
         
     | 
| 
       12 
11 
     | 
    
         
             
              specs:
         
     | 
| 
       13 
     | 
    
         
            -
                 
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
      
 12 
     | 
    
         
            +
                concurrent-ruby (1.2.2)
         
     | 
| 
      
 13 
     | 
    
         
            +
                docile (1.4.0)
         
     | 
| 
       15 
14 
     | 
    
         
             
                hana (1.3.7)
         
     | 
| 
       16 
     | 
    
         
            -
                 
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
                 
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
      
 15 
     | 
    
         
            +
                i18n (1.14.1)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  concurrent-ruby (~> 1.0)
         
     | 
| 
      
 17 
     | 
    
         
            +
                i18n-debug (1.2.0)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  i18n (< 2)
         
     | 
| 
      
 19 
     | 
    
         
            +
                minitest (5.15.0)
         
     | 
| 
      
 20 
     | 
    
         
            +
                rake (13.0.6)
         
     | 
| 
      
 21 
     | 
    
         
            +
                regexp_parser (2.8.2)
         
     | 
| 
      
 22 
     | 
    
         
            +
                simplecov (0.22.0)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  docile (~> 1.1)
         
     | 
| 
      
 24 
     | 
    
         
            +
                  simplecov-html (~> 0.11)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  simplecov_json_formatter (~> 0.1)
         
     | 
| 
      
 26 
     | 
    
         
            +
                simplecov-html (0.12.3)
         
     | 
| 
      
 27 
     | 
    
         
            +
                simplecov_json_formatter (0.1.4)
         
     | 
| 
      
 28 
     | 
    
         
            +
                simpleidn (0.2.1)
         
     | 
| 
      
 29 
     | 
    
         
            +
                  unf (~> 0.1.4)
         
     | 
| 
      
 30 
     | 
    
         
            +
                unf (0.1.4)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  unf_ext
         
     | 
| 
      
 32 
     | 
    
         
            +
                unf (0.1.4-java)
         
     | 
| 
      
 33 
     | 
    
         
            +
                unf_ext (0.0.9.1)
         
     | 
| 
       20 
34 
     | 
    
         | 
| 
       21 
35 
     | 
    
         
             
            PLATFORMS
         
     | 
| 
      
 36 
     | 
    
         
            +
              java
         
     | 
| 
       22 
37 
     | 
    
         
             
              ruby
         
     | 
| 
       23 
38 
     | 
    
         | 
| 
       24 
39 
     | 
    
         
             
            DEPENDENCIES
         
     | 
| 
       25 
40 
     | 
    
         
             
              bundler (~> 2.0)
         
     | 
| 
      
 41 
     | 
    
         
            +
              i18n
         
     | 
| 
      
 42 
     | 
    
         
            +
              i18n-debug
         
     | 
| 
       26 
43 
     | 
    
         
             
              json_schemer!
         
     | 
| 
       27 
44 
     | 
    
         
             
              minitest (~> 5.0)
         
     | 
| 
       28 
45 
     | 
    
         
             
              rake (~> 13.0)
         
     | 
| 
      
 46 
     | 
    
         
            +
              simplecov (~> 0.22)
         
     | 
| 
       29 
47 
     | 
    
         | 
| 
       30 
48 
     | 
    
         
             
            BUNDLED WITH
         
     | 
| 
       31 
     | 
    
         
            -
               2. 
     | 
| 
      
 49 
     | 
    
         
            +
               2.3.25
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -1,6 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # JSONSchemer
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            JSON Schema validator. Supports drafts 4, 6, and  
     | 
| 
      
 3 
     | 
    
         
            +
            JSON Schema validator. Supports drafts 4, 6, 7, 2019-09, 2020-12, OpenAPI 3.0, and OpenAPI 3.1.
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
            ## Installation
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
         @@ -45,7 +45,13 @@ schemer.valid?({ 'abc' => 10 }) 
     | 
|
| 
       45 
45 
     | 
    
         
             
            # error validation (`validate` returns an enumerator)
         
     | 
| 
       46 
46 
     | 
    
         | 
| 
       47 
47 
     | 
    
         
             
            schemer.validate({ 'abc' => 10 }).to_a
         
     | 
| 
       48 
     | 
    
         
            -
            # => [{"data"=>10, 
     | 
| 
      
 48 
     | 
    
         
            +
            # => [{"data"=>10,
         
     | 
| 
      
 49 
     | 
    
         
            +
            #      "data_pointer"=>"/abc",
         
     | 
| 
      
 50 
     | 
    
         
            +
            #      "schema"=>{"type"=>"integer", "minimum"=>11},
         
     | 
| 
      
 51 
     | 
    
         
            +
            #      "schema_pointer"=>"/properties/abc",
         
     | 
| 
      
 52 
     | 
    
         
            +
            #      "root_schema"=>{"type"=>"object", "properties"=>{"abc"=>{"type"=>"integer", "minimum"=>11}}},
         
     | 
| 
      
 53 
     | 
    
         
            +
            #      "type"=>"minimum",
         
     | 
| 
      
 54 
     | 
    
         
            +
            #      "error"=>"number at `/abc` is less than: 11"}]
         
     | 
| 
       49 
55 
     | 
    
         | 
| 
       50 
56 
     | 
    
         
             
            # default property values
         
     | 
| 
       51 
57 
     | 
    
         | 
| 
         @@ -74,6 +80,80 @@ schemer = JSONSchemer.schema(schema) 
     | 
|
| 
       74 
80 
     | 
    
         | 
| 
       75 
81 
     | 
    
         
             
            schema = '{ "type": "integer" }'
         
     | 
| 
       76 
82 
     | 
    
         
             
            schemer = JSONSchemer.schema(schema)
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
            # schema validation
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
            JSONSchemer.valid_schema?({ '$id' => 'valid' })
         
     | 
| 
      
 87 
     | 
    
         
            +
            # => true
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
            JSONSchemer.validate_schema({ '$id' => '#invalid' }).to_a
         
     | 
| 
      
 90 
     | 
    
         
            +
            # => [{"data"=>"#invalid",
         
     | 
| 
      
 91 
     | 
    
         
            +
            #      "data_pointer"=>"/$id",
         
     | 
| 
      
 92 
     | 
    
         
            +
            #      "schema"=>{"$ref"=>"#/$defs/uriReferenceString", "$comment"=>"Non-empty fragments not allowed.", "pattern"=>"^[^#]*#?$"},
         
     | 
| 
      
 93 
     | 
    
         
            +
            #      "schema_pointer"=>"/properties/$id",
         
     | 
| 
      
 94 
     | 
    
         
            +
            #      "root_schema"=>{...meta schema},
         
     | 
| 
      
 95 
     | 
    
         
            +
            #      "type"=>"pattern",
         
     | 
| 
      
 96 
     | 
    
         
            +
            #      "error"=>"string at `/$id` does not match pattern: ^[^#]*#?$"}]
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
            JSONSchemer.schema({ '$id' => 'valid' }).valid_schema?
         
     | 
| 
      
 99 
     | 
    
         
            +
            # => true
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
            JSONSchemer.schema({ '$id' => '#invalid' }).validate_schema.to_a
         
     | 
| 
      
 102 
     | 
    
         
            +
            # => [{"data"=>"#invalid",
         
     | 
| 
      
 103 
     | 
    
         
            +
            #      "data_pointer"=>"/$id",
         
     | 
| 
      
 104 
     | 
    
         
            +
            #      "schema"=>{"$ref"=>"#/$defs/uriReferenceString", "$comment"=>"Non-empty fragments not allowed.", "pattern"=>"^[^#]*#?$"},
         
     | 
| 
      
 105 
     | 
    
         
            +
            #      "schema_pointer"=>"/properties/$id",
         
     | 
| 
      
 106 
     | 
    
         
            +
            #      "root_schema"=>{...meta schema},
         
     | 
| 
      
 107 
     | 
    
         
            +
            #      "type"=>"pattern",
         
     | 
| 
      
 108 
     | 
    
         
            +
            #      "error"=>"string at `/$id` does not match pattern: ^[^#]*#?$"}]
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
            # subschemas
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
            schema = {
         
     | 
| 
      
 113 
     | 
    
         
            +
              'type' => 'integer',
         
     | 
| 
      
 114 
     | 
    
         
            +
              '$defs' => {
         
     | 
| 
      
 115 
     | 
    
         
            +
                'foo' => {
         
     | 
| 
      
 116 
     | 
    
         
            +
                  'type' => 'string'
         
     | 
| 
      
 117 
     | 
    
         
            +
                }
         
     | 
| 
      
 118 
     | 
    
         
            +
              }
         
     | 
| 
      
 119 
     | 
    
         
            +
            }
         
     | 
| 
      
 120 
     | 
    
         
            +
            schemer = JSONSchemer.schema(schema)
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
            schemer.ref('#/$defs/foo').validate(1).to_a
         
     | 
| 
      
 123 
     | 
    
         
            +
            # => [{"data"=>1,
         
     | 
| 
      
 124 
     | 
    
         
            +
            #      "data_pointer"=>"",
         
     | 
| 
      
 125 
     | 
    
         
            +
            #      "schema"=>{"type"=>"string"},
         
     | 
| 
      
 126 
     | 
    
         
            +
            #      "schema_pointer"=>"/$defs/foo",
         
     | 
| 
      
 127 
     | 
    
         
            +
            #      "root_schema"=>{"type"=>"integer", "$defs"=>{"foo"=>{"type"=>"string"}}},
         
     | 
| 
      
 128 
     | 
    
         
            +
            #      "type"=>"string",
         
     | 
| 
      
 129 
     | 
    
         
            +
            #      "error"=>"value at root is not a string"}]
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
            # schema bundling (https://json-schema.org/draft/2020-12/json-schema-core.html#section-9.3)
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
            schema = {
         
     | 
| 
      
 134 
     | 
    
         
            +
              '$id' => 'http://example.com/schema',
         
     | 
| 
      
 135 
     | 
    
         
            +
              'allOf' => [
         
     | 
| 
      
 136 
     | 
    
         
            +
                { '$ref' => 'schema/one' },
         
     | 
| 
      
 137 
     | 
    
         
            +
                { '$ref' => 'schema/two' }
         
     | 
| 
      
 138 
     | 
    
         
            +
              ]
         
     | 
| 
      
 139 
     | 
    
         
            +
            }
         
     | 
| 
      
 140 
     | 
    
         
            +
            refs = {
         
     | 
| 
      
 141 
     | 
    
         
            +
              URI('http://example.com/schema/one') => {
         
     | 
| 
      
 142 
     | 
    
         
            +
                'type' => 'integer'
         
     | 
| 
      
 143 
     | 
    
         
            +
              },
         
     | 
| 
      
 144 
     | 
    
         
            +
              URI('http://example.com/schema/two') => {
         
     | 
| 
      
 145 
     | 
    
         
            +
                'minimum' => 11
         
     | 
| 
      
 146 
     | 
    
         
            +
              }
         
     | 
| 
      
 147 
     | 
    
         
            +
            }
         
     | 
| 
      
 148 
     | 
    
         
            +
            schemer = JSONSchemer.schema(schema, :ref_resolver => refs.to_proc)
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
            schemer.bundle
         
     | 
| 
      
 151 
     | 
    
         
            +
            # => {"$id"=>"http://example.com/schema",
         
     | 
| 
      
 152 
     | 
    
         
            +
            #     "allOf"=>[{"$ref"=>"schema/one"}, {"$ref"=>"schema/two"}],
         
     | 
| 
      
 153 
     | 
    
         
            +
            #     "$schema"=>"https://json-schema.org/draft/2020-12/schema",
         
     | 
| 
      
 154 
     | 
    
         
            +
            #     "$defs"=>
         
     | 
| 
      
 155 
     | 
    
         
            +
            #      {"http://example.com/schema/one"=>{"type"=>"integer", "$id"=>"http://example.com/schema/one", "$schema"=>"https://json-schema.org/draft/2020-12/schema"},
         
     | 
| 
      
 156 
     | 
    
         
            +
            #       "http://example.com/schema/two"=>{"minimum"=>11, "$id"=>"http://example.com/schema/two", "$schema"=>"https://json-schema.org/draft/2020-12/schema"}}}
         
     | 
| 
       77 
157 
     | 
    
         
             
            ```
         
     | 
| 
       78 
158 
     | 
    
         | 
| 
       79 
159 
     | 
    
         
             
            ## Options
         
     | 
| 
         @@ -82,11 +162,55 @@ schemer = JSONSchemer.schema(schema) 
     | 
|
| 
       82 
162 
     | 
    
         
             
            JSONSchemer.schema(
         
     | 
| 
       83 
163 
     | 
    
         
             
              schema,
         
     | 
| 
       84 
164 
     | 
    
         | 
| 
       85 
     | 
    
         
            -
              #  
     | 
| 
      
 165 
     | 
    
         
            +
              # meta schema to use for vocabularies (keyword behavior) and schema validation
         
     | 
| 
      
 166 
     | 
    
         
            +
              # String/JSONSchemer::Schema
         
     | 
| 
      
 167 
     | 
    
         
            +
              # 'https://json-schema.org/draft/2020-12/schema': JSONSchemer.draft202012
         
     | 
| 
      
 168 
     | 
    
         
            +
              # 'https://json-schema.org/draft/2019-09/schema': JSONSchemer.draft201909
         
     | 
| 
      
 169 
     | 
    
         
            +
              # 'http://json-schema.org/draft-07/schema#': JSONSchemer.draft7
         
     | 
| 
      
 170 
     | 
    
         
            +
              # 'http://json-schema.org/draft-06/schema#': JSONSchemer.draft6
         
     | 
| 
      
 171 
     | 
    
         
            +
              # 'http://json-schema.org/draft-04/schema#': JSONSchemer.draft4
         
     | 
| 
      
 172 
     | 
    
         
            +
              # 'http://json-schema.org/schema#': JSONSchemer.draft4
         
     | 
| 
      
 173 
     | 
    
         
            +
              # 'https://spec.openapis.org/oas/3.1/dialect/base': JSONSchemer.openapi31
         
     | 
| 
      
 174 
     | 
    
         
            +
              # 'json-schemer://openapi30/schema': JSONSchemer.openapi30
         
     | 
| 
      
 175 
     | 
    
         
            +
              # default: JSONSchemer.draft202012
         
     | 
| 
      
 176 
     | 
    
         
            +
              meta_schema: 'https://json-schema.org/draft/2020-12/schema',
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
              # validate `format` (https://json-schema.org/draft/2020-12/json-schema-validation.html#section-7)
         
     | 
| 
       86 
179 
     | 
    
         
             
              # true/false
         
     | 
| 
       87 
180 
     | 
    
         
             
              # default: true
         
     | 
| 
       88 
181 
     | 
    
         
             
              format: true,
         
     | 
| 
       89 
182 
     | 
    
         | 
| 
      
 183 
     | 
    
         
            +
              # custom formats
         
     | 
| 
      
 184 
     | 
    
         
            +
              formats: {
         
     | 
| 
      
 185 
     | 
    
         
            +
                'int32' => proc do |instance, _format|
         
     | 
| 
      
 186 
     | 
    
         
            +
                  instance.is_a?(Integer) && instance.bit_length <= 32
         
     | 
| 
      
 187 
     | 
    
         
            +
                end,
         
     | 
| 
      
 188 
     | 
    
         
            +
                # disable specific format
         
     | 
| 
      
 189 
     | 
    
         
            +
                'email' => false
         
     | 
| 
      
 190 
     | 
    
         
            +
              },
         
     | 
| 
      
 191 
     | 
    
         
            +
             
     | 
| 
      
 192 
     | 
    
         
            +
              # custom content encodings
         
     | 
| 
      
 193 
     | 
    
         
            +
              # only `base64` is available by default
         
     | 
| 
      
 194 
     | 
    
         
            +
              content_encodings: {
         
     | 
| 
      
 195 
     | 
    
         
            +
                # return [success, annotation] tuple
         
     | 
| 
      
 196 
     | 
    
         
            +
                'urlsafe_base64' => proc do |instance|
         
     | 
| 
      
 197 
     | 
    
         
            +
                  [true, Base64.urlsafe_decode64(instance)]
         
     | 
| 
      
 198 
     | 
    
         
            +
                rescue
         
     | 
| 
      
 199 
     | 
    
         
            +
                  [false, nil]
         
     | 
| 
      
 200 
     | 
    
         
            +
                end
         
     | 
| 
      
 201 
     | 
    
         
            +
              },
         
     | 
| 
      
 202 
     | 
    
         
            +
             
     | 
| 
      
 203 
     | 
    
         
            +
              # custom content media types
         
     | 
| 
      
 204 
     | 
    
         
            +
              # only `application/json` is available by default
         
     | 
| 
      
 205 
     | 
    
         
            +
              content_media_types: {
         
     | 
| 
      
 206 
     | 
    
         
            +
                # return [success, annotation] tuple
         
     | 
| 
      
 207 
     | 
    
         
            +
                'text/csv' => proc do |instance|
         
     | 
| 
      
 208 
     | 
    
         
            +
                  [true, CSV.parse(instance)]
         
     | 
| 
      
 209 
     | 
    
         
            +
                rescue
         
     | 
| 
      
 210 
     | 
    
         
            +
                  [false, nil]
         
     | 
| 
      
 211 
     | 
    
         
            +
                end
         
     | 
| 
      
 212 
     | 
    
         
            +
              },
         
     | 
| 
      
 213 
     | 
    
         
            +
             
     | 
| 
       90 
214 
     | 
    
         
             
              # insert default property values during validation
         
     | 
| 
       91 
215 
     | 
    
         
             
              # true/false
         
     | 
| 
       92 
216 
     | 
    
         
             
              # default: false
         
     | 
| 
         @@ -110,10 +234,261 @@ JSONSchemer.schema( 
     | 
|
| 
       110 
234 
     | 
    
         
             
              # 'net/http'/proc/lambda/respond_to?(:call)
         
     | 
| 
       111 
235 
     | 
    
         
             
              # 'net/http': proc { |uri| JSON.parse(Net::HTTP.get(uri)) }
         
     | 
| 
       112 
236 
     | 
    
         
             
              # default: proc { |uri| raise UnknownRef, uri.to_s }
         
     | 
| 
       113 
     | 
    
         
            -
              ref_resolver: 'net/http'
         
     | 
| 
      
 237 
     | 
    
         
            +
              ref_resolver: 'net/http',
         
     | 
| 
      
 238 
     | 
    
         
            +
              
         
     | 
| 
      
 239 
     | 
    
         
            +
              # use different method to match regexes
         
     | 
| 
      
 240 
     | 
    
         
            +
              # 'ruby'/'ecma'/proc/lambda/respond_to?(:call)
         
     | 
| 
      
 241 
     | 
    
         
            +
              # 'ruby': proc { |pattern| Regexp.new(pattern) }
         
     | 
| 
      
 242 
     | 
    
         
            +
              # default: 'ruby'
         
     | 
| 
      
 243 
     | 
    
         
            +
              regexp_resolver: proc do |pattern|
         
     | 
| 
      
 244 
     | 
    
         
            +
                RE2::Regexp.new(pattern)
         
     | 
| 
      
 245 
     | 
    
         
            +
              end,
         
     | 
| 
      
 246 
     | 
    
         
            +
             
     | 
| 
      
 247 
     | 
    
         
            +
              # output formatting (https://json-schema.org/draft/2020-12/json-schema-core.html#section-12)
         
     | 
| 
      
 248 
     | 
    
         
            +
              # 'classic'/'flag'/'basic'/'detailed'/'verbose'
         
     | 
| 
      
 249 
     | 
    
         
            +
              # default: 'classic'
         
     | 
| 
      
 250 
     | 
    
         
            +
              output_format: 'basic',
         
     | 
| 
      
 251 
     | 
    
         
            +
             
     | 
| 
      
 252 
     | 
    
         
            +
              # validate `readOnly`/`writeOnly` keywords (https://spec.openapis.org/oas/v3.0.3#fixed-fields-19)
         
     | 
| 
      
 253 
     | 
    
         
            +
              # 'read'/'write'/nil
         
     | 
| 
      
 254 
     | 
    
         
            +
              # default: nil
         
     | 
| 
      
 255 
     | 
    
         
            +
              access_mode: 'read'
         
     | 
| 
       114 
256 
     | 
    
         
             
            )
         
     | 
| 
       115 
257 
     | 
    
         
             
            ```
         
     | 
| 
       116 
258 
     | 
    
         | 
| 
      
 259 
     | 
    
         
            +
            ## Custom Error Messages
         
     | 
| 
      
 260 
     | 
    
         
            +
             
     | 
| 
      
 261 
     | 
    
         
            +
            Error messages can be customized using the `x-error` keyword and/or [I18n](https://github.com/ruby-i18n/i18n) translations. `x-error` takes precedence if both are defined.
         
     | 
| 
      
 262 
     | 
    
         
            +
             
     | 
| 
      
 263 
     | 
    
         
            +
            ### `x-error` Keyword
         
     | 
| 
      
 264 
     | 
    
         
            +
             
     | 
| 
      
 265 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 266 
     | 
    
         
            +
            # override all errors for a schema
         
     | 
| 
      
 267 
     | 
    
         
            +
            schemer = JSONSchemer.schema({
         
     | 
| 
      
 268 
     | 
    
         
            +
              'type' => 'string',
         
     | 
| 
      
 269 
     | 
    
         
            +
              'x-error' => 'custom error for schema and all keywords'
         
     | 
| 
      
 270 
     | 
    
         
            +
            })
         
     | 
| 
      
 271 
     | 
    
         
            +
             
     | 
| 
      
 272 
     | 
    
         
            +
            schemer.validate(1).first
         
     | 
| 
      
 273 
     | 
    
         
            +
            # => {"data"=>1,
         
     | 
| 
      
 274 
     | 
    
         
            +
            #     "data_pointer"=>"",
         
     | 
| 
      
 275 
     | 
    
         
            +
            #     "schema"=>{"type"=>"string", "x-error"=>"custom error for schema and all keywords"},
         
     | 
| 
      
 276 
     | 
    
         
            +
            #     "schema_pointer"=>"",
         
     | 
| 
      
 277 
     | 
    
         
            +
            #     "root_schema"=>{"type"=>"string", "x-error"=>"custom error for schema and all keywords"},
         
     | 
| 
      
 278 
     | 
    
         
            +
            #     "type"=>"string",
         
     | 
| 
      
 279 
     | 
    
         
            +
            #     "error"=>"custom error for schema and all keywords",
         
     | 
| 
      
 280 
     | 
    
         
            +
            #     "x-error"=>true}
         
     | 
| 
      
 281 
     | 
    
         
            +
             
     | 
| 
      
 282 
     | 
    
         
            +
            schemer.validate(1, :output_format => 'basic')
         
     | 
| 
      
 283 
     | 
    
         
            +
            # => {"valid"=>false,
         
     | 
| 
      
 284 
     | 
    
         
            +
            #     "keywordLocation"=>"",
         
     | 
| 
      
 285 
     | 
    
         
            +
            #     "absoluteKeywordLocation"=>"json-schemer://schema#",
         
     | 
| 
      
 286 
     | 
    
         
            +
            #     "instanceLocation"=>"",
         
     | 
| 
      
 287 
     | 
    
         
            +
            #     "error"=>"custom error for schema and all keywords",
         
     | 
| 
      
 288 
     | 
    
         
            +
            #     "x-error"=>true,
         
     | 
| 
      
 289 
     | 
    
         
            +
            #     "errors"=>#<Enumerator: ...>}
         
     | 
| 
      
 290 
     | 
    
         
            +
             
     | 
| 
      
 291 
     | 
    
         
            +
            # keyword-specific errors
         
     | 
| 
      
 292 
     | 
    
         
            +
            schemer = JSONSchemer.schema({
         
     | 
| 
      
 293 
     | 
    
         
            +
              'type' => 'string',
         
     | 
| 
      
 294 
     | 
    
         
            +
              'minLength' => 10,
         
     | 
| 
      
 295 
     | 
    
         
            +
              'x-error' => {
         
     | 
| 
      
 296 
     | 
    
         
            +
                'type' => 'custom error for `type` keyword',
         
     | 
| 
      
 297 
     | 
    
         
            +
                # special `^` keyword for schema-level error
         
     | 
| 
      
 298 
     | 
    
         
            +
                '^' => 'custom error for schema',
         
     | 
| 
      
 299 
     | 
    
         
            +
                # same behavior as when `x-error` is a string
         
     | 
| 
      
 300 
     | 
    
         
            +
                '*' => 'fallback error for schema and all keywords'
         
     | 
| 
      
 301 
     | 
    
         
            +
              }
         
     | 
| 
      
 302 
     | 
    
         
            +
            })
         
     | 
| 
      
 303 
     | 
    
         
            +
             
     | 
| 
      
 304 
     | 
    
         
            +
            schemer.validate(1).map { _1.fetch('error') }
         
     | 
| 
      
 305 
     | 
    
         
            +
            # => ["custom error for `type` keyword"]
         
     | 
| 
      
 306 
     | 
    
         
            +
             
     | 
| 
      
 307 
     | 
    
         
            +
            schemer.validate('1').map { _1.fetch('error') }
         
     | 
| 
      
 308 
     | 
    
         
            +
            # => ["custom error for schema and all keywords"]
         
     | 
| 
      
 309 
     | 
    
         
            +
             
     | 
| 
      
 310 
     | 
    
         
            +
            schemer.validate(1, :output_format => 'basic').fetch('error')
         
     | 
| 
      
 311 
     | 
    
         
            +
            # => "custom error for schema"
         
     | 
| 
      
 312 
     | 
    
         
            +
             
     | 
| 
      
 313 
     | 
    
         
            +
            # variable interpolation (instance/instanceLocation/keywordLocation/absoluteKeywordLocation)
         
     | 
| 
      
 314 
     | 
    
         
            +
            schemer = JSONSchemer.schema({
         
     | 
| 
      
 315 
     | 
    
         
            +
              '$id' => 'https://example.com/schema',
         
     | 
| 
      
 316 
     | 
    
         
            +
              'properties' => {
         
     | 
| 
      
 317 
     | 
    
         
            +
                'abc' => {
         
     | 
| 
      
 318 
     | 
    
         
            +
                  'type' => 'string',
         
     | 
| 
      
 319 
     | 
    
         
            +
                  'x-error' => <<~ERROR
         
     | 
| 
      
 320 
     | 
    
         
            +
                    instance: %{instance}
         
     | 
| 
      
 321 
     | 
    
         
            +
                    instance location: %{instanceLocation}
         
     | 
| 
      
 322 
     | 
    
         
            +
                    keyword location: %{keywordLocation}
         
     | 
| 
      
 323 
     | 
    
         
            +
                    absolute keyword location: %{absoluteKeywordLocation}
         
     | 
| 
      
 324 
     | 
    
         
            +
                  ERROR
         
     | 
| 
      
 325 
     | 
    
         
            +
                }
         
     | 
| 
      
 326 
     | 
    
         
            +
              }
         
     | 
| 
      
 327 
     | 
    
         
            +
            })
         
     | 
| 
      
 328 
     | 
    
         
            +
             
     | 
| 
      
 329 
     | 
    
         
            +
            puts schemer.validate({ 'abc' => 1 }).first.fetch('error')
         
     | 
| 
      
 330 
     | 
    
         
            +
            # instance: 1
         
     | 
| 
      
 331 
     | 
    
         
            +
            # instance location: /abc
         
     | 
| 
      
 332 
     | 
    
         
            +
            # keyword location: /properties/abc/type
         
     | 
| 
      
 333 
     | 
    
         
            +
            # absolute keyword location: https://example.com/schema#/properties/abc/type
         
     | 
| 
      
 334 
     | 
    
         
            +
            ```
         
     | 
| 
      
 335 
     | 
    
         
            +
             
     | 
| 
      
 336 
     | 
    
         
            +
            ### I18n
         
     | 
| 
      
 337 
     | 
    
         
            +
             
     | 
| 
      
 338 
     | 
    
         
            +
            When the [I18n gem](https://github.com/ruby-i18n/i18n) is loaded, custom error messages are looked up under the `json_schemer` key. It may be necessary to restart your application after adding the root key because the existence check is cached for performance reasons.
         
     | 
| 
      
 339 
     | 
    
         
            +
             
     | 
| 
      
 340 
     | 
    
         
            +
            Translation keys are looked up in this order:
         
     | 
| 
      
 341 
     | 
    
         
            +
             
     | 
| 
      
 342 
     | 
    
         
            +
            1. `$LOCALE.json_schemer.errors.$ABSOLUTE_KEYWORD_LOCATION`
         
     | 
| 
      
 343 
     | 
    
         
            +
            2. `$LOCALE.json_schemer.errors.$SCHEMA_ID.$KEYWORD_LOCATION`
         
     | 
| 
      
 344 
     | 
    
         
            +
            3. `$LOCALE.json_schemer.errors.$KEYWORD_LOCATION`
         
     | 
| 
      
 345 
     | 
    
         
            +
            4. `$LOCALE.json_schemer.errors.$SCHEMA_ID.$KEYWORD`
         
     | 
| 
      
 346 
     | 
    
         
            +
            5. `$LOCALE.json_schemer.errors.$SCHEMA_ID.*`
         
     | 
| 
      
 347 
     | 
    
         
            +
            6. `$LOCALE.json_schemer.errors.$META_SCHEMA_ID.$KEYWORD`
         
     | 
| 
      
 348 
     | 
    
         
            +
            7. `$LOCALE.json_schemer.errors.$META_SCHEMA_ID.*`
         
     | 
| 
      
 349 
     | 
    
         
            +
            8. `$LOCALE.json_schemer.errors.$KEYWORD`
         
     | 
| 
      
 350 
     | 
    
         
            +
            9. `$LOCALE.json_schemer.errors.*`
         
     | 
| 
      
 351 
     | 
    
         
            +
             
     | 
| 
      
 352 
     | 
    
         
            +
            Example translations file:
         
     | 
| 
      
 353 
     | 
    
         
            +
             
     | 
| 
      
 354 
     | 
    
         
            +
            ```yaml
         
     | 
| 
      
 355 
     | 
    
         
            +
            en:
         
     | 
| 
      
 356 
     | 
    
         
            +
              json_schemer:
         
     | 
| 
      
 357 
     | 
    
         
            +
                errors:
         
     | 
| 
      
 358 
     | 
    
         
            +
                  'https://example.com/schema#/properties/abc/type': custom error for absolute keyword location
         
     | 
| 
      
 359 
     | 
    
         
            +
                  'https://example.com/schema':
         
     | 
| 
      
 360 
     | 
    
         
            +
                    '#/properties/abc/type': custom error for keyword location, nested under schema $id
         
     | 
| 
      
 361 
     | 
    
         
            +
                    'type': custom error for `type` keyword, nested under schema $id
         
     | 
| 
      
 362 
     | 
    
         
            +
                    '^': custom error for schema, nested under schema $id
         
     | 
| 
      
 363 
     | 
    
         
            +
                    '*': fallback error for schema and all keywords, nested under schema $id
         
     | 
| 
      
 364 
     | 
    
         
            +
                  '#/properties/abc/type': custom error for keyword location
         
     | 
| 
      
 365 
     | 
    
         
            +
                  'http://json-schema.org/draft-07/schema#':
         
     | 
| 
      
 366 
     | 
    
         
            +
                    'type': custom error for `type` keyword, nested under meta-schema $id ($schema)
         
     | 
| 
      
 367 
     | 
    
         
            +
                    '^': custom error for schema, nested under meta-schema $id
         
     | 
| 
      
 368 
     | 
    
         
            +
                    '*': fallback error for schema and all keywords, nested under meta-schema $id ($schema)
         
     | 
| 
      
 369 
     | 
    
         
            +
                  'type': custom error for `type` keyword
         
     | 
| 
      
 370 
     | 
    
         
            +
                  '^': custom error for schema
         
     | 
| 
      
 371 
     | 
    
         
            +
                  # variable interpolation (instance/instanceLocation/keywordLocation/absoluteKeywordLocation)
         
     | 
| 
      
 372 
     | 
    
         
            +
                  '*': |
         
     | 
| 
      
 373 
     | 
    
         
            +
                    fallback error for schema and all keywords
         
     | 
| 
      
 374 
     | 
    
         
            +
                    instance: %{instance}
         
     | 
| 
      
 375 
     | 
    
         
            +
                    instance location: %{instanceLocation}
         
     | 
| 
      
 376 
     | 
    
         
            +
                    keyword location: %{keywordLocation}
         
     | 
| 
      
 377 
     | 
    
         
            +
                    absolute keyword location: %{absoluteKeywordLocation}
         
     | 
| 
      
 378 
     | 
    
         
            +
            ```
         
     | 
| 
      
 379 
     | 
    
         
            +
             
     | 
| 
      
 380 
     | 
    
         
            +
            And output:
         
     | 
| 
      
 381 
     | 
    
         
            +
             
     | 
| 
      
 382 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 383 
     | 
    
         
            +
            require 'i18n'
         
     | 
| 
      
 384 
     | 
    
         
            +
            I18n.locale = :en                                         # $LOCALE=en
         
     | 
| 
      
 385 
     | 
    
         
            +
             
     | 
| 
      
 386 
     | 
    
         
            +
            schemer = JSONSchemer.schema({
         
     | 
| 
      
 387 
     | 
    
         
            +
              '$id' => 'https://example.com/schema',                  # $SCHEMA_ID=https://example.com/schema
         
     | 
| 
      
 388 
     | 
    
         
            +
              '$schema' => 'http://json-schema.org/draft-07/schema#', # $META_SCHEMA_ID=http://json-schema.org/draft-07/schema#
         
     | 
| 
      
 389 
     | 
    
         
            +
              'properties' => {
         
     | 
| 
      
 390 
     | 
    
         
            +
                'abc' => {
         
     | 
| 
      
 391 
     | 
    
         
            +
                  'type' => 'integer'                                 # $KEYWORD=type
         
     | 
| 
      
 392 
     | 
    
         
            +
                }                                                     # $KEYWORD_LOCATION=#/properties/abc/type
         
     | 
| 
      
 393 
     | 
    
         
            +
              }                                                       # $ABSOLUTE_KEYWORD_LOCATION=https://example.com/schema#/properties/abc/type
         
     | 
| 
      
 394 
     | 
    
         
            +
            })
         
     | 
| 
      
 395 
     | 
    
         
            +
             
     | 
| 
      
 396 
     | 
    
         
            +
            schemer.validate({ 'abc' => 'not-an-integer' }).first
         
     | 
| 
      
 397 
     | 
    
         
            +
            # => {"data"=>"not-an-integer",
         
     | 
| 
      
 398 
     | 
    
         
            +
            #     "data_pointer"=>"/abc",
         
     | 
| 
      
 399 
     | 
    
         
            +
            #     "schema"=>{"type"=>"integer"},
         
     | 
| 
      
 400 
     | 
    
         
            +
            #     "schema_pointer"=>"/properties/abc",
         
     | 
| 
      
 401 
     | 
    
         
            +
            #     "root_schema"=>{"$id"=>"https://example.com/schema", "$schema"=>"http://json-schema.org/draft-07/schema#", "properties"=>{"abc"=>{"type"=>"integer"}}},
         
     | 
| 
      
 402 
     | 
    
         
            +
            #     "type"=>"integer",
         
     | 
| 
      
 403 
     | 
    
         
            +
            #     "error"=>"custom error for absolute keyword location",
         
     | 
| 
      
 404 
     | 
    
         
            +
            #     "i18n"=>true
         
     | 
| 
      
 405 
     | 
    
         
            +
            ```
         
     | 
| 
      
 406 
     | 
    
         
            +
             
     | 
| 
      
 407 
     | 
    
         
            +
            In the example above, custom error messsages are looked up using the following keys (in order until one is found):
         
     | 
| 
      
 408 
     | 
    
         
            +
             
     | 
| 
      
 409 
     | 
    
         
            +
            1. `en.json_schemer.errors.'https://example.com/schema#/properties/abc/type'`
         
     | 
| 
      
 410 
     | 
    
         
            +
            2. `en.json_schemer.errors.'https://example.com/schema'.'#/properties/abc/type'`
         
     | 
| 
      
 411 
     | 
    
         
            +
            3. `en.json_schemer.errors.'#/properties/abc/type'`
         
     | 
| 
      
 412 
     | 
    
         
            +
            4. `en.json_schemer.errors.'https://example.com/schema'.type`
         
     | 
| 
      
 413 
     | 
    
         
            +
            5. `en.json_schemer.errors.'https://example.com/schema'.*`
         
     | 
| 
      
 414 
     | 
    
         
            +
            6. `en.json_schemer.errors.'http://json-schema.org/draft-07/schema#'.type`
         
     | 
| 
      
 415 
     | 
    
         
            +
            7. `en.json_schemer.errors.'http://json-schema.org/draft-07/schema#'.*`
         
     | 
| 
      
 416 
     | 
    
         
            +
            8. `en.json_schemer.errors.type`
         
     | 
| 
      
 417 
     | 
    
         
            +
            9. `en.json_schemer.errors.*`
         
     | 
| 
      
 418 
     | 
    
         
            +
             
     | 
| 
      
 419 
     | 
    
         
            +
            ## OpenAPI
         
     | 
| 
      
 420 
     | 
    
         
            +
             
     | 
| 
      
 421 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 422 
     | 
    
         
            +
            document = JSONSchemer.openapi({
         
     | 
| 
      
 423 
     | 
    
         
            +
              'openapi' => '3.1.0',
         
     | 
| 
      
 424 
     | 
    
         
            +
              'info' => {
         
     | 
| 
      
 425 
     | 
    
         
            +
                'title' => 'example'
         
     | 
| 
      
 426 
     | 
    
         
            +
              },
         
     | 
| 
      
 427 
     | 
    
         
            +
              'components' => {
         
     | 
| 
      
 428 
     | 
    
         
            +
                'schemas' => {
         
     | 
| 
      
 429 
     | 
    
         
            +
                  'example' => {
         
     | 
| 
      
 430 
     | 
    
         
            +
                    'type' => 'integer'
         
     | 
| 
      
 431 
     | 
    
         
            +
                  }
         
     | 
| 
      
 432 
     | 
    
         
            +
                }
         
     | 
| 
      
 433 
     | 
    
         
            +
              }
         
     | 
| 
      
 434 
     | 
    
         
            +
            })
         
     | 
| 
      
 435 
     | 
    
         
            +
             
     | 
| 
      
 436 
     | 
    
         
            +
            # document validation using meta schema
         
     | 
| 
      
 437 
     | 
    
         
            +
             
     | 
| 
      
 438 
     | 
    
         
            +
            document.valid?
         
     | 
| 
      
 439 
     | 
    
         
            +
            # => false
         
     | 
| 
      
 440 
     | 
    
         
            +
             
     | 
| 
      
 441 
     | 
    
         
            +
            document.validate.to_a
         
     | 
| 
      
 442 
     | 
    
         
            +
            # => [{"data"=>{"title"=>"example"},
         
     | 
| 
      
 443 
     | 
    
         
            +
            #      "data_pointer"=>"/info",
         
     | 
| 
      
 444 
     | 
    
         
            +
            #      "schema"=>{...info schema},
         
     | 
| 
      
 445 
     | 
    
         
            +
            #      "schema_pointer"=>"/$defs/info",
         
     | 
| 
      
 446 
     | 
    
         
            +
            #      "root_schema"=>{...meta schema},
         
     | 
| 
      
 447 
     | 
    
         
            +
            #      "type"=>"required",
         
     | 
| 
      
 448 
     | 
    
         
            +
            #      "details"=>{"missing_keys"=>["version"]}},
         
     | 
| 
      
 449 
     | 
    
         
            +
            #     ...]
         
     | 
| 
      
 450 
     | 
    
         
            +
             
     | 
| 
      
 451 
     | 
    
         
            +
            # data validation using schema by name (in `components/schemas`)
         
     | 
| 
      
 452 
     | 
    
         
            +
             
     | 
| 
      
 453 
     | 
    
         
            +
            document.schema('example').valid?(1)
         
     | 
| 
      
 454 
     | 
    
         
            +
            # => true
         
     | 
| 
      
 455 
     | 
    
         
            +
             
     | 
| 
      
 456 
     | 
    
         
            +
            document.schema('example').valid?('one')
         
     | 
| 
      
 457 
     | 
    
         
            +
            # => false
         
     | 
| 
      
 458 
     | 
    
         
            +
             
     | 
| 
      
 459 
     | 
    
         
            +
            # data validation using schema by ref
         
     | 
| 
      
 460 
     | 
    
         
            +
             
     | 
| 
      
 461 
     | 
    
         
            +
            document.ref('#/components/schemas/example').valid?(1)
         
     | 
| 
      
 462 
     | 
    
         
            +
            # => true
         
     | 
| 
      
 463 
     | 
    
         
            +
             
     | 
| 
      
 464 
     | 
    
         
            +
            document.ref('#/components/schemas/example').valid?('one')
         
     | 
| 
      
 465 
     | 
    
         
            +
            # => false
         
     | 
| 
      
 466 
     | 
    
         
            +
            ```
         
     | 
| 
      
 467 
     | 
    
         
            +
             
     | 
| 
      
 468 
     | 
    
         
            +
            ## CLI
         
     | 
| 
      
 469 
     | 
    
         
            +
             
     | 
| 
      
 470 
     | 
    
         
            +
            The `json_schemer` executable takes a JSON schema file as the first argument followed by one or more JSON data files to validate. If there are any validation errors, it outputs them and returns an error code.
         
     | 
| 
      
 471 
     | 
    
         
            +
             
     | 
| 
      
 472 
     | 
    
         
            +
            Validation errors are output as single-line JSON objects. The `--errors` option can be used to limit the number of errors returned or prevent output entirely (and fail fast).
         
     | 
| 
      
 473 
     | 
    
         
            +
             
     | 
| 
      
 474 
     | 
    
         
            +
            The schema or data can also be read from stdin using `-`.
         
     | 
| 
      
 475 
     | 
    
         
            +
             
     | 
| 
      
 476 
     | 
    
         
            +
            ```
         
     | 
| 
      
 477 
     | 
    
         
            +
            % json_schemer --help
         
     | 
| 
      
 478 
     | 
    
         
            +
            Usage:
         
     | 
| 
      
 479 
     | 
    
         
            +
              json_schemer [options] <schema> <data>...
         
     | 
| 
      
 480 
     | 
    
         
            +
              json_schemer [options] <schema> -
         
     | 
| 
      
 481 
     | 
    
         
            +
              json_schemer [options] - <data>...
         
     | 
| 
      
 482 
     | 
    
         
            +
              json_schemer -h | --help
         
     | 
| 
      
 483 
     | 
    
         
            +
              json_schemer --version
         
     | 
| 
      
 484 
     | 
    
         
            +
             
     | 
| 
      
 485 
     | 
    
         
            +
            Options:
         
     | 
| 
      
 486 
     | 
    
         
            +
              -e, --errors MAX                 Maximum number of errors to output
         
     | 
| 
      
 487 
     | 
    
         
            +
                                               Use "0" to validate with no output
         
     | 
| 
      
 488 
     | 
    
         
            +
              -h, --help                       Show help
         
     | 
| 
      
 489 
     | 
    
         
            +
              -v, --version                    Show version
         
     | 
| 
      
 490 
     | 
    
         
            +
            ```
         
     | 
| 
      
 491 
     | 
    
         
            +
             
     | 
| 
       117 
492 
     | 
    
         
             
            ## Development
         
     | 
| 
       118 
493 
     | 
    
         | 
| 
       119 
494 
     | 
    
         
             
            After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
         
     | 
| 
         @@ -0,0 +1,42 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'open-uri'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'csv'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            # https://datatracker.ietf.org/doc/html/rfc5892#appendix-A.1
         
     | 
| 
      
 7 
     | 
    
         
            +
            # https://datatracker.ietf.org/doc/html/rfc5892#appendix-A.2
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            csv_options = { :col_sep => ';', :skip_blanks => true, :skip_lines => /\A#/ }
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            unicode_data = URI('https://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt')
         
     | 
| 
      
 12 
     | 
    
         
            +
            derived_joining_type = URI('https://www.unicode.org/Public/UCD/latest/ucd/extracted/DerivedJoiningType.txt')
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            # https://www.unicode.org/reports/tr44/#Canonical_Combining_Class_Values
         
     | 
| 
      
 15 
     | 
    
         
            +
            virama_canonical_combining_class = '9'
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            virama_codes = CSV.new(unicode_data.read, **csv_options).select do |code, _name, _category, canonical_combining_class|
         
     | 
| 
      
 18 
     | 
    
         
            +
              canonical_combining_class == virama_canonical_combining_class
         
     | 
| 
      
 19 
     | 
    
         
            +
            end.map(&:first)
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            # https://www.unicode.org/reports/tr44/#Default_Values
         
     | 
| 
      
 22 
     | 
    
         
            +
            # https://www.unicode.org/reports/tr44/#Derived_Extracted
         
     | 
| 
      
 23 
     | 
    
         
            +
            codes_by_joining_type = CSV.new(derived_joining_type.read, **csv_options).group_by do |_code, joining_type|
         
     | 
| 
      
 24 
     | 
    
         
            +
              joining_type.gsub(/#.+/, '').strip
         
     | 
| 
      
 25 
     | 
    
         
            +
            end.transform_values do |rows|
         
     | 
| 
      
 26 
     | 
    
         
            +
              rows.map do |code, _joining_type|
         
     | 
| 
      
 27 
     | 
    
         
            +
                code.strip
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
            end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            def codes_to_character_class(codes)
         
     | 
| 
      
 32 
     | 
    
         
            +
              characters = codes.map do |code|
         
     | 
| 
      
 33 
     | 
    
         
            +
                code.gsub(/(\h+)/, '\u{\1}').gsub('..', '-')
         
     | 
| 
      
 34 
     | 
    
         
            +
              end
         
     | 
| 
      
 35 
     | 
    
         
            +
              "[#{characters.join}]"
         
     | 
| 
      
 36 
     | 
    
         
            +
            end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
            puts "VIRAMA_CHARACTER_CLASS = '#{codes_to_character_class(virama_codes)}'"
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
            codes_by_joining_type.slice('L', 'D', 'T', 'R').each do |joining_type, codes|
         
     | 
| 
      
 41 
     | 
    
         
            +
              puts "JOINING_TYPE_#{joining_type}_CHARACTER_CLASS = '#{codes_to_character_class(codes)}'"
         
     | 
| 
      
 42 
     | 
    
         
            +
            end
         
     | 
    
        data/bin/rake
    ADDED
    
    | 
         @@ -0,0 +1,29 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            #
         
     | 
| 
      
 5 
     | 
    
         
            +
            # This file was generated by Bundler.
         
     | 
| 
      
 6 
     | 
    
         
            +
            #
         
     | 
| 
      
 7 
     | 
    
         
            +
            # The application 'rake' is installed as part of a gem, and
         
     | 
| 
      
 8 
     | 
    
         
            +
            # this file is here to facilitate running it.
         
     | 
| 
      
 9 
     | 
    
         
            +
            #
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            require "pathname"
         
     | 
| 
      
 12 
     | 
    
         
            +
            ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
         
     | 
| 
      
 13 
     | 
    
         
            +
              Pathname.new(__FILE__).realpath)
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            bundle_binstub = File.expand_path("../bundle", __FILE__)
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            if File.file?(bundle_binstub)
         
     | 
| 
      
 18 
     | 
    
         
            +
              if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
         
     | 
| 
      
 19 
     | 
    
         
            +
                load(bundle_binstub)
         
     | 
| 
      
 20 
     | 
    
         
            +
              else
         
     | 
| 
      
 21 
     | 
    
         
            +
                abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
         
     | 
| 
      
 22 
     | 
    
         
            +
            Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
            end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            require "rubygems"
         
     | 
| 
      
 27 
     | 
    
         
            +
            require "bundler/setup"
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
            load Gem.bin_path("rake", "rake")
         
     |