json_schemer 0.1.10 → 0.2.0

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: 137a80ea5bf74eb23f2539a1ee4beb5752d6e17cb751101a764ef838239187ba
4
- data.tar.gz: e901305179283ddeb9d4f8922cde1c0ad274b648d0147383cf2c6e93e395783e
3
+ metadata.gz: 99e547dd87197c33f01403e652c5ddecdb1dc39df0e63071a588135a94100960
4
+ data.tar.gz: d2b6d281eaa5d6d228ef5a90ea1d8ddbd22574f72d638c83d3fe48b1fd50825c
5
5
  SHA512:
6
- metadata.gz: d6a0543fa428a1fcaa7081d9a9ef2394f350ad9d20c7a4878445869e5d35b8e3bd8a8f23ce71acbb46347bfb102ccbf3aec0d41bd72f58f458a3453d5b1b9b1f
7
- data.tar.gz: 3d1b65df9ca5b2c225d142faa4fd2653e61bca660cd26ba775fc97fc10602aa2bd464a14f404d5a0a0ce7a30641db3f3e4009b738ea2246fe0918434bd4399d8
6
+ metadata.gz: 2a506748ca559c13ae0c05204011be6bd5650c8982ef2fdd4e4874bc094664201a7d5856f1cceea5b5773956c74cb4d7e1a68c9b1c8165e3c1ce40d48fcef6ed
7
+ data.tar.gz: ae1eaa5e38b44e88244ab51782184f397050ce07d0ace89ef450777d4be4a7c1c2003b7d8e66df7c9f1a373b2ef1b1a27c0b8c5d26d03cbabe01645f3ecf6894
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- json_schemer (0.1.10)
4
+ json_schemer (0.2.0)
5
5
  ecma-re-validator (~> 0.2.0)
6
6
  hana (~> 1.3.3)
7
7
  regexp_parser (~> 1.2.0)
@@ -12,7 +12,7 @@ GEM
12
12
  specs:
13
13
  ecma-re-validator (0.2.0)
14
14
  regexp_parser (~> 1.2)
15
- hana (1.3.4)
15
+ hana (1.3.5)
16
16
  minitest (5.11.1)
17
17
  rake (10.5.0)
18
18
  regexp_parser (1.2.0)
data/lib/json_schemer.rb CHANGED
@@ -1,5 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'base64'
4
+ require 'json'
5
+ require 'net/http'
6
+ require 'pathname'
7
+ require 'time'
8
+ require 'uri'
9
+
10
+ require 'ecma-re-validator'
11
+ require 'hana'
12
+ require 'ipaddr'
13
+ require 'uri_template'
14
+
3
15
  require 'json_schemer/version'
4
16
  require 'json_schemer/format'
5
17
  require 'json_schemer/cached_ref_resolver'
@@ -7,11 +19,11 @@ require 'json_schemer/schema/base'
7
19
  require 'json_schemer/schema/draft4'
8
20
  require 'json_schemer/schema/draft6'
9
21
  require 'json_schemer/schema/draft7'
10
- require 'pathname'
11
22
 
12
23
  module JSONSchemer
13
24
  class UnsupportedMetaSchema < StandardError; end
14
25
  class UnknownRef < StandardError; end
26
+ class InvalidRefResolution < StandardError; end
15
27
  class InvalidFileURI < StandardError; end
16
28
  class InvalidSymbolKey < StandardError; end
17
29
 
@@ -1,11 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'ecma-re-validator'
4
- require 'ipaddr'
5
- require 'json'
6
- require 'time'
7
- require 'uri_template'
8
-
9
3
  module JSONSchemer
10
4
  module Format
11
5
  # this is no good
@@ -1,12 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'base64'
4
- require 'hana'
5
- require 'json'
6
- require 'net/http'
7
- require 'time'
8
- require 'uri'
9
-
10
3
  module JSONSchemer
11
4
  module Schema
