json_schemer 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0c60e714e579a94981d17cf588f7b036ff39b95f95c6a4080b291c335e7ad4a4
4
- data.tar.gz: b902418e60c5e98d19456aed4ae6aad09e787b4074a9b08177b9cb1fa967cb4f
3
+ metadata.gz: 6f1555ed0ddacfa334c2a2635b4cce09c532aae3121539c28086e27ddf3c22fe
4
+ data.tar.gz: 5e49a8c68c44f40ac3ac9b3766b49f8f1c5d21ff5560dd1e2dd8f3d994183655
5
5
  SHA512:
6
- metadata.gz: 56f2f16354a2d1aff6565de44517b67722b8abc764dab185da0f4a46813a73599d32c7fdc1792790ad2cd1eaa08c92bda13b77ac76847da1c53b59bde0850c9b
7
- data.tar.gz: efd9edf9ab76f389107ec9d02964ed4904655c3e5d2f2e6aec66cad668b6d034a5d61a7fea77f56b7f10d7c8a8f8ceb2635316d7987f41ac96f34c4bf1d25fc5
6
+ metadata.gz: e5e79032c18e1e4f9ba1a21be3d7b6afc48a7decdd37f7b2f9d4a9984b85684b1b115e2b16a4497955ec0d0d696d082dcd346f0443b3494f415718323f3beb86
7
+ data.tar.gz: 229375ee9837355c3225080db7b88eed7dce26a3fb608ded003453b9765de4a3d1546d3ea0eb6e7649698fcbe8ba58b49157cab7817f6a71ae66963abb5f45a4
@@ -1,3 +1,3 @@
1
1
  [submodule "JSON-Schema-Test-Suite"]
2
2
  path = JSON-Schema-Test-Suite
3
- url = git@github.com:json-schema-org/JSON-Schema-Test-Suite
3
+ url = https://github.com/json-schema-org/JSON-Schema-Test-Suite
@@ -1,5 +1,10 @@
1
1
  sudo: false
2
2
  language: ruby
3
3
  rvm:
4
- - 2.5.0
4
+ - 2.5
5
+ - 2.3
6
+ - 2.2
5
7
  before_install: gem install bundler -v 1.16.1
8
+ before_script:
9
+ - pip install --user Flask
10
+ - ./JSON-Schema-Test-Suite/bin/jsonschema_suite serve &
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- json_schemer (0.1.3)
4
+ json_schemer (0.1.4)
5
5
  ecma-re-validator (~> 0.1.2)
6
6
  hana (~> 1.3.3)
7
7
  uri_template (~> 0.7.0)
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # JSONSchemer
2
2
 
3
- JSON Schema draft-07 validator
3
+ JSON Schema validator. Supports drafts 4, 6, and 7.
4
4
 
5
5
  ## Installation
6
6
 
@@ -32,7 +32,7 @@ schema = {
32
32
  }
33
33
  }
34
34
  }
35
- schemer = JSONSchemer::Schema.new(schema)
35
+ schemer = JSONSchemer.schema(schema)
36
36
 
37
37
  # true/false validation
38
38
 
@@ -51,7 +51,7 @@ schemer.validate({ 'abc' => 10 }).to_a
51
51
  ## Options
52
52
 
