json_schemer 0.1.5 → 0.1.6
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/Gemfile.lock +2 -2
- data/README.md +10 -0
- data/lib/json_schemer.rb +34 -4
- data/lib/json_schemer/cached_ref_resolver.rb +15 -0
- data/lib/json_schemer/schema/base.rb +222 -106
- data/lib/json_schemer/schema/draft4.rb +8 -8
- data/lib/json_schemer/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8dc3f244ec43815a4207b9be2112cc76afee3cd37b96a3e1eb0fd9b189f8ad72
|
4
|
+
data.tar.gz: 3dcede7d0418d30e98fe75054000657bf03201a1fbf8231810506a2a91de1bbb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1ea22d403dd91c7c555d7fe949a25840255a1ed55af658aad703b965b79e063fe2ae5a1d82b3b3ac7a6a56cc32b5720ec0639d734235e934ccaef04f87a11146
|
7
|
+
data.tar.gz: 243e04ef109d923d9de753856943f2269548485de36f476e9d527be132c1f737abcd1ee6e74c6b3263bb95d9b03fcfc0a6f8a11c4249d538baab0d469c97652d
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -46,6 +46,16 @@ schemer.valid?({ 'abc' => 10 })
|
|
46
46
|
|
47
47
|
schemer.validate({ 'abc' => 10 }).to_a
|
48
48
|
# => [{"data"=>10, "schema"=>{"type"=>"integer", "minimum"=>11}, "pointer"=>"#/abc", "type"=>"minimum"}]
|
49
|
+
|
50
|
+
# schema files
|
51
|
+
|
52
|
+
schema = Pathname.new('/path/to/schema.json')
|
53
|
+
schemer = JSONSchemer.schema(schema)
|
54
|
+
|
55
|
+
# schema json string
|
56
|
+
|
57
|
+
schema = '{ "type": "integer" }'
|
58
|
+
schemer = JSONSchemer.schema(schema)
|
49
59
|
```
|
50
60
|
|
51
61
|
## Options
|
data/lib/json_schemer.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'json_schemer/version'
|
4
4
|
require 'json_schemer/format'
|
5
|
+
require 'json_schemer/cached_ref_resolver'
|
5
6
|
require 'json_schemer/schema/base'
|
6
7
|
require 'json_schemer/schema/draft4'
|
7
8
|
require 'json_schemer/schema/draft6'
|
@@ -10,6 +11,8 @@ require 'json_schemer/schema/draft7'
|
|
10
11
|
module JSONSchemer
|
11
12
|
class UnsupportedMetaSchema < StandardError; end
|
12
13
|
class UnknownRef < StandardError; end
|
14
|
+
class InvalidFileURI < StandardError; end
|
15
|
+
class InvalidSymbolKey < StandardError; end
|
13
16
|
|
14
17
|
DRAFT_CLASS_BY_META_SCHEMA = {
|
15
18
|
'http://json-schema.org/draft-04/schema#' => Schema::Draft4,
|
@@ -19,9 +22,36 @@ module JSONSchemer
|
|
19
22
|
|
20
23
|
DEFAULT_META_SCHEMA = 'http://json-schema.org/draft-07/schema#'
|
21
24
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
25
|
+
FILE_URI_REF_RESOLVER = proc do |uri|
|
26
|
+
raise InvalidFileURI, 'must use `file` scheme' unless uri.scheme == 'file'
|
27
|
+
raise InvalidFileURI, 'cannot have a host (use `file:///`)' if uri.host
|
28
|
+
JSON.parse(File.read(uri.path))
|
29
|
+
end
|
30
|
+
|
31
|
+
class << self
|
32
|
+
def schema(schema, **options)
|
33
|
+
case schema
|
34
|
+
when String
|
35
|
+
schema = JSON.parse(schema)
|
36
|
+
when Pathname
|
37
|
+
uri = URI.parse("file://#{schema.realpath}")
|
38
|
+
if options.key?(:ref_resolver)
|
39
|
+
schema = FILE_URI_REF_RESOLVER.call(uri)
|
40
|
+
else
|
41
|
+
ref_resolver = CachedRefResolver.new(&FILE_URI_REF_RESOLVER)
|
42
|
+
schema = ref_resolver.call(uri)
|
43
|
+
options[:ref_resolver] = ref_resolver
|
44
|
+
end
|
45
|
+
schema[draft_class(schema)::ID_KEYWORD] ||= uri.to_s
|
46
|
+
end
|
47
|
+
draft_class(schema).new(schema, **options)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def draft_class(schema)
|
53
|
+
meta_schema = schema.is_a?(Hash) && schema.key?('$schema') ? schema['$schema'] : DEFAULT_META_SCHEMA
|
54
|
+
DRAFT_CLASS_BY_META_SCHEMA[meta_schema] || raise(UnsupportedMetaSchema, meta_schema)
|
55
|
+
end
|
26
56
|
end
|
27
57
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JSONSchemer
|
4
|
+
class CachedRefResolver
|
5
|
+
def initialize(&ref_resolver)
|
6
|
+
@ref_resolver = ref_resolver
|
7
|
+
@cache = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(uri)
|
11
|
+
@cache[uri] = @ref_resolver.call(uri) unless @cache.key?(uri)
|
12
|
+
@cache[uri]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -12,6 +12,18 @@ module JSONSchemer
|
|
12
12
|
class Base
|
13
13
|
include Format
|
14
14
|
|
15
|
+
Instance = Struct.new(:data, :data_pointer, :schema, :schema_pointer, :parent_uri) do
|
16
|
+
def merge(
|
17
|
+
data: self.data,
|
18
|
+
data_pointer: self.data_pointer,
|
19
|
+
schema: self.schema,
|
20
|
+
schema_pointer: self.schema_pointer,
|
21
|
+
parent_uri: self.parent_uri
|
22
|
+
)
|
23
|
+
self.class.new(data, data_pointer, schema, schema_pointer, parent_uri)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
15
27
|
ID_KEYWORD = '$id'
|
16
28
|
DEFAULT_REF_RESOLVER = proc { |uri| raise UnknownRef, uri.to_s }.freeze
|
17
29
|
NET_HTTP_REF_RESOLVER = proc { |uri| JSON.parse(Net::HTTP.get(uri)) }.freeze
|
@@ -24,23 +36,36 @@ module JSONSchemer
|
|
24
36
|
keywords: nil,
|
25
37
|
ref_resolver: DEFAULT_REF_RESOLVER
|
26
38
|
)
|
39
|
+
raise InvalidSymbolKey, 'schemas must use string keys' if schema.is_a?(Hash) && schema.first.first.is_a?(Symbol)
|
27
40
|
@root = schema
|
28
41
|
@format = format
|
29
42
|
@formats = formats
|
30
43
|
@keywords = keywords
|
31
|
-
@ref_resolver = ref_resolver == 'net/http' ? NET_HTTP_REF_RESOLVER : ref_resolver
|
44
|
+
@ref_resolver = ref_resolver == 'net/http' ? CachedRefResolver.new(&NET_HTTP_REF_RESOLVER) : ref_resolver
|
32
45
|
end
|
33
46
|
|
34
|
-
def valid?(data
|
35
|
-
|
47
|
+
def valid?(data)
|
48
|
+
valid_instance?(Instance.new(data, '', root, '', nil))
|
36
49
|
end
|
37
50
|
|
38
|
-
def validate(data
|
39
|
-
|
51
|
+
def validate(data)
|
52
|
+
validate_instance(Instance.new(data, '', root, '', nil))
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
|
57
|
+
def valid_instance?(instance)
|
58
|
+
validate_instance(instance).none?
|
59
|
+
end
|
60
|
+
|
61
|
+
def validate_instance(instance)
|
62
|
+
return enum_for(:validate_instance, instance) unless block_given?
|
63
|
+
|
64
|
+
schema = instance.schema
|
40
65
|
|
41
66
|
return if schema == true
|
42
67
|
if schema == false
|
43
|
-
yield error(
|
68
|
+
yield error(instance, 'schema')
|
44
69
|
return
|
45
70
|
end
|
46
71
|
|
@@ -59,60 +84,60 @@ module JSONSchemer
|
|
59
84
|
ref = schema['$ref']
|
60
85
|
id = schema[id_keyword]
|
61
86
|
|
62
|
-
parent_uri = join_uri(parent_uri, id)
|
87
|
+
instance.parent_uri = join_uri(instance.parent_uri, id)
|
63
88
|
|
64
89
|
if ref
|
65
|
-
validate_ref(
|
90
|
+
validate_ref(instance, ref, &Proc.new)
|
66
91
|
return
|
67
92
|
end
|
68
93
|
|
69
94
|
if format? && custom_format?(format)
|
70
|
-
validate_custom_format(
|
95
|
+
validate_custom_format(instance, formats.fetch(format), &Proc.new)
|
71
96
|
end
|
72
97
|
|
73
98
|
if keywords
|
74
99
|
keywords.each do |keyword, callable|
|
75
100
|
if schema.key?(keyword)
|
76
|
-
result = callable.call(data, schema, pointer)
|
101
|
+
result = callable.call(data, schema, instance.pointer)
|
77
102
|
if result.is_a?(Array)
|
78
103
|
result.each { |error| yield error }
|
79
104
|
elsif !result
|
80
|
-
yield error(
|
105
|
+
yield error(instance, keyword)
|
81
106
|
end
|
82
107
|
end
|
83
108
|
end
|
84
109
|
end
|
85
110
|
|
86
|
-
|
87
|
-
|
111
|
+
data = instance.data
|
112
|
+
|
113
|
+
yield error(instance, 'enum') if enum && !enum.include?(data)
|
114
|
+
yield error(instance, 'const') if schema.key?('const') && schema['const'] != data
|
88
115
|
|
89
|
-
yield error(
|
90
|
-
yield error(
|
91
|
-
yield error(
|
92
|
-
yield error(
|
116
|
+
yield error(instance, 'allOf') if all_of && !all_of.all? { |subschema| valid_instance?(instance.merge(schema: subschema)) }
|
117
|
+
yield error(instance, 'anyOf') if any_of && !any_of.any? { |subschema| valid_instance?(instance.merge(schema: subschema)) }
|
118
|
+
yield error(instance, 'oneOf') if one_of && !one_of.one? { |subschema| valid_instance?(instance.merge(schema: subschema)) }
|
119
|
+
yield error(instance, 'not') if !not_schema.nil? && valid_instance?(instance.merge(schema: not_schema))
|
93
120
|
|
94
|
-
if if_schema &&
|
95
|
-
yield error(
|
121
|
+
if if_schema && valid_instance?(instance.merge(schema: if_schema))
|
122
|
+
yield error(instance, 'then') if !then_schema.nil? && !valid_instance?(instance.merge(schema: then_schema))
|
96
123
|
elsif if_schema
|
97
|
-
yield error(
|
124
|
+
yield error(instance, 'else') if !else_schema.nil? && !valid_instance?(instance.merge(schema: else_schema))
|
98
125
|
end
|
99
126
|
|
100
127
|
case type
|
101
128
|
when nil
|
102
|
-
validate_class(
|
129
|
+
validate_class(instance, &Proc.new)
|
103
130
|
when String
|
104
|
-
validate_type(
|
131
|
+
validate_type(instance, type, &Proc.new)
|
105
132
|
when Array
|
106
|
-
if valid_type = type.find { |subtype|
|
107
|
-
validate_type(
|
133
|
+
if valid_type = type.find { |subtype| valid_instance?(instance.merge(schema: { 'type' => subtype })) }
|
134
|
+
validate_type(instance, valid_type, &Proc.new)
|
108
135
|
else
|
109
|
-
yield error(
|
136
|
+
yield error(instance, 'type')
|
110
137
|
end
|
111
138
|
end
|
112
139
|
end
|
113
140
|
|
114
|
-
protected
|
115
|
-
|
116
141
|
def ids
|
117
142
|
@ids ||= resolve_ids(root)
|
118
143
|
end
|
@@ -147,125 +172,157 @@ module JSONSchemer
|
|
147
172
|
)
|
148
173
|
end
|
149
174
|
|
150
|
-
def error(
|
175
|
+
def error(instance, type)
|
151
176
|
{
|
152
|
-
'data' => data,
|
153
|
-
'
|
154
|
-
'
|
177
|
+
'data' => instance.data,
|
178
|
+
'data_pointer' => instance.data_pointer,
|
179
|
+
'schema' => instance.schema,
|
180
|
+
'schema_pointer' => instance.schema_pointer,
|
181
|
+
'root_schema' => root,
|
155
182
|
'type' => type,
|
156
183
|
}
|
157
184
|
end
|
158
185
|
|
159
|
-
def validate_class(
|
160
|
-
case data
|
186
|
+
def validate_class(instance)
|
187
|
+
case instance.data
|
161
188
|
when Integer
|
162
|
-
validate_integer(
|
189
|
+
validate_integer(instance, &Proc.new)
|
163
190
|
when Numeric
|
164
|
-
validate_number(
|
191
|
+
validate_number(instance, &Proc.new)
|
165
192
|
when String
|
166
|
-
validate_string(
|
193
|
+
validate_string(instance, &Proc.new)
|
167
194
|
when Array
|
168
|
-
validate_array(
|
195
|
+
validate_array(instance, &Proc.new)
|
169
196
|
when Hash
|
170
|
-
validate_object(
|
197
|
+
validate_object(instance, &Proc.new)
|
171
198
|
end
|
172
199
|
end
|
173
200
|
|
174
|
-
def validate_type(
|
201
|
+
def validate_type(instance, type)
|
175
202
|
case type
|
176
203
|
when 'null'
|
177
|
-
yield error(
|
204
|
+
yield error(instance, 'null') unless instance.data.nil?
|
178
205
|
when 'boolean'
|
179
|
-
yield error(
|
206
|
+
yield error(instance, 'boolean') unless BOOLEANS.include?(instance.data)
|
180
207
|
when 'number'
|
181
|
-
validate_number(
|
208
|
+
validate_number(instance, &Proc.new)
|
182
209
|
when 'integer'
|
183
|
-
validate_integer(
|
210
|
+
validate_integer(instance, &Proc.new)
|
184
211
|
when 'string'
|
185
|
-
validate_string(
|
212
|
+
validate_string(instance, &Proc.new)
|
186
213
|
when 'array'
|
187
|
-
validate_array(
|
214
|
+
validate_array(instance, &Proc.new)
|
188
215
|
when 'object'
|
189
|
-
validate_object(
|
216
|
+
validate_object(instance, &Proc.new)
|
190
217
|
end
|
191
218
|
end
|
192
219
|
|
193
|
-
def validate_ref(
|
194
|
-
ref_uri = join_uri(parent_uri, ref)
|
220
|
+
def validate_ref(instance, ref)
|
221
|
+
ref_uri = join_uri(instance.parent_uri, ref)
|
195
222
|
|
196
223
|
if valid_json_pointer?(ref_uri.fragment)
|
197
|
-
ref_pointer = Hana::Pointer.new(URI.unescape(ref_uri.fragment
|
224
|
+
ref_pointer = Hana::Pointer.new(URI.unescape(ref_uri.fragment))
|
198
225
|
if ref.start_with?('#')
|
199
|
-
|
226
|
+
subinstance = instance.merge(
|
227
|
+
schema: ref_pointer.eval(root),
|
228
|
+
schema_pointer: ref_uri.fragment,
|
229
|
+
parent_uri: pointer_uri(root, ref_pointer)
|
230
|
+
)
|
231
|
+
validate_instance(subinstance, &Proc.new)
|
200
232
|
else
|
201
233
|
ref_root = ref_resolver.call(ref_uri)
|
202
234
|
ref_object = child(ref_root)
|
203
|
-
|
235
|
+
subinstance = instance.merge(
|
236
|
+
schema: ref_pointer.eval(ref_root),
|
237
|
+
schema_pointer: ref_uri.fragment,
|
238
|
+
parent_uri: pointer_uri(ref_root, ref_pointer)
|
239
|
+
)
|
240
|
+
ref_object.validate_instance(subinstance, &Proc.new)
|
204
241
|
end
|
205
|
-
elsif ids
|
206
|
-
|
242
|
+
elsif id = ids[ref_uri.to_s]
|
243
|
+
subinstance = instance.merge(
|
244
|
+
schema: id.fetch(:schema),
|
245
|
+
schema_pointer: id.fetch(:pointer),
|
246
|
+
parent_uri: ref_uri
|
247
|
+
)
|
248
|
+
validate_instance(subinstance, &Proc.new)
|
207
249
|
else
|
208
250
|
ref_root = ref_resolver.call(ref_uri)
|
209
251
|
ref_object = child(ref_root)
|
210
|
-
|
252
|
+
id = ref_object.ids[ref_uri.to_s] || { schema: ref_root, pointer: '' }
|
253
|
+
subinstance = instance.merge(
|
254
|
+
schema: id.fetch(:schema),
|
255
|
+
schema_pointer: id.fetch(:pointer),
|
256
|
+
parent_uri: ref_uri
|
257
|
+
)
|
258
|
+
ref_object.validate_instance(subinstance, &Proc.new)
|
211
259
|
end
|
212
260
|
end
|
213
261
|
|
214
|
-
def validate_custom_format(
|
215
|
-
yield error(
|
262
|
+
def validate_custom_format(instance, custom_format)
|
263
|
+
yield error(instance, 'format') if custom_format != false && !custom_format.call(instance.data, instance.schema)
|
216
264
|
end
|
217
265
|
|
218
|
-
def validate_exclusive_maximum(
|
219
|
-
yield error(
|
266
|
+
def validate_exclusive_maximum(instance, exclusive_maximum, maximum)
|
267
|
+
yield error(instance, 'exclusiveMaximum') if instance.data >= exclusive_maximum
|
220
268
|
end
|
221
269
|
|
222
|
-
def validate_exclusive_minimum(
|
223
|
-
yield error(
|
270
|
+
def validate_exclusive_minimum(instance, exclusive_minimum, minimum)
|
271
|
+
yield error(instance, 'exclusiveMinimum') if instance.data <= exclusive_minimum
|
224
272
|
end
|
225
273
|
|
226
|
-
def validate_numeric(
|
274
|
+
def validate_numeric(instance)
|
275
|
+
schema = instance.schema
|
276
|
+
data = instance.data
|
277
|
+
|
227
278
|
multiple_of = schema['multipleOf']
|
228
279
|
maximum = schema['maximum']
|
229
280
|
exclusive_maximum = schema['exclusiveMaximum']
|
230
281
|
minimum = schema['minimum']
|
231
282
|
exclusive_minimum = schema['exclusiveMinimum']
|
232
283
|
|
233
|
-
yield error(
|
234
|
-
yield error(
|
284
|
+
yield error(instance, 'maximum') if maximum && data > maximum
|
285
|
+
yield error(instance, 'minimum') if minimum && data < minimum
|
235
286
|
|
236
|
-
validate_exclusive_maximum(
|
237
|
-
validate_exclusive_minimum(
|
287
|
+
validate_exclusive_maximum(instance, exclusive_maximum, maximum, &Proc.new) if exclusive_maximum
|
288
|
+
validate_exclusive_minimum(instance, exclusive_minimum, minimum, &Proc.new) if exclusive_minimum
|
238
289
|
|
239
290
|
if multiple_of
|
240
291
|
quotient = data / multiple_of.to_f
|
241
|
-
yield error(
|
292
|
+
yield error(instance, 'multipleOf') unless quotient.floor == quotient
|
242
293
|
end
|
243
294
|
end
|
244
295
|
|
245
|
-
def validate_number(
|
246
|
-
unless data.is_a?(Numeric)
|
247
|
-
yield error(
|
296
|
+
def validate_number(instance)
|
297
|
+
unless instance.data.is_a?(Numeric)
|
298
|
+
yield error(instance, 'number')
|
248
299
|
return
|
249
300
|
end
|
250
301
|
|
251
|
-
validate_numeric(
|
302
|
+
validate_numeric(instance, &Proc.new)
|
252
303
|
end
|
253
304
|
|
254
|
-
def validate_integer(
|
305
|
+
def validate_integer(instance)
|
306
|
+
data = instance.data
|
307
|
+
|
255
308
|
if !data.is_a?(Numeric) || (!data.is_a?(Integer) && data.floor != data)
|
256
|
-
yield error(
|
309
|
+
yield error(instance, 'integer')
|
257
310
|
return
|
258
311
|
end
|
259
312
|
|
260
|
-
validate_numeric(
|
313
|
+
validate_numeric(instance, &Proc.new)
|
261
314
|
end
|
262
315
|
|
263
|
-
def validate_string(
|
316
|
+
def validate_string(instance)
|
317
|
+
data = instance.data
|
318
|
+
|
264
319
|
unless data.is_a?(String)
|
265
|
-
yield error(
|
320
|
+
yield error(instance, 'string')
|
266
321
|
return
|
267
322
|
end
|
268
323
|
|
324
|
+
schema = instance.schema
|
325
|
+
|
269
326
|
max_length = schema['maxLength']
|
270
327
|
min_length = schema['minLength']
|
271
328
|
pattern = schema['pattern']
|
@@ -273,10 +330,10 @@ module JSONSchemer
|
|
273
330
|
content_encoding = schema['contentEncoding']
|
274
331
|
content_media_type = schema['contentMediaType']
|
275
332
|
|
276
|
-
yield error(
|
277
|
-
yield error(
|
278
|
-
yield error(
|
279
|
-
yield error(
|
333
|
+
yield error(instance, 'maxLength') if max_length && data.size > max_length
|
334
|
+
yield error(instance, 'minLength') if min_length && data.size < min_length
|
335
|
+
yield error(instance, 'pattern') if pattern && Regexp.new(pattern) !~ data
|
336
|
+
yield error(instance, 'format') if format? && spec_format?(format) && !valid_spec_format?(data, format)
|
280
337
|
|
281
338
|
if content_encoding || content_media_type
|
282
339
|
decoded_data = data
|
@@ -288,13 +345,13 @@ module JSONSchemer
|
|
288
345
|
else # '7bit', '8bit', 'binary', 'quoted-printable'
|
289
346
|
raise NotImplementedError
|
290
347
|
end
|
291
|
-
yield error(
|
348
|
+
yield error(instance, 'contentEncoding') unless decoded_data
|
292
349
|
end
|
293
350
|
|
294
351
|
if content_media_type && decoded_data
|
295
352
|
case content_media_type.downcase
|
296
353
|
when 'application/json'
|
297
|
-
yield error(
|
354
|
+
yield error(instance, 'contentMediaType') unless valid_json?(decoded_data)
|
298
355
|
else
|
299
356
|
raise NotImplementedError
|
300
357
|
end
|
@@ -302,12 +359,16 @@ module JSONSchemer
|
|
302
359
|
end
|
303
360
|
end
|
304
361
|
|
305
|
-
def validate_array(
|
362
|
+
def validate_array(instance, &block)
|
363
|
+
data = instance.data
|
364
|
+
|
306
365
|
unless data.is_a?(Array)
|
307
|
-
yield error(
|
366
|
+
yield error(instance, 'array')
|
308
367
|
return
|
309
368
|
end
|
310
369
|
|
370
|
+
schema = instance.schema
|
371
|
+
|
311
372
|
items = schema['items']
|
312
373
|
additional_items = schema['additionalItems']
|
313
374
|
max_items = schema['maxItems']
|
@@ -315,34 +376,56 @@ module JSONSchemer
|
|
315
376
|
unique_items = schema['uniqueItems']
|
316
377
|
contains = schema['contains']
|
317
378
|
|
318
|
-
yield error(
|
319
|
-
yield error(
|
320
|
-
yield error(
|
321
|
-
yield error(
|
379
|
+
yield error(instance, 'maxItems') if max_items && data.size > max_items
|
380
|
+
yield error(instance, 'minItems') if min_items && data.size < min_items
|
381
|
+
yield error(instance, 'uniqueItems') if unique_items && data.size != data.uniq.size
|
382
|
+
yield error(instance, 'contains') if !contains.nil? && data.all? { |item| !valid_instance?(instance.merge(data: item, schema: contains)) }
|
322
383
|
|
323
384
|
if items.is_a?(Array)
|
324
385
|
data.each_with_index do |item, index|
|
325
386
|
if index < items.size
|
326
|
-
|
387
|
+
subinstance = instance.merge(
|
388
|
+
data: item,
|
389
|
+
data_pointer: "#{instance.data_pointer}/#{index}",
|
390
|
+
schema: items[index],
|
391
|
+
schema_pointer: "#{instance.schema_pointer}/items/#{index}"
|
392
|
+
)
|
393
|
+
validate_instance(subinstance, &block)
|
327
394
|
elsif !additional_items.nil?
|
328
|
-
|
395
|
+
subinstance = instance.merge(
|
396
|
+
data: item,
|
397
|
+
data_pointer: "#{instance.data_pointer}/#{index}",
|
398
|
+
schema: additional_items,
|
399
|
+
schema_pointer: "#{instance.schema_pointer}/additionalItems"
|
400
|
+
)
|
401
|
+
validate_instance(subinstance, &block)
|
329
402
|
else
|
330
403
|
break
|
331
404
|
end
|
332
405
|
end
|
333
406
|
elsif !items.nil?
|
334
407
|
data.each_with_index do |item, index|
|
335
|
-
|
408
|
+
subinstance = instance.merge(
|
409
|
+
data: item,
|
410
|
+
data_pointer: "#{instance.data_pointer}/#{index}",
|
411
|
+
schema: items,
|
412
|
+
schema_pointer: "#{instance.schema_pointer}/items"
|
413
|
+
)
|
414
|
+
validate_instance(subinstance, &block)
|
336
415
|
end
|
337
416
|
end
|
338
417
|
end
|
339
418
|
|
340
|
-
def validate_object(
|
419
|
+
def validate_object(instance, &block)
|
420
|
+
data = instance.data
|
421
|
+
|
341
422
|
unless data.is_a?(Hash)
|
342
|
-
yield error(
|
423
|
+
yield error(instance, 'object')
|
343
424
|
return
|
344
425
|
end
|
345
426
|
|
427
|
+
schema = instance.schema
|
428
|
+
|
346
429
|
max_properties = schema['maxProperties']
|
347
430
|
min_properties = schema['minProperties']
|
348
431
|
required = schema['required']
|
@@ -356,32 +439,52 @@ module JSONSchemer
|
|
356
439
|
dependencies.each do |key, value|
|
357
440
|
next unless data.key?(key)
|
358
441
|
subschema = value.is_a?(Array) ? { 'required' => value } : value
|
359
|
-
|
442
|
+
subinstance = instance.merge(schema: subschema, schema_pointer: "#{instance.schema_pointer}/dependencies/#{key}")
|
443
|
+
validate_instance(subinstance, &block)
|
360
444
|
end
|
361
445
|
end
|
362
446
|
|
363
|
-
yield error(
|
364
|
-
yield error(
|
365
|
-
yield error(
|
447
|
+
yield error(instance, 'maxProperties') if max_properties && data.size > max_properties
|
448
|
+
yield error(instance, 'minProperties') if min_properties && data.size < min_properties
|
449
|
+
yield error(instance, 'required') if required && required.any? { |key| !data.key?(key) }
|
366
450
|
|
367
451
|
regex_pattern_properties = nil
|
368
452
|
data.each do |key, value|
|
369
|
-
|
453
|
+
unless property_names.nil?
|
454
|
+
subinstance = instance.merge(
|
455
|
+
data: key,
|
456
|
+
schema: property_names,
|
457
|
+
schema_pointer: "#{instance.schema_pointer}/propertyNames"
|
458
|
+
)
|
459
|
+
validate_instance(subinstance, &block)
|
460
|
+
end
|
370
461
|
|
371
462
|
matched_key = false
|
372
463
|
|
373
464
|
if properties && properties.key?(key)
|
374
|
-
|
465
|
+
subinstance = instance.merge(
|
466
|
+
data: value,
|
467
|
+
data_pointer: "#{instance.data_pointer}/#{key}",
|
468
|
+
schema: properties[key],
|
469
|
+
schema_pointer: "#{instance.schema_pointer}/properties/#{key}"
|
470
|
+
)
|
471
|
+
validate_instance(subinstance, &block)
|
375
472
|
matched_key = true
|
376
473
|
end
|
377
474
|
|
378
475
|
if pattern_properties
|
379
476
|
regex_pattern_properties ||= pattern_properties.map do |pattern, property_schema|
|
380
|
-
[Regexp.new(pattern), property_schema]
|
477
|
+
[pattern, Regexp.new(pattern), property_schema]
|
381
478
|
end
|
382
|
-
regex_pattern_properties.each do |regex, property_schema|
|
479
|
+
regex_pattern_properties.each do |pattern, regex, property_schema|
|
383
480
|
if regex =~ key
|
384
|
-
|
481
|
+
subinstance = instance.merge(
|
482
|
+
data: value,
|
483
|
+
data_pointer: "#{instance.data_pointer}/#{key}",
|
484
|
+
schema: property_schema,
|
485
|
+
schema_pointer: "#{instance.schema_pointer}/patternProperties/#{pattern}"
|
486
|
+
)
|
487
|
+
validate_instance(subinstance, &block)
|
385
488
|
matched_key = true
|
386
489
|
end
|
387
490
|
end
|
@@ -389,7 +492,15 @@ module JSONSchemer
|
|
389
492
|
|
390
493
|
next if matched_key
|
391
494
|
|
392
|
-
|
495
|
+
unless additional_properties.nil?
|
496
|
+
subinstance = instance.merge(
|
497
|
+
data: value,
|
498
|
+
data_pointer: "#{instance.data_pointer}/#{key}",
|
499
|
+
schema: additional_properties,
|
500
|
+
schema_pointer: "#{instance.schema_pointer}/additionalProperties"
|
501
|
+
)
|
502
|
+
validate_instance(subinstance, &block)
|
503
|
+
end
|
393
504
|
end
|
394
505
|
end
|
395
506
|
|
@@ -425,15 +536,20 @@ module JSONSchemer
|
|
425
536
|
uri_parts ? URI.join(*uri_parts) : nil
|
426
537
|
end
|
427
538
|
|
428
|
-
def resolve_ids(schema, ids = {}, parent_uri = nil)
|
539
|
+
def resolve_ids(schema, ids = {}, parent_uri = nil, pointer = '')
|
429
540
|
if schema.is_a?(Array)
|
430
|
-
schema.
|
541
|
+
schema.each_with_index { |subschema, index| resolve_ids(subschema, ids, parent_uri, "#{pointer}/#{index}") }
|
431
542
|
elsif schema.is_a?(Hash)
|
432
543
|
id = schema[id_keyword]
|
433
544
|
uri = join_uri(parent_uri, id)
|
434
|
-
|
545
|
+
unless uri == parent_uri
|
546
|
+
ids[uri.to_s] = {
|
547
|
+
schema: schema,
|
548
|
+
pointer: pointer
|
549
|
+
}
|
550
|
+
end
|
435
551
|
if definitions = schema['definitions']
|
436
|
-
definitions.
|
552
|
+
definitions.each { |key, subschema| resolve_ids(subschema, ids, uri, "#{pointer}/definitions/#{key}") }
|
437
553
|
end
|
438
554
|
end
|
439
555
|
ids
|
@@ -24,21 +24,21 @@ module JSONSchemer
|
|
24
24
|
SUPPORTED_FORMATS.include?(format)
|
25
25
|
end
|
26
26
|
|
27
|
-
def validate_exclusive_maximum(
|
28
|
-
yield error(
|
27
|
+
def validate_exclusive_maximum(instance, exclusive_maximum, maximum)
|
28
|
+
yield error(instance, 'exclusiveMaximum') if exclusive_maximum && instance.data >= maximum
|
29
29
|
end
|
30
30
|
|
31
|
-
def validate_exclusive_minimum(
|
32
|
-
yield error(
|
31
|
+
def validate_exclusive_minimum(instance, exclusive_minimum, minimum)
|
32
|
+
yield error(instance, 'exclusiveMinimum') if exclusive_minimum && instance.data <= minimum
|
33
33
|
end
|
34
34
|
|
35
|
-
def validate_integer(
|
36
|
-
if !data.is_a?(Integer)
|
37
|
-
yield error(
|
35
|
+
def validate_integer(instance)
|
36
|
+
if !instance.data.is_a?(Integer)
|
37
|
+
yield error(instance, 'integer')
|
38
38
|
return
|
39
39
|
end
|
40
40
|
|
41
|
-
validate_numeric(
|
41
|
+
validate_numeric(instance, &Proc.new)
|
42
42
|
end
|
43
43
|
end
|
44
44
|
end
|
data/lib/json_schemer/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: json_schemer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Harsha
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-08-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -113,6 +113,7 @@ files:
|
|
113
113
|
- bin/setup
|
114
114
|
- json_schemer.gemspec
|
115
115
|
- lib/json_schemer.rb
|
116
|
+
- lib/json_schemer/cached_ref_resolver.rb
|
116
117
|
- lib/json_schemer/format.rb
|
117
118
|
- lib/json_schemer/schema/base.rb
|
118
119
|
- lib/json_schemer/schema/draft4.rb
|
@@ -139,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
139
140
|
version: '0'
|
140
141
|
requirements: []
|
141
142
|
rubyforge_project:
|
142
|
-
rubygems_version: 2.7.
|
143
|
+
rubygems_version: 2.7.6
|
143
144
|
signing_key:
|
144
145
|
specification_version: 4
|
145
146
|
summary: JSON Schema validator. Supports drafts 4, 6, and 7.
|