12
5
  class Base
@@ -25,8 +18,8 @@ module JSONSchemer
25
18
  end
26
19
 
27
20
  ID_KEYWORD = '$id'
28
- DEFAULT_REF_RESOLVER = proc { |uri| raise UnknownRef, uri.to_s }.freeze
29
- NET_HTTP_REF_RESOLVER = proc { |uri| JSON.parse(Net::HTTP.get(uri)) }.freeze
21
+ DEFAULT_REF_RESOLVER = proc { |uri| raise UnknownRef, uri.to_s }
22
+ NET_HTTP_REF_RESOLVER = proc { |uri| JSON.parse(Net::HTTP.get(uri)) }
30
23
  BOOLEANS = Set[true, false].freeze
31
24
 
32
25
  RUBY_REGEX_ANCHORS_TO_ECMA_262 = {
@@ -43,7 +36,7 @@ module JSONSchemer
43
36
  keywords: nil,
44
37
  ref_resolver: DEFAULT_REF_RESOLVER
45
38
  )
46
- raise InvalidSymbolKey, 'schemas must use string keys' if schema.is_a?(Hash) && schema.first.first.is_a?(Symbol)
39
+ raise InvalidSymbolKey, 'schemas must use string keys' if schema.is_a?(Hash) && !schema.first.first.is_a?(String)
47
40
  @root = schema
48
41
  @format = format
49
42
  @formats = formats
@@ -65,18 +58,17 @@ module JSONSchemer
65
58
  validate_instance(instance).none?
66
59
  end
67
60
 
68
- def validate_instance(instance)
61
+ def validate_instance(instance, &block)
69
62
  return enum_for(:validate_instance, instance) unless block_given?
70
63
 
71
64
  schema = instance.schema
72
65
 
73
- return if schema == true
74
66
  if schema == false
75
67
  yield error(instance, 'schema')
76
68
  return
77
69
  end
78
70
 
79
- return if schema.empty?
71
+ return if schema == true || schema.empty?
80
72
 
81
73
  type = schema['type']
82
74
  enum = schema['enum']
@@ -94,12 +86,12 @@ module JSONSchemer
94
86
  instance.parent_uri = join_uri(instance.parent_uri, id)
95
87
 
96
88
  if ref
97
- validate_ref(instance, ref, &Proc.new)
89
+ validate_ref(instance, ref, &block)
98
90
  return
99
91
  end
100
92
 
101
93
  if format? && custom_format?(format)
102
- validate_custom_format(instance, formats.fetch(format), &Proc.new)
94
+ validate_custom_format(instance, formats.fetch(format), &block)
103
95
  end
104
96
 
105
97
  if keywords
@@ -107,7 +99,7 @@ module JSONSchemer
107
99
  if schema.key?(keyword)
108
100
  result = callable.call(data, schema, instance.pointer)
109
101
  if result.is_a?(Array)
110
- result.each { |error| yield error }
102
+ result.each(&block)
111
103
  elsif !result
112
104
  yield error(instance, keyword)
113
105
  end
@@ -120,25 +112,50 @@ module JSONSchemer
120
112
  yield error(instance, 'enum') if enum && !enum.include?(data)
121
113
  yield error(instance, 'const') if schema.key?('const') && schema['const'] != data
122
114
 
