json_schemer 0.1.3 → 0.1.4
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/.gitmodules +1 -1
- data/.travis.yml +6 -1
- data/Gemfile.lock +1 -1
- data/README.md +6 -4
- data/json_schemer.gemspec +1 -1
- data/lib/json_schemer.rb +18 -512
- data/lib/json_schemer/format.rb +158 -58
- data/lib/json_schemer/schema/base.rb +437 -0
- data/lib/json_schemer/schema/draft4.rb +44 -0
- data/lib/json_schemer/schema/draft6.rb +25 -0
- data/lib/json_schemer/schema/draft7.rb +33 -0
- data/lib/json_schemer/version.rb +3 -1
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6f1555ed0ddacfa334c2a2635b4cce09c532aae3121539c28086e27ddf3c22fe
|
4
|
+
data.tar.gz: 5e49a8c68c44f40ac3ac9b3766b49f8f1c5d21ff5560dd1e2dd8f3d994183655
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e5e79032c18e1e4f9ba1a21be3d7b6afc48a7decdd37f7b2f9d4a9984b85684b1b115e2b16a4497955ec0d0d696d082dcd346f0443b3494f415718323f3beb86
|
7
|
+
data.tar.gz: 229375ee9837355c3225080db7b88eed7dce26a3fb608ded003453b9765de4a3d1546d3ea0eb6e7649698fcbe8ba58b49157cab7817f6a71ae66963abb5f45a4
|
data/.gitmodules
CHANGED
data/.travis.yml
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# JSONSchemer
|
2
2
|
|
3
|
-
JSON Schema
|
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
|
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
|
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
|
-
|
74
|
+
## Build Status
|
75
|
+
|
76
|
+
[](https://travis-ci.org/davishmcclurg/json_schemer)
|
75
77
|
|
76
78
|
## Contributing
|
77
79
|
|
data/json_schemer.gemspec
CHANGED
@@ -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
|
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
|
|
data/lib/json_schemer.rb
CHANGED
@@ -1,521 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
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
|
18
|
-
|
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
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
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
|
-
|
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
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
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
|