json_schemer 0.2.18 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +7 -7
  3. data/CHANGELOG.md +102 -0
  4. data/Gemfile.lock +30 -10
  5. data/README.md +402 -6
  6. data/bin/hostname_character_classes +42 -0
  7. data/bin/rake +29 -0
  8. data/exe/json_schemer +62 -0
  9. data/json_schemer.gemspec +9 -12
  10. data/lib/json_schemer/cached_resolver.rb +16 -0
  11. data/lib/json_schemer/configuration.rb +31 -0
  12. data/lib/json_schemer/content.rb +18 -0
  13. data/lib/json_schemer/draft201909/meta.rb +320 -0
  14. data/lib/json_schemer/draft201909/vocab/applicator.rb +104 -0
  15. data/lib/json_schemer/draft201909/vocab/core.rb +45 -0
  16. data/lib/json_schemer/draft201909/vocab.rb +31 -0
  17. data/lib/json_schemer/draft202012/meta.rb +364 -0
  18. data/lib/json_schemer/draft202012/vocab/applicator.rb +382 -0
  19. data/lib/json_schemer/draft202012/vocab/content.rb +52 -0
  20. data/lib/json_schemer/draft202012/vocab/core.rb +160 -0
  21. data/lib/json_schemer/draft202012/vocab/format_annotation.rb +23 -0
  22. data/lib/json_schemer/draft202012/vocab/format_assertion.rb +23 -0
  23. data/lib/json_schemer/draft202012/vocab/meta_data.rb +30 -0
  24. data/lib/json_schemer/draft202012/vocab/unevaluated.rb +104 -0
  25. data/lib/json_schemer/draft202012/vocab/validation.rb +286 -0
  26. data/lib/json_schemer/draft202012/vocab.rb +105 -0
  27. data/lib/json_schemer/draft4/meta.rb +161 -0
  28. data/lib/json_schemer/draft4/vocab/validation.rb +39 -0
  29. data/lib/json_schemer/draft4/vocab.rb +18 -0
  30. data/lib/json_schemer/draft6/meta.rb +172 -0
  31. data/lib/json_schemer/draft6/vocab.rb +16 -0
  32. data/lib/json_schemer/draft7/meta.rb +183 -0
  33. data/lib/json_schemer/draft7/vocab/validation.rb +69 -0
  34. data/lib/json_schemer/draft7/vocab.rb +30 -0
  35. data/lib/json_schemer/ecma_regexp.rb +51 -0
  36. data/lib/json_schemer/errors.rb +1 -0
  37. data/lib/json_schemer/format/duration.rb +23 -0
  38. data/lib/json_schemer/format/email.rb +56 -0
  39. data/lib/json_schemer/format/hostname.rb +58 -0
  40. data/lib/json_schemer/format/json_pointer.rb +18 -0
  41. data/lib/json_schemer/format/uri_template.rb +34 -0
  42. data/lib/json_schemer/format.rb +128 -109
  43. data/lib/json_schemer/keyword.rb +56 -0
  44. data/lib/json_schemer/location.rb +25 -0
  45. data/lib/json_schemer/openapi.rb +38 -0
  46. data/lib/json_schemer/openapi30/document.rb +1672 -0
  47. data/lib/json_schemer/openapi30/meta.rb +32 -0
  48. data/lib/json_schemer/openapi30/vocab/base.rb +18 -0
  49. data/lib/json_schemer/openapi30/vocab.rb +12 -0
  50. data/lib/json_schemer/openapi31/document.rb +1557 -0
  51. data/lib/json_schemer/openapi31/meta.rb +136 -0
  52. data/lib/json_schemer/openapi31/vocab/base.rb +127 -0
  53. data/lib/json_schemer/openapi31/vocab.rb +18 -0
  54. data/lib/json_schemer/output.rb +56 -0
  55. data/lib/json_schemer/result.rb +242 -0
  56. data/lib/json_schemer/schema.rb +424 -0
  57. data/lib/json_schemer/version.rb +1 -1
  58. data/lib/json_schemer.rb +243 -30
  59. metadata +141 -25
  60. data/lib/json_schemer/cached_ref_resolver.rb +0 -14
  61. data/lib/json_schemer/schema/base.rb +0 -658
  62. data/lib/json_schemer/schema/draft4.rb +0 -44
  63. data/lib/json_schemer/schema/draft6.rb +0 -25
  64. data/lib/json_schemer/schema/draft7.rb +0 -32