123
- yield error(instance, 'allOf') if all_of && !all_of.all? { |subschema| valid_instance?(instance.merge(schema: subschema)) }
124
- yield error(instance, 'anyOf') if any_of && !any_of.any? { |subschema| valid_instance?(instance.merge(schema: subschema)) }
125
- yield error(instance, 'oneOf') if one_of && !one_of.one? { |subschema| valid_instance?(instance.merge(schema: subschema)) }
126
- yield error(instance, 'not') if !not_schema.nil? && valid_instance?(instance.merge(schema: not_schema))
115
+ if all_of
116
+ all_of.each_with_index do |subschema, index|
117
+ validate_instance(instance.merge(schema: subschema, schema_pointer: "#{instance.schema_pointer}/allOf/#{index}"), &block)
118
+ end
119
+ end
120
+
121
+ if any_of
122
+ subschemas = any_of.lazy.with_index.map do |subschema, index|
123
+ validate_instance(instance.merge(schema: subschema, schema_pointer: "#{instance.schema_pointer}/anyOf/#{index}"))
124
+ end
125
+ subschemas.each { |subschema| subschema.each(&block) } unless subschemas.any?(&:none?)
126
+ end
127
+
128
+ if one_of
129
+ subschemas = one_of.map.with_index do |subschema, index|
130
+ validate_instance(instance.merge(schema: subschema, schema_pointer: "#{instance.schema_pointer}/oneOf/#{index}"))
131
+ end
132
+ valid_subschema_count = subschemas.count(&:none?)
133
+ if valid_subschema_count > 1
134
+ yield error(instance, 'oneOf')
135
+ elsif valid_subschema_count == 0
136
+ subschemas.each { |subschema| subschema.each(&block) }
137
+ end
138
+ end
139
+
140
+ unless not_schema.nil?
141
+ subinstance = instance.merge(schema: not_schema, schema_pointer: "#{instance.schema_pointer}/not")
142
+ yield error(subinstance, 'not') if valid_instance?(subinstance)
143
+ end
127
144
 
128
145
  if if_schema && valid_instance?(instance.merge(schema: if_schema))
129
- yield error(instance, 'then') if !then_schema.nil? && !valid_instance?(instance.merge(schema: then_schema))
146
+ validate_instance(instance.merge(schema: then_schema, schema_pointer: "#{instance.schema_pointer}/then"), &block) unless then_schema.nil?
130
147
  elsif if_schema
131
- yield error(instance, 'else') if !else_schema.nil? && !valid_instance?(instance.merge(schema: else_schema))
148
+ validate_instance(instance.merge(schema: else_schema, schema_pointer: "#{instance.schema_pointer}/else"), &block) unless else_schema.nil?
132
149
  end
133
150
 
134
151
  case type
135
152
  when nil
136
- validate_class(instance, &Proc.new)
153
+ validate_class(instance, &block)
137
154
  when String
138
- validate_type(instance, type, &Proc.new)
155
+ validate_type(instance, type, &block)
139
156
  when Array
140
157
  if valid_type = type.find { |subtype| valid_instance?(instance.merge(schema: { 'type' => subtype })) }
141
- validate_type(instance, valid_type, &Proc.new)
158
+ validate_type(instance, valid_type, &block)
142
159
  else
143
160
  yield error(instance, 'type')
144
161
  end
@@ -179,8 +196,8 @@ module JSONSchemer
179
196
  )
180
197
  end
181
198
 
