File: core.rb

package info (click to toggle)
ruby-json-schemer 2.4.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 544 kB
  • sloc: ruby: 7,428; makefile: 4; sh: 4
file content (160 lines) | stat: -rw-r--r-- 4,971 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# frozen_string_literal: true
module JSONSchemer
  module Draft202012
    module Vocab
      module Core
        class Schema < Keyword
          def parse
            schema.meta_schema = if value == schema.base_uri.to_s
              schema
            else
              META_SCHEMAS_BY_BASE_URI_STR[value] || root.resolve_ref(URI(value))
            end
            value
          end
        end

        class Vocabulary < Keyword
          def parse
            value.each_with_object({}) do |(vocabulary, required), out|
              if VOCABULARIES.key?(vocabulary)
                out[vocabulary] = VOCABULARIES.fetch(vocabulary)
              elsif required
                raise UnknownVocabulary, vocabulary
              end
            end.tap do |vocabularies|
              schema.keywords = vocabularies.sort_by do |vocabulary, _keywords|
                VOCABULARY_ORDER.fetch(vocabulary, Float::INFINITY)
              end.each_with_object({}) do |(_vocabulary, keywords), out|
                out.merge!(keywords)
              end
              schema.keyword_order = schema.keywords.transform_values.with_index { |_keyword_class, index| index }
            end
          end
        end

        class Id < Keyword
          def parse
            URI.join(schema.base_uri, value).tap do |uri|
              schema.base_uri = uri
              root.resources[:lexical][uri] = schema
            end
          end
        end

        class Anchor < Keyword
          def parse
            URI.join(schema.base_uri, "##{value}").tap do |uri|
              root.resources[:lexical][uri] = schema
            end
          end
        end

        class Ref < Keyword
          def self.exclusive?
            false
          end

          def ref_uri
            @ref_uri ||= URI.join(schema.base_uri, value)
          end

          def ref_schema
            @ref_schema ||= root.resolve_ref(ref_uri)
          end

          def validate(instance, instance_location, keyword_location, context)
            ref_schema.validate_instance(instance, instance_location, keyword_location, context)
          end
        end

        class DynamicAnchor < Keyword
          def parse
            URI.join(schema.base_uri, "##{value}").tap do |uri|
              root.resources[:lexical][uri] = schema
              root.resources[:dynamic][uri] = schema
            end
          end
        end

        class DynamicRef < Keyword
          def ref_uri
            @ref_uri ||= URI.join(schema.base_uri, value)
          end

          def ref_schema
            @ref_schema ||= root.resolve_ref(ref_uri)
          end

          def dynamic_anchor
            return @dynamic_anchor if defined?(@dynamic_anchor)
            fragment = ref_schema.parsed['$dynamicAnchor']&.parsed&.fragment
            @dynamic_anchor = (fragment == ref_uri.fragment ? fragment : nil)
          end

          def validate(instance, instance_location, keyword_location, context)
            schema = ref_schema

            if dynamic_anchor
              context.dynamic_scope.each do |ancestor|
                dynamic_uri = URI.join(ancestor.base_uri, "##{dynamic_anchor}")
                if ancestor.root.resources.fetch(:dynamic).key?(dynamic_uri)
                  schema = ancestor.root.resources.fetch(:dynamic).fetch(dynamic_uri)
                  break
                end
              end
            end

            schema.validate_instance(instance, instance_location, keyword_location, context)
          end
        end

        class Defs < Keyword
          def parse
            value.each_with_object({}) do |(key, subschema), out|
              out[key] = subschema(subschema, key)
            end
          end
        end

        class Comment < Keyword; end

        class XError < Keyword
          def message(error_key)
            value.is_a?(Hash) ? (value[error_key] || value[CATCHALL]) : value
          end
        end

        class UnknownKeyword < Keyword
          def parse
            if value.is_a?(Hash)
              {}
            elsif value.is_a?(Array)
              []
            else
              value
            end
          end

          def fetch(token)
            if value.is_a?(Hash)
              parsed[token] ||= JSONSchemer::Schema::UNKNOWN_KEYWORD_CLASS.new(value.fetch(token), self, token, schema)
            elsif value.is_a?(Array)
              parsed[token.to_i] ||= JSONSchemer::Schema::UNKNOWN_KEYWORD_CLASS.new(value.fetch(token.to_i), self, token, schema)
            else
              raise KeyError.new(:receiver => parsed, :key => token)
            end
          end

          def parsed_schema
            @parsed_schema ||= subschema(value)
          end

          def validate(instance, instance_location, keyword_location, _context)
            result(instance, instance_location, keyword_location, true, :annotation => value)
          end
        end
      end
    end
  end
end