json_schemer 0.2.18 → 2.2.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 +7 -7
- data/CHANGELOG.md +89 -0
- data/Gemfile.lock +35 -10
- data/README.md +402 -6
- data/bin/hostname_character_classes +42 -0
- data/bin/rake +29 -0
- data/exe/json_schemer +62 -0
- data/json_schemer.gemspec +9 -12
- data/lib/json_schemer/cached_resolver.rb +16 -0
- data/lib/json_schemer/configuration.rb +31 -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 +104 -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 +128 -109
- data/lib/json_schemer/keyword.rb +56 -0
- data/lib/json_schemer/location.rb +25 -0
- data/lib/json_schemer/openapi.rb +38 -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 +242 -0
- data/lib/json_schemer/schema.rb +424 -0
- data/lib/json_schemer/version.rb +1 -1
- data/lib/json_schemer.rb +243 -29
- metadata +141 -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
@@ -0,0 +1,424 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module JSONSchemer
|
3
|
+
class Schema
|
4
|
+
Context = Struct.new(:instance, :dynamic_scope, :adjacent_results, :short_circuit, :access_mode) do
|
5
|
+
def original_instance(instance_location)
|
6
|
+
Hana::Pointer.parse(Location.resolve(instance_location)).reduce(instance) do |obj, token|
|
7
|
+
if obj.is_a?(Array)
|
8
|
+
obj.fetch(token.to_i)
|
9
|
+
elsif !obj.key?(token) && obj.key?(token.to_sym)
|
10
|
+
obj.fetch(token.to_sym)
|
11
|
+
else
|
12
|
+
obj.fetch(token)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
include Output
|
19
|
+
|
20
|
+
SCHEMA_KEYWORD_CLASS = Draft202012::Vocab::Core::Schema
|
21
|
+
VOCABULARY_KEYWORD_CLASS = Draft202012::Vocab::Core::Vocabulary
|
22
|
+
ID_KEYWORD_CLASS = Draft202012::Vocab::Core::Id
|
23
|
+
UNKNOWN_KEYWORD_CLASS = Draft202012::Vocab::Core::UnknownKeyword
|
24
|
+
NOT_KEYWORD_CLASS = Draft202012::Vocab::Applicator::Not
|
25
|
+
PROPERTIES_KEYWORD_CLASS = Draft202012::Vocab::Applicator::Properties
|
26
|
+
|
27
|
+
NET_HTTP_REF_RESOLVER = proc { |uri| JSON.parse(Net::HTTP.get(uri)) }
|
28
|
+
RUBY_REGEXP_RESOLVER = proc { |pattern| Regexp.new(pattern) }
|
29
|
+
ECMA_REGEXP_RESOLVER = proc { |pattern| Regexp.new(EcmaRegexp.ruby_equivalent(pattern)) }
|
30
|
+
|
31
|
+
DEFAULT_PROPERTY_DEFAULT_RESOLVER = proc do |instance, property, results_with_tree_validity|
|
32
|
+
results_with_tree_validity = results_with_tree_validity.select(&:last) unless results_with_tree_validity.size == 1
|
33
|
+
annotations = results_with_tree_validity.to_set { |result, _tree_valid| result.annotation }
|
34
|
+
if annotations.size == 1
|
35
|
+
instance[property] = annotations.first.clone
|
36
|
+
true
|
37
|
+
else
|
38
|
+
false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
SYMBOL_PROPERTY_DEFAULT_RESOLVER = proc do |instance, property, results_with_tree_validity|
|
42
|
+
DEFAULT_PROPERTY_DEFAULT_RESOLVER.call(instance, property.to_sym, results_with_tree_validity)
|
43
|
+
end
|
44
|
+
|
45
|
+
attr_accessor :base_uri, :meta_schema, :keywords, :keyword_order
|
46
|
+
attr_reader :value, :parent, :root, :configuration, :parsed
|
47
|
+
attr_reader :vocabulary, :format, :formats, :content_encodings, :content_media_types, :custom_keywords, :before_property_validation, :after_property_validation, :insert_property_defaults
|
48
|
+
|
49
|
+
def initialize(
|
50
|
+
value,
|
51
|
+
parent = nil,
|
52
|
+
root = self,
|
53
|
+
keyword = nil,
|
54
|
+
configuration: JSONSchemer.configuration,
|
55
|
+
base_uri: configuration.base_uri,
|
56
|
+
meta_schema: configuration.meta_schema,
|
57
|
+
vocabulary: configuration.vocabulary,
|
58
|
+
format: configuration.format,
|
59
|
+
formats: configuration.formats,
|
60
|
+
content_encodings: configuration.content_encodings,
|
61
|
+
content_media_types: configuration.content_media_types,
|
62
|
+
keywords: configuration.keywords,
|
63
|
+
before_property_validation: configuration.before_property_validation,
|
64
|
+
after_property_validation: configuration.after_property_validation,
|
65
|
+
insert_property_defaults: configuration.insert_property_defaults,
|
66
|
+
property_default_resolver: configuration.property_default_resolver,
|
67
|
+
ref_resolver: configuration.ref_resolver,
|
68
|
+
regexp_resolver: configuration.regexp_resolver,
|
69
|
+
output_format: configuration.output_format,
|
70
|
+
resolve_enumerators: configuration.resolve_enumerators,
|
71
|
+
access_mode: configuration.access_mode
|
72
|
+
)
|
73
|
+
@value = deep_stringify_keys(value)
|
74
|
+
@parent = parent
|
75
|
+
@root = root
|
76
|
+
@keyword = keyword
|
77
|
+
@schema = self
|
78
|
+
@configuration = configuration
|
79
|
+
@base_uri = base_uri
|
80
|
+
@meta_schema = meta_schema
|
81
|
+
@vocabulary = vocabulary
|
82
|
+
@format = format
|
83
|
+
@formats = formats
|
84
|
+
@content_encodings = content_encodings
|
85
|
+
@content_media_types = content_media_types
|
86
|
+
@custom_keywords = keywords
|
87
|
+
@before_property_validation = Array(before_property_validation)
|
88
|
+
@after_property_validation = Array(after_property_validation)
|
89
|
+
@insert_property_defaults = insert_property_defaults
|
90
|
+
@property_default_resolver = property_default_resolver
|
91
|
+
@original_ref_resolver = ref_resolver
|
92
|
+
@original_regexp_resolver = regexp_resolver
|
93
|
+
@output_format = output_format
|
94
|
+
@resolve_enumerators = resolve_enumerators
|
95
|
+
@access_mode = access_mode
|
96
|
+
@parsed = parse
|
97
|
+
end
|
98
|
+
|
99
|
+
def valid?(instance, **options)
|
100
|
+
validate(instance, :output_format => 'flag', **options).fetch('valid')
|
101
|
+
end
|
102
|
+
|
103
|
+
def validate(instance, output_format: @output_format, resolve_enumerators: @resolve_enumerators, access_mode: @access_mode)
|
104
|
+
instance_location = Location.root
|
105
|
+
context = Context.new(instance, [], nil, (!insert_property_defaults && output_format == 'flag'), access_mode)
|
106
|
+
result = validate_instance(deep_stringify_keys(instance), instance_location, root_keyword_location, context)
|
107
|
+
if insert_property_defaults && result.insert_property_defaults(context, &property_default_resolver)
|
108
|
+
result = validate_instance(deep_stringify_keys(instance), instance_location, root_keyword_location, context)
|
109
|
+
end
|
110
|
+
output = result.output(output_format)
|
111
|
+
resolve_enumerators!(output) if resolve_enumerators
|
112
|
+
output
|
113
|
+
end
|
114
|
+
|
115
|
+
def valid_schema?(**options)
|
116
|
+
meta_schema.valid?(value, **options)
|
117
|
+
end
|
118
|
+
|
119
|
+
def validate_schema(**options)
|
120
|
+
meta_schema.validate(value, **options)
|
121
|
+
end
|
122
|
+
|
123
|
+
def ref(value)
|
124
|
+
root.resolve_ref(URI.join(base_uri, value))
|
125
|
+
end
|
126
|
+
|
127
|
+
def validate_instance(instance, instance_location, keyword_location, context)
|
128
|
+
context.dynamic_scope.push(self)
|
129
|
+
original_adjacent_results = context.adjacent_results
|
130
|
+
adjacent_results = context.adjacent_results = {}
|
131
|
+
short_circuit = context.short_circuit
|
132
|
+
|
133
|
+
begin
|
134
|
+
return result(instance, instance_location, keyword_location, false) if value == false
|
135
|
+
return result(instance, instance_location, keyword_location, true) if value == true || value.empty?
|
136
|
+
|
137
|
+
valid = true
|
138
|
+
nested = []
|
139
|
+
|
140
|
+
parsed.each do |keyword, keyword_instance|
|
141
|
+
next unless keyword_result = keyword_instance.validate(instance, instance_location, join_location(keyword_location, keyword), context)
|
142
|
+
valid &&= keyword_result.valid
|
143
|
+
return result(instance, instance_location, keyword_location, false) if short_circuit && !valid
|
144
|
+
nested << keyword_result
|
145
|
+
adjacent_results[keyword_instance.class] = keyword_result
|
146
|
+
end
|
147
|
+
|
148
|
+
if root.custom_keywords.any?
|
149
|
+
resolved_instance_location = Location.resolve(instance_location)
|
150
|
+
root.custom_keywords.each do |custom_keyword, callable|
|
151
|
+
if value.key?(custom_keyword)
|
152
|
+
[*callable.call(instance, value, resolved_instance_location)].each do |custom_keyword_result|
|
153
|
+
custom_keyword_valid = custom_keyword_result == true
|
154
|
+
valid &&= custom_keyword_valid
|
155
|
+
type = custom_keyword_result.is_a?(String) ? custom_keyword_result : custom_keyword
|
156
|
+
details = { 'keyword' => custom_keyword, 'result' => custom_keyword_result }
|
157
|
+
nested << result(instance, instance_location, keyword_location, custom_keyword_valid, :type => type, :details => details)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
result(instance, instance_location, keyword_location, valid, nested)
|
164
|
+
ensure
|
165
|
+
context.dynamic_scope.pop
|
166
|
+
context.adjacent_results = original_adjacent_results
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def resolve_ref(uri)
|
171
|
+
pointer = ''
|
172
|
+
if Format.valid_json_pointer?(uri.fragment)
|
173
|
+
pointer = URI.decode_www_form_component(uri.fragment)
|
174
|
+
uri.fragment = nil
|
175
|
+
end
|
176
|
+
|
177
|
+
lexical_resources = resources.fetch(:lexical)
|
178
|
+
schema = lexical_resources[uri]
|
179
|
+
|
180
|
+
if !schema && uri.fragment.nil?
|
181
|
+
empty_fragment_uri = uri.dup
|
182
|
+
empty_fragment_uri.fragment = ''
|
183
|
+
schema = lexical_resources[empty_fragment_uri]
|
184
|
+
end
|
185
|
+
|
186
|
+
unless schema
|
187
|
+
location_independent_identifier = uri.fragment
|
188
|
+
uri.fragment = nil
|
189
|
+
remote_schema = JSONSchemer.schema(
|
190
|
+
ref_resolver.call(uri) || raise(InvalidRefResolution, uri.to_s),
|
191
|
+
:configuration => configuration,
|
192
|
+
:base_uri => uri,
|
193
|
+
:meta_schema => meta_schema,
|
194
|
+
:ref_resolver => ref_resolver,
|
195
|
+
:regexp_resolver => regexp_resolver
|
196
|
+
)
|
197
|
+
remote_uri = remote_schema.base_uri.dup
|
198
|
+
remote_uri.fragment = location_independent_identifier if location_independent_identifier
|
199
|
+
schema = remote_schema.resources.fetch(:lexical).fetch(remote_uri)
|
200
|
+
end
|
201
|
+
|
202
|
+
schema = Hana::Pointer.parse(pointer).reduce(schema) do |obj, token|
|
203
|
+
obj.fetch(token)
|
204
|
+
rescue IndexError
|
205
|
+
raise InvalidRefPointer, pointer
|
206
|
+
end
|
207
|
+
|
208
|
+
schema = schema.parsed_schema if schema.is_a?(Keyword)
|
209
|
+
raise InvalidRefPointer, pointer unless schema.is_a?(Schema)
|
210
|
+
|
211
|
+
schema
|
212
|
+
end
|
213
|
+
|
214
|
+
def resolve_regexp(pattern)
|
215
|
+
regexp_resolver.call(pattern) || raise(InvalidRegexpResolution, pattern)
|
216
|
+
end
|
217
|
+
|
218
|
+
def bundle
|
219
|
+
return value unless value.is_a?(Hash)
|
220
|
+
|
221
|
+
id_keyword = meta_schema.id_keyword
|
222
|
+
defs_keyword = meta_schema.defs_keyword
|
223
|
+
|
224
|
+
compound_document = value.dup
|
225
|
+
compound_document[id_keyword] = base_uri.to_s
|
226
|
+
compound_document['$schema'] = meta_schema.base_uri.to_s
|
227
|
+
embedded_resources = compound_document[defs_keyword] = (compound_document[defs_keyword]&.dup || {})
|
228
|
+
|
229
|
+
if compound_document.key?('$ref') && meta_schema.keywords.fetch('$ref').exclusive?
|
230
|
+
compound_document['allOf'] = (compound_document['allOf']&.dup || [])
|
231
|
+
compound_document['allOf'] << { '$ref' => compound_document.delete('$ref') }
|
232
|
+
end
|
233
|
+
|
234
|
+
values = [self]
|
235
|
+
while value = values.shift
|
236
|
+
case value
|
237
|
+
when Schema
|
238
|
+
values << value.parsed
|
239
|
+
when Keyword
|
240
|
+
if value.respond_to?(:ref_uri) && value.respond_to?(:ref_schema)
|
241
|
+
ref_uri = value.ref_uri.dup
|
242
|
+
ref_uri.fragment = nil
|
243
|
+
ref_id = ref_uri.to_s
|
244
|
+
ref_schema = value.ref_schema.root
|
245
|
+
|
246
|
+
next if ref_schema == root || embedded_resources.key?(ref_id)
|
247
|
+
|
248
|
+
embedded_resource = ref_schema.value.dup
|
249
|
+
embedded_resource[id_keyword] = ref_id
|
250
|
+
embedded_resource['$schema'] = ref_schema.meta_schema.base_uri.to_s
|
251
|
+
embedded_resources[ref_id] = embedded_resource
|
252
|
+
|
253
|
+
values << ref_schema
|
254
|
+
else
|
255
|
+
values << value.parsed
|
256
|
+
end
|
257
|
+
when Hash
|
258
|
+
values.concat(value.values)
|
259
|
+
when Array
|
260
|
+
values.concat(value)
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
compound_document
|
265
|
+
end
|
266
|
+
|
267
|
+
def absolute_keyword_location
|
268
|
+
# using `equal?` because `URI::Generic#==` is slow
|
269
|
+
@absolute_keyword_location ||= if !parent || (!parent.schema.base_uri.equal?(base_uri) && (base_uri.fragment.nil? || base_uri.fragment.empty?))
|
270
|
+
absolute_keyword_location_uri = base_uri.dup
|
271
|
+
absolute_keyword_location_uri.fragment = ''
|
272
|
+
absolute_keyword_location_uri.to_s
|
273
|
+
elsif keyword
|
274
|
+
"#{parent.absolute_keyword_location}/#{fragment_encode(escaped_keyword)}"
|
275
|
+
else
|
276
|
+
parent.absolute_keyword_location
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
def schema_pointer
|
281
|
+
@schema_pointer ||= if !parent
|
282
|
+
''
|
283
|
+
elsif keyword
|
284
|
+
"#{parent.schema_pointer}/#{escaped_keyword}"
|
285
|
+
else
|
286
|
+
parent.schema_pointer
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def error_key
|
291
|
+
'^'
|
292
|
+
end
|
293
|
+
|
294
|
+
def fetch(key)
|
295
|
+
parsed.fetch(key)
|
296
|
+
end
|
297
|
+
|
298
|
+
def fetch_format(format, *args, &block)
|
299
|
+
if meta_schema == self
|
300
|
+
formats.fetch(format, *args, &block)
|
301
|
+
else
|
302
|
+
formats.fetch(format) { meta_schema.fetch_format(format, *args, &block) }
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
def fetch_content_encoding(content_encoding, *args, &block)
|
307
|
+
if meta_schema == self
|
308
|
+
content_encodings.fetch(content_encoding, *args, &block)
|
309
|
+
else
|
310
|
+
content_encodings.fetch(content_encoding) { meta_schema.fetch_content_encoding(content_encoding, *args, &block) }
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
def fetch_content_media_type(content_media_type, *args, &block)
|
315
|
+
if meta_schema == self
|
316
|
+
content_media_types.fetch(content_media_type, *args, &block)
|
317
|
+
else
|
318
|
+
content_media_types.fetch(content_media_type) { meta_schema.fetch_content_media_type(content_media_type, *args, &block) }
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
def id_keyword
|
323
|
+
@id_keyword ||= (keywords.key?('$id') ? '$id' : 'id')
|
324
|
+
end
|
325
|
+
|
326
|
+
def defs_keyword
|
327
|
+
@defs_keyword ||= (keywords.key?('$defs') ? '$defs' : 'definitions')
|
328
|
+
end
|
329
|
+
|
330
|
+
def resources
|
331
|
+
@resources ||= { :lexical => {}, :dynamic => {} }
|
332
|
+
end
|
333
|
+
|
334
|
+
def error(formatted_instance_location:, **options)
|
335
|
+
if value == false && parent&.respond_to?(:false_schema_error)
|
336
|
+
parent.false_schema_error(:formatted_instance_location => formatted_instance_location, **options)
|
337
|
+
else
|
338
|
+
"value at #{formatted_instance_location} does not match schema"
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
def ref_resolver
|
343
|
+
@ref_resolver ||= @original_ref_resolver == 'net/http' ? CachedResolver.new(&NET_HTTP_REF_RESOLVER) : @original_ref_resolver
|
344
|
+
end
|
345
|
+
|
346
|
+
def regexp_resolver
|
347
|
+
@regexp_resolver ||= case @original_regexp_resolver
|
348
|
+
when 'ecma'
|
349
|
+
CachedResolver.new(&ECMA_REGEXP_RESOLVER)
|
350
|
+
when 'ruby'
|
351
|
+
CachedResolver.new(&RUBY_REGEXP_RESOLVER)
|
352
|
+
else
|
353
|
+
@original_regexp_resolver
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
def inspect
|
358
|
+
"#<#{self.class.name} @value=#{@value.inspect} @parent=#{@parent.inspect} @keyword=#{@keyword.inspect}>"
|
359
|
+
end
|
360
|
+
|
361
|
+
private
|
362
|
+
|
363
|
+
def parse
|
364
|
+
@parsed = {}
|
365
|
+
|
366
|
+
if value.is_a?(Hash) && value.key?('$schema')
|
367
|
+
@parsed['$schema'] = SCHEMA_KEYWORD_CLASS.new(value.fetch('$schema'), self, '$schema')
|
368
|
+
elsif meta_schema.is_a?(String)
|
369
|
+
SCHEMA_KEYWORD_CLASS.new(meta_schema, self, '$schema')
|
370
|
+
end
|
371
|
+
|
372
|
+
if value.is_a?(Hash) && value.key?('$vocabulary')
|
373
|
+
@parsed['$vocabulary'] = VOCABULARY_KEYWORD_CLASS.new(value.fetch('$vocabulary'), self, '$vocabulary')
|
374
|
+
elsif vocabulary
|
375
|
+
VOCABULARY_KEYWORD_CLASS.new(vocabulary, self, '$vocabulary')
|
376
|
+
end
|
377
|
+
|
378
|
+
keywords = meta_schema.keywords
|
379
|
+
exclusive_ref = value.is_a?(Hash) && value.key?('$ref') && keywords.fetch('$ref').exclusive?
|
380
|
+
|
381
|
+
if root == self && (!value.is_a?(Hash) || !value.key?(meta_schema.id_keyword) || exclusive_ref)
|
382
|
+
ID_KEYWORD_CLASS.new(base_uri, self, meta_schema.id_keyword)
|
383
|
+
end
|
384
|
+
|
385
|
+
if exclusive_ref
|
386
|
+
@parsed['$ref'] = keywords.fetch('$ref').new(value.fetch('$ref'), self, '$ref')
|
387
|
+
defs_keyword = meta_schema.defs_keyword
|
388
|
+
if value.key?(defs_keyword) && keywords.key?(defs_keyword)
|
389
|
+
@parsed[defs_keyword] = keywords.fetch(defs_keyword).new(value.fetch(defs_keyword), self, defs_keyword)
|
390
|
+
end
|
391
|
+
elsif value.is_a?(Hash)
|
392
|
+
keyword_order = meta_schema.keyword_order
|
393
|
+
last = keywords.size
|
394
|
+
|
395
|
+
value.sort do |(keyword_a, _value_a), (keyword_b, _value_b)|
|
396
|
+
keyword_order.fetch(keyword_a, last) <=> keyword_order.fetch(keyword_b, last)
|
397
|
+
end.each do |keyword, value|
|
398
|
+
@parsed[keyword] ||= keywords.fetch(keyword, UNKNOWN_KEYWORD_CLASS).new(value, self, keyword)
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
@parsed
|
403
|
+
end
|
404
|
+
|
405
|
+
def root_keyword_location
|
406
|
+
@root_keyword_location ||= Location.root
|
407
|
+
end
|
408
|
+
|
409
|
+
def property_default_resolver
|
410
|
+
@property_default_resolver ||= insert_property_defaults == :symbol ? SYMBOL_PROPERTY_DEFAULT_RESOLVER : DEFAULT_PROPERTY_DEFAULT_RESOLVER
|
411
|
+
end
|
412
|
+
|
413
|
+
def resolve_enumerators!(output)
|
414
|
+
case output
|
415
|
+
when Hash
|
416
|
+
output.transform_values! { |value| resolve_enumerators!(value) }
|
417
|
+
when Enumerator
|
418
|
+
output.map { |value| resolve_enumerators!(value) }
|
419
|
+
else
|
420
|
+
output
|
421
|
+
end
|
422
|
+
end
|
423
|
+
end
|
424
|
+
end
|
data/lib/json_schemer/version.rb
CHANGED