182
- def error(instance, type)
183
- {
199
+ def error(instance, type, details = nil)
200
+ error = {
184
201
  'data' => instance.data,
185
202
  'data_pointer' => instance.data_pointer,
186
203
  'schema' => instance.schema,
@@ -188,43 +205,45 @@ module JSONSchemer
188
205
  'root_schema' => root,
189
206
  'type' => type,
190
207
  }
208
+ error['details'] = details if details
209
+ error
191
210
  end
192
211
 
193
- def validate_class(instance)
212
+ def validate_class(instance, &block)
194
213
  case instance.data
195
214
  when Integer
196
- validate_integer(instance, &Proc.new)
215
+ validate_integer(instance, &block)
197
216
  when Numeric
198
- validate_number(instance, &Proc.new)
217
+ validate_number(instance, &block)
199
218
  when String
200
- validate_string(instance, &Proc.new)
219
+ validate_string(instance, &block)
201
220
  when Array
202
- validate_array(instance, &Proc.new)
221
+ validate_array(instance, &block)
203
222
  when Hash
204
- validate_object(instance, &Proc.new)
223
+ validate_object(instance, &block)
205
224
  end
206
225
  end
207
226
 
208
- def validate_type(instance, type)
227
+ def validate_type(instance, type, &block)
209
228
  case type
210
229
  when 'null'
211
230
  yield error(instance, 'null') unless instance.data.nil?
212
231
  when 'boolean'
213
232
  yield error(instance, 'boolean') unless BOOLEANS.include?(instance.data)
214
233
  when 'number'
215
- validate_number(instance, &Proc.new)
234
+ validate_number(instance, &block)
216
235
  when 'integer'
217
- validate_integer(instance, &Proc.new)
236
+ validate_integer(instance, &block)
218
237
  when 'string'
219
- validate_string(instance, &Proc.new)
238
+ validate_string(instance, &block)
220
239
  when 'array'
221
- validate_array(instance, &Proc.new)
240
+ validate_array(instance, &block)
222
241
  when 'object'
223
- validate_object(instance, &Proc.new)
242
+ validate_object(instance, &block)
224
243
  end
225
244
  end
226
245
 
227
- def validate_ref(instance, ref)
246
+ def validate_ref(instance, ref, &block)
228
247
  ref_uri = join_uri(instance.parent_uri, ref)
229
248
 
230
249
  if valid_json_pointer?(ref_uri.fragment)
@@ -235,16 +254,16 @@ module JSONSchemer
235
254
  schema_pointer: ref_uri.fragment,
236
255
  parent_uri: pointer_uri(root, ref_pointer)
237
256
  )
238
- validate_instance(subinstance, &Proc.new)
257
+ validate_instance(subinstance, &block)
239
258
  else
240
- ref_root = ref_resolver.call(ref_uri)
259
+ ref_root = resolve_ref(ref_uri)
241
260
  ref_object = child(ref_root)
242
261
  subinstance = instance.merge(
243
262
  schema: ref_pointer.eval(ref_root),
244
263
  schema_pointer: ref_uri.fragment,
245
264
  parent_uri: pointer_uri(ref_root, ref_pointer)
246
265
  )
247
- ref_object.validate_instance(subinstance, &Proc.new)
266
+ ref_object.validate_instance(subinstance, &block)
248
267
  end
249
268
  elsif id = ids[ref_uri.to_s]
250
269
  subinstance = instance.merge(
@@ -252,9 +271,9 @@ module JSONSchemer
252
271
  schema_pointer: id.fetch(:pointer),
253
272
  parent_uri: ref_uri
254
273
  )
255
- validate_instance(subinstance, &Proc.new)
274
+ validate_instance(subinstance, &block)
256
275
  else
257
- ref_root = ref_resolver.call(ref_uri)
276
+ ref_root = resolve_ref(ref_uri)
258
277
  ref_object = child(ref_root)
259
278
  id = ref_object.ids[ref_uri.to_s] || { schema: ref_root, pointer: '' }
260
279
  subinstance = instance.merge(
@@ -262,7 +281,7 @@ module JSONSchemer
262
281
  schema_pointer: id.fetch(:pointer),
263
282
  parent_uri: ref_uri
264
283
  )
265
- ref_object.validate_instance(subinstance, &Proc.new)
284
+ ref_object.validate_instance(subinstance, &block)
266
285
  end
267
286
  end
268
287
 
@@ -278,7 +297,7 @@ module JSONSchemer
278
297
  yield error(instance, 'exclusiveMinimum') if instance.data <= exclusive_minimum
279
298
  end
280
299
 
281
- def validate_numeric(instance)
300
+ def validate_numeric(instance, &block)
282
301
  schema = instance.schema
283
302
  data = instance.data
284
303
 
@@ -291,8 +310,8 @@ module JSONSchemer
291
310
  yield error(instance, 'maximum') if maximum && data > maximum
292
311
  yield error(instance, 'minimum') if minimum && data < minimum