53
53
  ```ruby
54
- JSONSchemer::Schema.new(
54
+ JSONSchemer.schema(
55
55
  schema,
56
56
 
57
57
  # validate `format` (https://tools.ietf.org/html/draft-handrews-json-schema-validation-00#section-7)
@@ -71,7 +71,9 @@ JSONSchemer::Schema.new(
71
71
 
72
72
  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.
73
73
 
74
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
74
+ ## Build Status
75
+
76
+ [![Build Status](https://travis-ci.org/davishmcclurg/json_schemer.svg?branch=master)](https://travis-ci.org/davishmcclurg/json_schemer)
75
77
 
76
78
  ## Contributing
77
79
 
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["David Harsha"]
10
10
  spec.email = ["davishmcclurg@gmail.com"]
11
11
 
12
- spec.summary = "JSON Schema draft-07 validator"
12
+ spec.summary = "JSON Schema validator. Supports drafts 4, 6, and 7."
13
13
  spec.homepage = "https://github.com/davishmcclurg/json_schemer"
14
14
  spec.license = "MIT"
15
15
 
@@ -1,521 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "json_schemer/version"
4
- require "json_schemer/format"
5
-
6
- require "base64"
7
- require "ecma-re-validator"
8
- require "hana"
9
- require "ipaddr"
10
- require "json"
11
- require 'net/http'
12
- require "time"
13
- require "uri"
14
- require "uri_template"
3
+ require 'json_schemer/version'
4
+ require 'json_schemer/format'
5
+ require 'json_schemer/schema/base'
6
+ require 'json_schemer/schema/draft4'
7
+ require 'json_schemer/schema/draft6'
8
+ require 'json_schemer/schema/draft7'
15
9
 
16
10
  module JSONSchemer
17
- class Schema
18
- class InvalidMetaSchema < StandardError; end
19
- class UnknownRef < StandardError; end
20
-
21
- META_SCHEMA = 'http://json-schema.org/draft-07/schema#'
22
- DEFAULT_REF_RESOLVER = proc { |uri| raise UnknownRef, uri.to_s }.freeze
23
- NET_HTTP_REF_RESOLVER = proc { |uri| JSON.parse(Net::HTTP.get(uri)) }.freeze
24
- BOOLEANS = Set[true, false].freeze
25
-
26
- def initialize(
27
- schema,
28
- format: true,
29
- formats: nil,
30
- keywords: nil,
31
- ref_resolver: DEFAULT_REF_RESOLVER
32
- )
33
- if schema.is_a?(Hash) && schema.key?('$schema') && schema['$schema'] != META_SCHEMA
34
- raise InvalidMetaSchema, "draft-07 is the only supported meta-schema (#{META_SCHEMA})"
35
- end
36
-
37
- @root = schema
38
- @format = format
39
- @formats = formats
40
- @keywords = keywords
41
- @ref_resolver = ref_resolver == 'net/http' ? NET_HTTP_REF_RESOLVER : ref_resolver
42
- end
43
-
44
- def valid?(data, schema = root, pointer = '', parent_uri = nil)
45
- validate(data, schema, pointer, parent_uri).none?
46
- end
47
-
48
- def validate(data, schema = root, pointer = '', parent_uri = nil)
49
- return enum_for(:validate, data, schema, pointer, parent_uri) unless block_given?
50
-
51
- return if schema == true
52
- if schema == false
53
- yield error(data, schema, pointer, 'schema')
54
- return
55
- end
56
-
57
- return if schema.empty?
58
-
59
- type = schema['type']
60
- enum = schema['enum']
61
- all_of = schema['allOf']
62
- any_of = schema['anyOf']
63
- one_of = schema['oneOf']
64
- not_schema = schema['not']
65
- if_schema = schema['if']
66
- then_schema = schema['then']
67
- else_schema = schema['else']
68
- format = schema['format']
69
- ref = schema['$ref']
70
- id = schema['$id']
71
-
72
- parent_uri = join_uri(parent_uri, id)
73
-
74
- if ref
75
- validate_ref(data, schema, pointer, parent_uri, ref, &Proc.new)
76
- return
77
- end
78
-
79
- validate_format(data, schema, pointer, format, &Proc.new) if format && format?
80
-
81
- if keywords
82
- keywords.each do |keyword, callable|
83
- if schema.key?(keyword)
84
- result = callable.call(data, schema, pointer)
85
- if result.is_a?(Array)
86
- result.each { |error| yield error }
87
- elsif !result
88
- yield error(data, schema, pointer, keyword)
89
- end
90
- end
91
- end
92
- end
93
-
94
- yield error(data, schema, pointer, 'enum') if enum && !enum.include?(data)
95
- yield error(data, schema, pointer, 'const') if schema.key?('const') && schema['const'] != data
96
-
97
- yield error(data, schema, pointer, 'allOf') if all_of && !all_of.all? { |subschema| valid?(data, subschema, pointer, parent_uri) }
98
- yield error(data, schema, pointer, 'anyOf') if any_of && !any_of.any? { |subschema| valid?(data, subschema, pointer, parent_uri) }
99
- yield error(data, schema, pointer, 'oneOf') if one_of && !one_of.one? { |subschema| valid?(data, subschema, pointer, parent_uri) }
100
- yield error(data, schema, pointer, 'not') if !not_schema.nil? && valid?(data, not_schema, pointer, parent_uri)
101
-
102
- if if_schema && valid?(data, if_schema, pointer, parent_uri)
103
- yield error(data, schema, pointer, 'then') if !then_schema.nil? && !valid?(data, then_schema, pointer, parent_uri)
104
- elsif if_schema
105
- yield error(data, schema, pointer, 'else') if !else_schema.nil? && !valid?(data, else_schema, pointer, parent_uri)
106
- end
107
-
108
- case type
109
- when nil
110
- validate_class(data, schema, pointer, parent_uri, &Proc.new)
111
- when String
112
- validate_type(data, schema, pointer, parent_uri, type, &Proc.new)
113
- when Array
114
- if valid_type = type.find { |subtype| valid?(data, { 'type' => subtype }, pointer, parent_uri) }
115
- validate_type(data, schema, pointer, parent_uri, valid_type, &Proc.new)
116
- else
117
- yield error(data, schema, pointer, 'type')
118
- end
119
- end
120
- end
121
-
122
- protected
123
-
124
- def ids
125
- @ids ||= resolve_ids(root)
126
- end
127
-
128
- private
129
-
130
- attr_reader :root, :formats, :keywords, :ref_resolver
131
-
132
- def format?
133
- !!@format
134
- end
135
-
136
- def child(schema)
137
- self.class.new(
138
- schema,
139
- format: format?,
140
- formats: formats,
141
- keywords: keywords,
142
- ref_resolver: ref_resolver
143
- )
144
- end
145
-
146
- def error(data, schema, pointer, type)
147
- {
148
- 'data' => data,
149
- 'schema' => schema,
150
- 'pointer' => pointer,
151
- 'type' => type,
152
- }
153
- end
154
-
155
- def validate_class(data, schema, pointer, parent_uri)
156
- case data
157
- when Integer
158
- validate_integer(data, schema, pointer, &Proc.new)
159
- when Numeric
160
- validate_number(data, schema, pointer, &Proc.new)
161
- when String
162
- validate_string(data, schema, pointer, &Proc.new)
163
- when Array
164
- validate_array(data, schema, pointer, parent_uri, &Proc.new)
165
- when Hash
166
- validate_object(data, schema, pointer, parent_uri, &Proc.new)
167
- end
168
- end
169
-
170
- def validate_type(data, schema, pointer, parent_uri, type)
171
- case type
172
- when 'null'
173
- yield error(data, schema, pointer, 'null') unless data.nil?
174
- when 'boolean'
175
- yield error(data, schema, pointer, 'boolean') unless BOOLEANS.include?(data)
176
- when 'number'
177
- validate_number(data, schema, pointer, &Proc.new)
178
- when 'integer'
179
- validate_integer(data, schema, pointer, &Proc.new)
180
- when 'string'
181
- validate_string(data, schema, pointer, &Proc.new)
182
- when 'array'
183
- validate_array(data, schema, pointer, parent_uri, &Proc.new)
184
- when 'object'
185
- validate_object(data, schema, pointer, parent_uri, &Proc.new)
186
- end
187
- end
188
-
189
- def validate_ref(data, schema, pointer, parent_uri, ref)
190
- ref_uri = join_uri(parent_uri, ref)
191
-
192
- if valid_json_pointer?(ref_uri.fragment)
193
- ref_pointer = Hana::Pointer.new(URI.unescape(ref_uri.fragment || ''))
194
- if ref.start_with?('#')
195
- validate(data, ref_pointer.eval(root), pointer, pointer_uri(root, ref_pointer), &Proc.new)
196
- else
197
- ref_root = ref_resolver.call(ref_uri)
198
- ref_object = child(ref_root)
199
- ref_object.validate(data, ref_pointer.eval(ref_root), pointer, pointer_uri(ref_root, ref_pointer), &Proc.new)
200
- end
201
- elsif ids.key?(ref_uri.to_s)
202
- validate(data, ids.fetch(ref_uri.to_s), pointer, ref_uri, &Proc.new)
203
- else
204
- ref_root = ref_resolver.call(ref_uri)
205
- ref_object = child(ref_root)
206
- ref_object.validate(data, ref_object.ids.fetch(ref_uri.to_s, ref_root), pointer, ref_uri, &Proc.new)
207
- end
208
- end
209
-
210
- def validate_format(data, schema, pointer, format)
211
- valid = if formats && formats.key?(format)
212
- format_option = formats[format]
213
- format_option == false || format_option.call(data, schema)
214
- else
215
- case format
216
- when 'date-time'
217
- valid_date_time?(data)
218
- when 'date'
219
- valid_date_time?("#{data}T04:05:06.123456789+07:00")
220
- when 'time'
221
- valid_date_time?("2001-02-03T#{data}")
222
- when 'email'
223
- data.ascii_only? && valid_email?(data)
224
- when 'idn-email'
225
- valid_email?(data)
226
- when 'hostname'
227
- data.ascii_only? && valid_hostname?(data)
228
- when 'idn-hostname'
229
- valid_hostname?(data)
230
- when 'ipv4'
231
- valid_ip?(data, :v4)
232
- when 'ipv6'
233
- valid_ip?(data, :v6)
234
- when 'uri'
235
- data.ascii_only? && valid_iri?(data)
236
- when 'uri-reference'
237
- data.ascii_only? && (valid_iri?(data) || valid_iri_reference?(data))
238
- when 'iri'
239
- valid_iri?(data)
240
- when 'iri-reference'
241
- valid_iri?(data) || valid_iri_reference?(data)
242
- when 'uri-template'
243
- valid_uri_template?(data)
244
- when 'json-pointer'
245
- valid_json_pointer?(data)
246
- when 'relative-json-pointer'
247
- valid_relative_json_pointer?(data)
248
- when 'regex'
249
- EcmaReValidator.valid?(data)
250
- end
251
- end
252
- yield error(data, schema, pointer, 'format') unless valid
253
- end
254
-
255
- def validate_numeric(data, schema, pointer)
256
- multiple_of = schema['multipleOf']
257
- maximum = schema['maximum']
258
- exclusive_maximum = schema['exclusiveMaximum']
259
- minimum = schema['minimum']
260
- exclusive_minimum = schema['exclusiveMinimum']
261
-
262
- yield error(data, schema, pointer, 'maximum') if maximum && data > maximum
263
- yield error(data, schema, pointer, 'exclusiveMaximum') if exclusive_maximum && data >= exclusive_maximum
264
- yield error(data, schema, pointer, 'minimum') if minimum && data < minimum
265
- yield error(data, schema, pointer, 'exclusiveMinimum') if exclusive_minimum && data <= exclusive_minimum
266
-
267
- if multiple_of
268
- quotient = data / multiple_of.to_f
269
- yield error(data, schema, pointer, 'multipleOf') unless quotient.floor == quotient
270
- end
271
- end
272
-
273
- def validate_number(data, schema, pointer)
274
- unless data.is_a?(Numeric)
275
- yield error(data, schema, pointer, 'number')
276
- return
277
- end
278
-
279
- validate_numeric(data, schema, pointer, &Proc.new)
280
- end
281
-
282
- def validate_integer(data, schema, pointer)
283
- if !data.is_a?(Numeric) || (!data.is_a?(Integer) && data.floor != data)
284
- yield error(data, schema, pointer, 'integer')
285
- return
286
- end
287
-
288
- validate_numeric(data, schema, pointer, &Proc.new)
289
- end
290
-
291
- def validate_string(data, schema, pointer)
292
- unless data.is_a?(String)
293
- yield error(data, schema, pointer, 'string')
294
- return
295
- end
296
-
297
- max_length = schema['maxLength']
298
- min_length = schema['minLength']
299
- pattern = schema['pattern']
300
- content_encoding = schema['contentEncoding']
301
- content_media_type = schema['contentMediaType']
302
-
303
- yield error(data, schema, pointer, 'maxLength') if max_length && data.size > max_length
304
- yield error(data, schema, pointer, 'minLength') if min_length && data.size < min_length
305
- yield error(data, schema, pointer, 'pattern') if pattern && Regexp.new(pattern) !~ data
306
-
307
- if content_encoding || content_media_type
308
- decoded_data = data
309
-
310
- if content_encoding
311
- decoded_data = case content_encoding.downcase
312
- when 'base64'
313
- safe_strict_decode64(data)
314
- else # '7bit', '8bit', 'binary', 'quoted-printable'
315
- raise NotImplementedError
316
- end
317
- yield error(data, schema, pointer, 'contentEncoding') unless decoded_data
318
- end
319
-
320
- if content_media_type && decoded_data
321
- case content_media_type.downcase
322
- when 'application/json'
323
- yield error(data, schema, pointer, 'contentMediaType') unless valid_json?(decoded_data)
324
- else
325
- raise NotImplementedError
326
- end
327
- end
328
- end
329
- end
330
-
331
- def validate_array(data, schema, pointer, parent_uri, &block)
332
- unless data.is_a?(Array)
333
- yield error(data, schema, pointer, 'array')
334
- return
335
- end
336
-
337
- items = schema['items']
338
- additional_items = schema['additionalItems']
339
- max_items = schema['maxItems']
340
- min_items = schema['minItems']
341
- unique_items = schema['uniqueItems']
342
- contains = schema['contains']
343
-
344
- yield error(data, schema, pointer, 'maxItems') if max_items && data.size > max_items
345
- yield error(data, schema, pointer, 'minItems') if min_items && data.size < min_items
346
- yield error(data, schema, pointer, 'uniqueItems') if unique_items && data.size != data.uniq.size
347
- yield error(data, schema, pointer, 'contains') if !contains.nil? && data.all? { |item| !valid?(item, contains, pointer, parent_uri) }
348
-
349
- if items.is_a?(Array)
350
- data.each_with_index do |item, index|
351
- if index < items.size
352
- validate(item, items[index], "#{pointer}/#{index}", parent_uri, &block)
353
- elsif !additional_items.nil?
354
- validate(item, additional_items, "#{pointer}/#{index}", parent_uri, &block)
355
- else
356
- break
357
- end
358
- end
359
- elsif !items.nil?
360
- data.each_with_index do |item, index|
361
- validate(item, items, "#{pointer}/#{index}", parent_uri, &block)
362
- end
363
- end
364
- end
365
-
366
- def validate_object(data, schema, pointer, parent_uri, &block)
367
- unless data.is_a?(Hash)
368
- yield error(data, schema, pointer, 'object')
369
- return
370
- end
371
-
372
- max_properties = schema['maxProperties']
373
- min_properties = schema['minProperties']
374
- required = schema['required']
375
- properties = schema['properties']
376
- pattern_properties = schema['patternProperties']
377
- additional_properties = schema['additionalProperties']
378
- dependencies = schema['dependencies']
379
- property_names = schema['propertyNames']
380
-
381
- if dependencies
382
- dependencies.each do |key, value|
383
- next unless data.key?(key)
384
- subschema = value.is_a?(Array) ? { 'required' => value } : value
385
- validate(data, subschema, pointer, parent_uri, &block)
386
- end
387
- end
388
-
389
- yield error(data, schema, pointer, 'maxProperties') if max_properties && data.size > max_properties
390
- yield error(data, schema, pointer, 'minProperties') if min_properties && data.size < min_properties
391
- yield error(data, schema, pointer, 'required') if required && required.any? { |key| !data.key?(key) }
392
-
393
- regex_pattern_properties = nil
394
- data.each do |key, value|
395
- validate(key, property_names, pointer, parent_uri, &block) unless property_names.nil?
396
-
397
- matched_key = false
398
-
399
- if properties && properties.key?(key)
400
- validate(value, properties[key], "#{pointer}/#{key}", parent_uri, &block)
401
- matched_key = true
402
- end
403
-
404
- if pattern_properties
405
- regex_pattern_properties ||= pattern_properties.map do |pattern, property_schema|
406
- [Regexp.new(pattern), property_schema]
407
- end
408
- regex_pattern_properties.each do |regex, property_schema|
409
- if regex =~ key
410
- validate(value, property_schema, "#{pointer}/#{key}", parent_uri, &block)
411
- matched_key = true
412
- end
413
- end
414
- end
415
-
416
- next if matched_key
417
-
418
- validate(value, additional_properties, "#{pointer}/#{key}", parent_uri, &block) unless additional_properties.nil?
419
- end
420
- end
421
-
422
- def safe_strict_decode64(data)
423
- begin
424
- Base64.strict_decode64(data)
425
- rescue ArgumentError => e
426
- raise e unless e.message == 'invalid base64'
427
- nil
428
- end
429
- end
430
-
431
- def valid_json?(data)
432
- JSON.parse(data)
433
- true
434
- rescue JSON::ParserError
435
- false
436
- end
437
-
438
- def valid_date_time?(data)
439
- DateTime.rfc3339(data)
440
- true
441
- rescue ArgumentError => e
442
- raise e unless e.message == 'invalid date'
443
- false
444
- end
445
-
446
- def valid_email?(data)
447
- !!(Format::EMAIL_REGEX =~ data)
448
- end
449
-
450
- def valid_hostname?(data)
451
- !!(Format::HOSTNAME_REGEX =~ data && data.split('.').all? { |label| label.size <= 63 })
452
- end
453
-
454
- def valid_ip?(data, type)
455
- ip_address = IPAddr.new(data)
456
- type == :v4 ? ip_address.ipv4? : ip_address.ipv6?
457
- rescue IPAddr::InvalidAddressError
458
- false
459
- end
460
-
461
- def valid_iri?(data)
462
- !!(Format::IRI =~ data)
463
- end
464
-
465
- def valid_iri_reference?(data)
466
- !!(Format::IRELATIVE_REF =~ data)
467
- end
468
-
469
- def valid_uri_template?(data)
470
- URITemplate.new(data)
471
- true
472
- rescue URITemplate::Invalid
473
- false
474
- end
475
-
476
- def valid_json_pointer?(data)
477
- !!(Format::JSON_POINTER_REGEX =~ data)
478
- end
479
-
480
- def valid_relative_json_pointer?(data)
481
- !!(Format::RELATIVE_JSON_POINTER_REGEX =~ data)
482
- end
11
+ class UnsupportedMetaSchema < StandardError; end
12
+ class UnknownRef < StandardError; end
483
13
 
484
- def join_uri(a, b)
485
- if a && b
486
- URI.join(a, b)
487
- elsif b
488
- URI.parse(b)
489
- else
490
- a
491
- end
492
- end
14
+ DRAFT_CLASS_BY_META_SCHEMA = {
15
+ 'http://json-schema.org/draft-04/schema#' => Schema::Draft4,
16
+ 'http://json-schema.org/draft-06/schema#' => Schema::Draft6,
17
+ 'http://json-schema.org/draft-07/schema#' => Schema::Draft7
18
+ }.freeze
493
19
 
494
- def pointer_uri(schema, pointer)
495
- uri_parts = nil
496
- pointer.reduce(schema) do |obj, token|
497
- next obj.fetch(token.to_i) if obj.is_a?(Array)
498
- if obj_id = obj['$id']
499
- uri_parts ||= []
500
- uri_parts << obj_id
501
- end
502
- obj.fetch(token)
503
- end
504
- uri_parts ? URI.join(*uri_parts) : nil
505
- end
20
+ DEFAULT_META_SCHEMA = 'http://json-schema.org/draft-07/schema#'
506
21
 
507
- def resolve_ids(schema, ids = {}, parent_uri = nil)
508
- if schema.is_a?(Array)
509
- schema.each { |subschema| resolve_ids(subschema, ids, parent_uri) }
510
- elsif schema.is_a?(Hash)
511
- id = schema['$id']
512
- uri = join_uri(parent_uri, id)
513
- ids[uri.to_s] = schema unless uri == parent_uri
514
- if definitions = schema['definitions']
515
- definitions.each_value { |subschema| resolve_ids(subschema, ids, uri) }
516
- end
517
- end
518
- ids
519
- end
22
+ def self.schema(schema, **options)
23
+ meta_schema = schema.is_a?(Hash) && schema.key?('$schema') ? schema['$schema'] : DEFAULT_META_SCHEMA
24
+ draft_class = DRAFT_CLASS_BY_META_SCHEMA[meta_schema] || raise(UnsupportedMetaSchema, meta_schema)
25
+ draft_class.new(schema, **options)
520
26
  end
521
27
  end