@@ -1,658 +0,0 @@
1
- # frozen_string_literal: true
2
- module JSONSchemer
3
- module Schema
4
- class Base
5
- include Format
6
-
7
- Instance = Struct.new(:data, :data_pointer, :schema, :schema_pointer, :parent_uri, :before_property_validation, :after_property_validation) do
8
- def merge(
9
- data: self.data,
10
- data_pointer: self.data_pointer,
11
- schema: self.schema,
12
- schema_pointer: self.schema_pointer,
13
- parent_uri: self.parent_uri,
14
- before_property_validation: self.before_property_validation,
15
- after_property_validation: self.after_property_validation
16
- )
17
- self.class.new(data, data_pointer, schema, schema_pointer, parent_uri, before_property_validation, after_property_validation)
18
- end
19
- end
20
-
21
- ID_KEYWORD = '$id'
22
- DEFAULT_REF_RESOLVER = proc { |uri| raise UnknownRef, uri.to_s }
23
- NET_HTTP_REF_RESOLVER = proc { |uri| JSON.parse(Net::HTTP.get(uri)) }
24
- BOOLEANS = Set[true, false].freeze
25
-
26
- RUBY_REGEX_ANCHORS_TO_ECMA_262 = {
27
- :bos => 'A',
28
- :eos => 'z',
29
- :bol => '\A',
30
- :eol => '\z'
31
- }.freeze
32
-
33
- INSERT_DEFAULT_PROPERTY = proc do |data, property, property_schema, _parent|
34
- if !data.key?(property) && property_schema.is_a?(Hash) && property_schema.key?('default')
35
- data[property] = property_schema.fetch('default').clone
36
- end
37
- end
38
-
39
- def initialize(
40
- schema,
41
- format: true,
42
- insert_property_defaults: false,
43
- before_property_validation: nil,
44
- after_property_validation: nil,
45
- formats: nil,
46
- keywords: nil,
47
- ref_resolver: DEFAULT_REF_RESOLVER
48
- )
49
- raise InvalidSymbolKey, 'schemas must use string keys' if schema.is_a?(Hash) && !schema.empty? && !schema.first.first.is_a?(String)
50
- @root = schema
51
- @format = format
52
- @before_property_validation = [*before_property_validation]
53
- @before_property_validation.unshift(INSERT_DEFAULT_PROPERTY) if insert_property_defaults
54
- @after_property_validation = [*after_property_validation]
55
- @formats = formats
56
- @keywords = keywords
57
- @ref_resolver = ref_resolver == 'net/http' ? CachedRefResolver.new(&NET_HTTP_REF_RESOLVER) : ref_resolver
58
- end
59
-
60
- def valid?(data)
61
- valid_instance?(Instance.new(data, '', root, '', nil, @before_property_validation, @after_property_validation))
62
- end
63
-
64
- def validate(data)
65
- validate_instance(Instance.new(data, '', root, '', nil, @before_property_validation, @after_property_validation))
66
- end
67
-
68
- protected
69
-
70
- def valid_instance?(instance)
71
- validate_instance(instance).none?
72
- end
73
-
74
- def validate_instance(instance, &block)
75
- return enum_for(:validate_instance, instance) unless block_given?
76
-
77
- schema = instance.schema
78
-
79
- if schema == false
80
- yield error(instance, 'schema')
81
- return
82
- end
83
-
84
- return if schema == true || schema.empty?
85
-
86
- type = schema['type']
87
- enum = schema['enum']
88
- all_of = schema['allOf']
89
- any_of = schema['anyOf']
90
- one_of = schema['oneOf']
91
- not_schema = schema['not']
92
- if_schema = schema['if']
93
- then_schema = schema['then']
94
- else_schema = schema['else']
95
- format = schema['format']
96
- ref = schema['$ref']
97
- id = schema[id_keyword]
98
-
99
- instance.parent_uri = join_uri(instance.parent_uri, id)
100
-
101
- if ref
102
- validate_ref(instance, ref, &block)
103
- return
104
- end
105
-
106
- if format? && custom_format?(format)
107
- validate_custom_format(instance, formats.fetch(format), &block)
108
- end
109
-
110
- data = instance.data
111
-
112
- if keywords
113
- keywords.each do |keyword, callable|
114
- if schema.key?(keyword)
115
- result = callable.call(data, schema, instance.data_pointer)
116
- if result.is_a?(Array)
117
- result.each(&block)
118
- elsif !result
119
- yield error(instance, keyword)
120
- end
121
- end
122
- end
123
- end
124
-
125
- yield error(instance, 'enum') if enum && !enum.include?(data)
126
- yield error(instance, 'const') if schema.key?('const') && schema['const'] != data
127
-
128
- if all_of
129
- all_of.each_with_index do |subschema, index|
130
- subinstance = instance.merge(
131
- schema: subschema,
132
- schema_pointer: "#{instance.schema_pointer}/allOf/#{index}",
133
- before_property_validation: false,
134
- after_property_validation: false
135
- )
136
- validate_instance(subinstance, &block)
137
- end
138
- end
139
-
140
- if any_of
141
- subschemas = any_of.lazy.with_index.map do |subschema, index|
142
- subinstance = instance.merge(
143
- schema: subschema,
144
- schema_pointer: "#{instance.schema_pointer}/anyOf/#{index}",
145
- before_property_validation: false,
146
- after_property_validation: false
147
- )
148
- validate_instance(subinstance)
149
- end
150
- subschemas.each { |subschema| subschema.each(&block) } unless subschemas.any?(&:none?)
151
- end
152
-
153
- if one_of
154
- subschemas = one_of.map.with_index do |subschema, index|
155
- subinstance = instance.merge(
156
- schema: subschema,
157
- schema_pointer: "#{instance.schema_pointer}/oneOf/#{index}",
158
- before_property_validation: false,
159
- after_property_validation: false
160
- )
161
- validate_instance(subinstance)
162
- end
163
- valid_subschema_count = subschemas.count(&:none?)
164
- if valid_subschema_count > 1
165
- yield error(instance, 'oneOf')
166
- elsif valid_subschema_count == 0
167
- subschemas.each { |subschema| subschema.each(&block) }
168
- end
169
- end
170
-
171
- unless not_schema.nil?
172
- subinstance = instance.merge(
173
- schema: not_schema,
174
- schema_pointer: "#{instance.schema_pointer}/not",
175
- before_property_validation: false,
176
- after_property_validation: false
177
- )
178
- yield error(subinstance, 'not') if valid_instance?(subinstance)
179
- end
180
-
181
- if if_schema && valid_instance?(instance.merge(schema: if_schema, before_property_validation: false, after_property_validation: false))
182
- validate_instance(instance.merge(schema: then_schema, schema_pointer: "#{instance.schema_pointer}/then"), &block) unless then_schema.nil?
183
- elsif if_schema
184
- validate_instance(instance.merge(schema: else_schema, schema_pointer: "#{instance.schema_pointer}/else"), &block) unless else_schema.nil?
185
- end
186
-
187
- case type
188
- when nil
189
- validate_class(instance, &block)
190
- when String
191
- validate_type(instance, type, &block)
192
- when Array
193
- if valid_type = type.find { |subtype| valid_instance?(instance.merge(schema: { 'type' => subtype })) }
194
- validate_type(instance, valid_type, &block)
195
- else
196
- yield error(instance, 'type')
197
- end
198
- end
199
- end
200
-
201
- def ids
202
- @ids ||= resolve_ids(root)
203
- end
204
-
205
- private
206
-
207
- attr_reader :root, :formats, :keywords, :ref_resolver
208
-
209
- def id_keyword
210
- ID_KEYWORD
211
- end
212
-
213
- def format?
214
- !!@format
215
- end
216
-
217
- def custom_format?(format)
218
- !!(formats && formats.key?(format))
219
- end
220
-
221
- def spec_format?(format)
222
- !custom_format?(format) && supported_format?(format)
223
- end
224
-
225
- def child(schema)
226
- JSONSchemer.schema(
227
- schema,
228
- format: format?,
229
- formats: formats,
230
- keywords: keywords,
231
- ref_resolver: ref_resolver
232
- )
233
- end
234
-
235
- def error(instance, type, details = nil)
236
- error = {
237
- 'data' => instance.data,
238
- 'data_pointer' => instance.data_pointer,
239
- 'schema' => instance.schema,
240
- 'schema_pointer' => instance.schema_pointer,
241
- 'root_schema' => root,
242
- 'type' => type,
243
- }
244
- error['details'] = details if details
245
- error
246
- end
247
-
248
- def validate_class(instance, &block)
249
- case instance.data
250
- when Integer
251
- validate_integer(instance, &block)
252
- when Numeric
253
- validate_number(instance, &block)
254
- when String
255
- validate_string(instance, &block)
256
- when Array
257
- validate_array(instance, &block)
258
- when Hash
259
- validate_object(instance, &block)
260
- end
261
- end
262
-
263
- def validate_type(instance, type, &block)
264
- case type
265
- when 'null'
266
- yield error(instance, 'null') unless instance.data.nil?
267
- when 'boolean'
268
- yield error(instance, 'boolean') unless BOOLEANS.include?(instance.data)
269
- when 'number'
270
- validate_number(instance, &block)
271
- when 'integer'
272
- validate_integer(instance, &block)
273
- when 'string'
274
- validate_string(instance, &block)
275
- when 'array'
276
- validate_array(instance, &block)
277
- when 'object'
278
- validate_object(instance, &block)
279
- end
280
- end
281
-
282
- def validate_ref(instance, ref, &block)
283
- if ref.start_with?('#')
284
- schema_pointer = ref.slice(1..-1)
285
- if valid_json_pointer?(schema_pointer)
286
- ref_pointer = Hana::Pointer.new(URI.decode_www_form_component(schema_pointer))
287
- subinstance = instance.merge(
288
- schema: ref_pointer.eval(root),
289
- schema_pointer: schema_pointer,
290
- parent_uri: (pointer_uri(root, ref_pointer) || instance.parent_uri)
291
- )
292
- validate_instance(subinstance, &block)
293
- return
294
- end
295
- end
296
-
297
- ref_uri = join_uri(instance.parent_uri, ref)
298
-
299
- if valid_json_pointer?(ref_uri.fragment)
300
- ref_pointer = Hana::Pointer.new(URI.decode_www_form_component(ref_uri.fragment))
301
- ref_root = resolve_ref(ref_uri)
302
- ref_object = child(ref_root)
303
- subinstance = instance.merge(
304
- schema: ref_pointer.eval(ref_root),
305
- schema_pointer: ref_uri.fragment,
306
- parent_uri: (pointer_uri(ref_root, ref_pointer) || ref_uri)
307
- )
308
- ref_object.validate_instance(subinstance, &block)
309
- elsif id = ids[ref_uri.to_s]
310
- subinstance = instance.merge(
311
- schema: id.fetch(:schema),
312
- schema_pointer: id.fetch(:pointer),
313
- parent_uri: ref_uri
314
- )
315
- validate_instance(subinstance, &block)
316
- else
317
- ref_root = resolve_ref(ref_uri)
318
- ref_object = child(ref_root)
319
- id = ref_object.ids[ref_uri.to_s] || { schema: ref_root, pointer: '' }
320
- subinstance = instance.merge(
321
- schema: id.fetch(:schema),
322
- schema_pointer: id.fetch(:pointer),
323
- parent_uri: ref_uri
324
- )
325
- ref_object.validate_instance(subinstance, &block)
326
- end
327
- end
328
-
329
- def validate_custom_format(instance, custom_format)
330
- yield error(instance, 'format') if custom_format != false && !custom_format.call(instance.data, instance.schema)
331
- end
332
-
333
- def validate_exclusive_maximum(instance, exclusive_maximum, maximum)
334
- yield error(instance, 'exclusiveMaximum') if instance.data >= exclusive_maximum
335
- end
336
-
337
- def validate_exclusive_minimum(instance, exclusive_minimum, minimum)
338
- yield error(instance, 'exclusiveMinimum') if instance.data <= exclusive_minimum
339
- end
340
-
341
- def validate_numeric(instance, &block)
342
- schema = instance.schema
343
- data = instance.data
344
-
345
- multiple_of = schema['multipleOf']
346
- maximum = schema['maximum']
347
- exclusive_maximum = schema['exclusiveMaximum']
348
- minimum = schema['minimum']
349
- exclusive_minimum = schema['exclusiveMinimum']
350
-
351
- yield error(instance, 'maximum') if maximum && data > maximum
352
- yield error(instance, 'minimum') if minimum && data < minimum
353
-
354
- validate_exclusive_maximum(instance, exclusive_maximum, maximum, &block) if exclusive_maximum
355
- validate_exclusive_minimum(instance, exclusive_minimum, minimum, &block) if exclusive_minimum
356
-
357
- if multiple_of
358
- quotient = data / multiple_of.to_f
359
- yield error(instance, 'multipleOf') unless quotient.floor == quotient
360
- end
361
- end
362
-
363
- def validate_number(instance, &block)
364
- unless instance.data.is_a?(Numeric)
365
- yield error(instance, 'number')
366
- return
367
- end
368
-
369
- validate_numeric(instance, &block)
370
- end
371
-
372
- def validate_integer(instance, &block)
373
- data = instance.data
374
-
375
- if !data.is_a?(Numeric) || (!data.is_a?(Integer) && data.floor != data)
376
- yield error(instance, 'integer')
377
- return
378
- end
379
-
380
- validate_numeric(instance, &block)
381
- end
382
-
383
- def validate_string(instance, &block)
384
- data = instance.data
385
-
386
- unless data.is_a?(String)
387
- yield error(instance, 'string')
388
- return
389
- end
390
-
391
- schema = instance.schema
392
-
393
- max_length = schema['maxLength']
394
- min_length = schema['minLength']
395
- pattern = schema['pattern']
396
- format = schema['format']
397
- content_encoding = schema['contentEncoding']
398
- content_media_type = schema['contentMediaType']
399
-
400
- yield error(instance, 'maxLength') if max_length && data.size > max_length
401
- yield error(instance, 'minLength') if min_length && data.size < min_length
402
- yield error(instance, 'pattern') if pattern && ecma_262_regex(pattern) !~ data
403
- yield error(instance, 'format') if format? && spec_format?(format) && !valid_spec_format?(data, format)
404
-
405
- if content_encoding || content_media_type
406
- decoded_data = data
407
-
408
- if content_encoding
409
- decoded_data = case content_encoding.downcase
410
- when 'base64'
411
- safe_strict_decode64(data)
412
- else # '7bit', '8bit', 'binary', 'quoted-printable'
413
- raise NotImplementedError
414
- end
415
- yield error(instance, 'contentEncoding') unless decoded_data
416
- end
417
-
418
- if content_media_type && decoded_data
419
- case content_media_type.downcase
420
- when 'application/json'
421
- yield error(instance, 'contentMediaType') unless valid_json?(decoded_data)
422
- else
423
- raise NotImplementedError
424
- end
425
- end
426
- end
427
- end
428
-
429
- def validate_array(instance, &block)
430
- data = instance.data
431
-
432
- unless data.is_a?(Array)
433
- yield error(instance, 'array')
434
- return
435
- end
436
-
437
- schema = instance.schema
438
-
439
- items = schema['items']
440
- additional_items = schema['additionalItems']
441
- max_items = schema['maxItems']
442
- min_items = schema['minItems']
443
- unique_items = schema['uniqueItems']
444
- contains = schema['contains']
445
-
446
- yield error(instance, 'maxItems') if max_items && data.size > max_items
447
- yield error(instance, 'minItems') if min_items && data.size < min_items
448
- yield error(instance, 'uniqueItems') if unique_items && data.size != data.uniq.size
449
- yield error(instance, 'contains') if !contains.nil? && data.all? { |item| !valid_instance?(instance.merge(data: item, schema: contains)) }
450
-
451
- if items.is_a?(Array)
452
- data.each_with_index do |item, index|
453
- if index < items.size
454
- subinstance = instance.merge(
455
- data: item,
456
- data_pointer: "#{instance.data_pointer}/#{index}",
457
- schema: items[index],
458
- schema_pointer: "#{instance.schema_pointer}/items/#{index}"
459
- )
460
- validate_instance(subinstance, &block)
461
- elsif !additional_items.nil?
462
- subinstance = instance.merge(
463
- data: item,
464
- data_pointer: "#{instance.data_pointer}/#{index}",
465
- schema: additional_items,
466
- schema_pointer: "#{instance.schema_pointer}/additionalItems"
467
- )
468
- validate_instance(subinstance, &block)
469
- else
470
- break
471
- end
472
- end
473
- elsif !items.nil?
474
- data.each_with_index do |item, index|
475
- subinstance = instance.merge(
476
- data: item,
477
- data_pointer: "#{instance.data_pointer}/#{index}",
478
- schema: items,
479
- schema_pointer: "#{instance.schema_pointer}/items"
480
- )
481
- validate_instance(subinstance, &block)
482
- end
483
- end
484
- end
485
-
486
- def validate_object(instance, &block)
487
- data = instance.data
488
-
489
- unless data.is_a?(Hash)
490
- yield error(instance, 'object')
491
- return
492
- end
493
-
494
- schema = instance.schema
495
-
496
- max_properties = schema['maxProperties']
497
- min_properties = schema['minProperties']
498
- required = schema['required']
499
- properties = schema['properties']
500
- pattern_properties = schema['patternProperties']
501
- additional_properties = schema['additionalProperties']
502
- dependencies = schema['dependencies']
503
- property_names = schema['propertyNames']
504
-
505
- if instance.before_property_validation && properties
506
- properties.each do |property, property_schema|
507
- instance.before_property_validation.each do |hook|
508
- hook.call(data, property, property_schema, schema)
509
- end
510
- end
511
- end
512
-
513
- if dependencies
514
- dependencies.each do |key, value|
515
- next unless data.key?(key)
516
- subschema = value.is_a?(Array) ? { 'required' => value } : value
517
- subinstance = instance.merge(schema: subschema, schema_pointer: "#{instance.schema_pointer}/dependencies/#{key}")
518
- validate_instance(subinstance, &block)
519
- end
520
- end
521
-
522
- yield error(instance, 'maxProperties') if max_properties && data.size > max_properties
523
- yield error(instance, 'minProperties') if min_properties && data.size < min_properties
524
- if required
525
- missing_keys = required - data.keys
526
- yield error(instance, 'required', 'missing_keys' => missing_keys) if missing_keys.any?
527
- end
528
-
529
- regex_pattern_properties = nil
530
- data.each do |key, value|
531
- unless property_names.nil?
532
- subinstance = instance.merge(
533
- data: key,
534
- schema: property_names,
535
- schema_pointer: "#{instance.schema_pointer}/propertyNames"
536
- )
537
- validate_instance(subinstance, &block)
538
- end
539
-
540
- matched_key = false
541
-
542
- if properties && properties.key?(key)
543
- subinstance = instance.merge(
544
- data: value,
545
- data_pointer: "#{instance.data_pointer}/#{key}",
546
- schema: properties[key],
547
- schema_pointer: "#{instance.schema_pointer}/properties/#{key}"
548
- )
549
- validate_instance(subinstance, &block)
550
- matched_key = true
551
- end
552
-
553
- if pattern_properties
554
- regex_pattern_properties ||= pattern_properties.map do |pattern, property_schema|
555
- [pattern, ecma_262_regex(pattern), property_schema]
556
- end
557
- regex_pattern_properties.each do |pattern, regex, property_schema|
558
- if regex.match?(key)
559
- subinstance = instance.merge(
560
- data: value,
561
- data_pointer: "#{instance.data_pointer}/#{key}",
562
- schema: property_schema,
563
- schema_pointer: "#{instance.schema_pointer}/patternProperties/#{pattern}"
564
- )
565
- validate_instance(subinstance, &block)
566
- matched_key = true
567
- end
568
- end
569
- end
570
-
571
- next if matched_key
572
-
573
- unless additional_properties.nil?
574
- subinstance = instance.merge(
575
- data: value,
576
- data_pointer: "#{instance.data_pointer}/#{key}",
577
- schema: additional_properties,
578
- schema_pointer: "#{instance.schema_pointer}/additionalProperties"
579
- )
580
- validate_instance(subinstance, &block)
581
- end
582
- end
583
-
584
- if instance.after_property_validation && properties
585
- properties.each do |property, property_schema|
586
- instance.after_property_validation.each do |hook|
587
- hook.call(data, property, property_schema, schema)
588
- end
589
- end
590
- end
591
- end
592
-
593
- def safe_strict_decode64(data)
594
- Base64.strict_decode64(data)
595
- rescue ArgumentError => e
596
- raise e unless e.message == 'invalid base64'
597
- nil
598
- end
599
-
600
- def ecma_262_regex(pattern)
601
- @ecma_262_regex ||= {}
602
- @ecma_262_regex[pattern] ||= Regexp.new(
603
- Regexp::Scanner.scan(pattern).map do |type, token, text|
604
- type == :anchor ? RUBY_REGEX_ANCHORS_TO_ECMA_262.fetch(token, text) : text
605
- end.join
606
- )
607
- end
608
-
609
- def join_uri(a, b)
610
- b = URI.parse(b) if b
611
- if a && b && a.relative? && b.relative?
612
- b
613
- elsif a && b
614
- URI.join(a, b)
615
- elsif b
616
- b
617
- else
618
- a
619
- end
620
- end
621
-
622
- def pointer_uri(schema, pointer)
623
- uri_parts = nil
624
- pointer.reduce(schema) do |obj, token|
625
- next obj.fetch(token.to_i) if obj.is_a?(Array)
626
- if obj_id = obj[id_keyword]
627
- uri_parts ||= []
628
- uri_parts << obj_id
629
- end
630
- obj.fetch(token)
631
- end
632
- uri_parts ? URI.join(*uri_parts) : nil
633
- end
634
-
635
- def resolve_ids(schema, ids = {}, parent_uri = nil, pointer = '')
636
- if schema.is_a?(Array)
637
- schema.each_with_index { |subschema, index| resolve_ids(subschema, ids, parent_uri, "#{pointer}/#{index}") }
638
- elsif schema.is_a?(Hash)
639
- uri = join_uri(parent_uri, schema[id_keyword])
640
- schema.each do |key, value|
641
- if key == id_keyword && uri != parent_uri
642
- ids[uri.to_s] = {
643
- schema: schema,
644
- pointer: pointer
645
- }
646
- end
647
- resolve_ids(value, ids, uri, "#{pointer}/#{key}")
648
- end
649
- end
650
- ids
651
- end
652
-
653
- def resolve_ref(uri)
654
- ref_resolver.call(uri) || raise(InvalidRefResolution, uri.to_s)
655
- end
656
- end
657
- end
658
- end
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
- module JSONSchemer
3
- module Schema
4
- class Draft4 < Base
5
- ID_KEYWORD = 'id'
6
- SUPPORTED_FORMATS = Set[
7
- 'date-time',
8
- 'email',
9
- 'hostname',
10
- 'ipv4',
11
- 'ipv6',
12
- 'uri',
13
- 'regex'
14
- ].freeze
15
-
16
- private
17
-
18
- def id_keyword
19
- ID_KEYWORD
20
- end
21
-
22
- def supported_format?(format)
23
- SUPPORTED_FORMATS.include?(format)
24
- end
25
-
26
- def validate_exclusive_maximum(instance, exclusive_maximum, maximum)
27
- yield error(instance, 'exclusiveMaximum') if exclusive_maximum && instance.data >= maximum
28
- end
29
-
30
- def validate_exclusive_minimum(instance, exclusive_minimum, minimum)
31
- yield error(instance, 'exclusiveMinimum') if exclusive_minimum && instance.data <= minimum
32
- end
33
-
34
- def validate_integer(instance, &block)
35
- if !instance.data.is_a?(Integer)
36
- yield error(instance, 'integer')
37
- return
38
- end
39
-
40
- validate_numeric(instance, &block)
41
- end
42
- end
43
- end
44
- end