293
312
 
294
- validate_exclusive_maximum(instance, exclusive_maximum, maximum, &Proc.new) if exclusive_maximum
295
- validate_exclusive_minimum(instance, exclusive_minimum, minimum, &Proc.new) if exclusive_minimum
313
+ validate_exclusive_maximum(instance, exclusive_maximum, maximum, &block) if exclusive_maximum
314
+ validate_exclusive_minimum(instance, exclusive_minimum, minimum, &block) if exclusive_minimum
296
315
 
297
316
  if multiple_of
298
317
  quotient = data / multiple_of.to_f
@@ -300,16 +319,16 @@ module JSONSchemer
300
319
  end
301
320
  end
302
321
 
303
- def validate_number(instance)
322
+ def validate_number(instance, &block)
304
323
  unless instance.data.is_a?(Numeric)
305
324
  yield error(instance, 'number')
306
325
  return
307
326
  end
308
327
 
309
- validate_numeric(instance, &Proc.new)
328
+ validate_numeric(instance, &block)
310
329
  end
311
330
 
312
- def validate_integer(instance)
331
+ def validate_integer(instance, &block)
313
332
  data = instance.data
314
333
 
315
334
  if !data.is_a?(Numeric) || (!data.is_a?(Integer) && data.floor != data)
@@ -317,10 +336,10 @@ module JSONSchemer
317
336
  return
318
337
  end
319
338
 
320
- validate_numeric(instance, &Proc.new)
339
+ validate_numeric(instance, &block)
321
340
  end
322
341
 
323
- def validate_string(instance)
342
+ def validate_string(instance, &block)
324
343
  data = instance.data
325
344
 
326
345
  unless data.is_a?(String)
@@ -453,7 +472,10 @@ module JSONSchemer
453
472
 
454
473
  yield error(instance, 'maxProperties') if max_properties && data.size > max_properties
455
474
  yield error(instance, 'minProperties') if min_properties && data.size < min_properties
456
- yield error(instance, 'required') if required && required.any? { |key| !data.key?(key) }
475
+ if required
476
+ missing_keys = required - data.keys
477
+ yield error(instance, 'required', 'missing_keys' => missing_keys) if missing_keys.any?
478
+ end
457
479
 
458
480
  regex_pattern_properties = nil
459
481
  data.each do |key, value|
@@ -570,6 +592,10 @@ module JSONSchemer
570
592
  end
571
593
  ids
572
594
  end
595
+
596
+ def resolve_ref(uri)
597
+ ref_resolver.call(uri) || raise(InvalidRefResolution, uri.to_s)
598
+ end
573
599
  end
574
600
  end
575
601
  end
@@ -32,13 +32,13 @@ module JSONSchemer
32
32
  yield error(instance, 'exclusiveMinimum') if exclusive_minimum && instance.data <= minimum
33
33
  end
34
34
 
35
- def validate_integer(instance)
35
+ def validate_integer(instance, &block)
36
36
  if !instance.data.is_a?(Integer)
37
37
  yield error(instance, 'integer')
38
38
  return
39
39
  end
40
40
 
41
- validate_numeric(instance, &Proc.new)
41
+ validate_numeric(instance, &block)
42
42
  end
43
43
  end
44
44
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JSONSchemer
4
- VERSION = '0.1.10'
4
+ VERSION = '0.2.0'
5
5
  end
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.10
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Harsha
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-01-04 00:00:00.000000000 Z
11
+ date: 2019-03-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -153,7 +153,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
153
153
  - !ruby/object:Gem::Version
154
154
  version: '0'
155
155
  requirements: []
156
- rubygems_version: 3.0.1
156
+ rubyforge_project:
157
+ rubygems_version: 2.7.6
157
158
  signing_key:
158
159
  specification_version: 4
159
160
  summary: JSON Schema validator. Supports drafts 4, 6, and 7.