json_schemer 0.1.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0a14b7cec8b7dd7aa1b10acb4e47445fdd66ea218862c1f40f65221e789d0fdb
4
+ data.tar.gz: 8d84709159df2cfdafdef42cd9bcc982644b89713857faad3ff563a74f4ef937
5
+ SHA512:
6
+ metadata.gz: '09cad7b2683c64f022ed697239c0083882efd769a292561ce2b3027dc5162831036faa9161572a8d8e9f3248e169acc76658fedfbcd42df5d92df787e5612ad4'
7
+ data.tar.gz: 181f8e84068f344a9f662952592eb283a820363dba4e2162af42bcd9dc8313d9d4591bb43faafa9a6a46ebf420a1af27aa17df1fea73d107a59bf22e16d90661
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
@@ -0,0 +1,3 @@
1
+ [submodule "JSON-Schema-Test-Suite"]
2
+ path = JSON-Schema-Test-Suite
3
+ url = git@github.com:json-schema-org/JSON-Schema-Test-Suite
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5.0
5
+ before_install: gem install bundler -v 1.16.1
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in json_schemer.gemspec
6
+ gemspec
@@ -0,0 +1,38 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ json_schemer (0.1.0)
5
+ ecma-re-validator (~> 0.1.2)
6
+ hana (~> 1.3.3)
7
+ rdf (~> 3.0.1)
8
+ uri_template (~> 0.7.0)
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ concurrent-ruby (1.0.5)
14
+ ecma-re-validator (0.1.2)
15
+ regexp_parser (~> 0.2)
16
+ hamster (3.0.0)
17
+ concurrent-ruby (~> 1.0)
18
+ hana (1.3.3)
19
+ link_header (0.0.8)
20
+ minitest (5.11.1)
21
+ rake (10.5.0)
22
+ rdf (3.0.1)
23
+ hamster (~> 3.0)
24
+ link_header (~> 0.0, >= 0.0.8)
25
+ regexp_parser (0.4.9)
26
+ uri_template (0.7.0)
27
+
28
+ PLATFORMS
29
+ ruby
30
+
31
+ DEPENDENCIES
32
+ bundler (~> 1.16)
33
+ json_schemer!
34
+ minitest (~> 5.0)
35
+ rake (~> 10.0)
36
+
37
+ BUNDLED WITH
38
+ 1.16.1
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 David Harsha
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,50 @@
1
+ # JSONSchemer
2
+
3
+ JSON Schema draft-07 validator
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'json_schemer'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install json_schemer
20
+
21
+ ## Usage
22
+
23
+ ```ruby
24
+ require 'json_schemer'
25
+
26
+ schema = {
27
+ 'type' => 'integer'
28
+ }
29
+ schemer = JSONSchemer::Schema.new(schema)
30
+
31
+ schemer.valid?(1)
32
+ # => true
33
+
34
+ schemer.valid?(1.1)
35
+ # => false
36
+ ```
37
+
38
+ ## Development
39
+
40
+ 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.
41
+
42
+ 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).
43
+
44
+ ## Contributing
45
+
46
+ Bug reports and pull requests are welcome on GitHub at https://github.com/davishmcclurg/json_schemer.
47
+
48
+ ## License
49
+
50
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "json_schemer"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,31 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "json_schemer/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "json_schemer"
8
+ spec.version = JSONSchemer::VERSION
9
+ spec.authors = ["David Harsha"]
10
+ spec.email = ["davishmcclurg@gmail.com"]
11
+
12
+ spec.summary = "JSON Schema draft-07 validator"
13
+ spec.homepage = "https://github.com/davishmcclurg/json_schemer"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.16"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "minitest", "~> 5.0"
26
+
27
+ spec.add_runtime_dependency "ecma-re-validator", "~> 0.1.2"
28
+ spec.add_runtime_dependency "hana", "~> 1.3.3"
29
+ spec.add_runtime_dependency "rdf", "~> 3.0.1"
30
+ spec.add_runtime_dependency "uri_template", "~> 0.7.0"
31
+ end
@@ -0,0 +1,460 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json_schemer/version"
4
+
5
+ require "base64"
6
+ require "ecma-re-validator"
7
+ require "hana"
8
+ require "ipaddr"
9
+ require "json"
10
+ require 'net/http'
11
+ require "rdf"
12
+ require "time"
13
+ require "uri"
14
+ require "uri_template"
15
+
16
+ module JSONSchemer
17
+ class Schema
18
+ BOOLEANS = Set[true, false].freeze
19
+ # this is no good
20
+ EMAIL_REGEX = /\A[^@\s]+@([\p{L}\d-]+\.)+[\p{L}\d\-]{2,}\z/i.freeze
21
+ LABEL_REGEX_STRING = '\p{L}([\p{L}\p{N}\-]*[\p{L}\p{N}])?'
22
+ HOSTNAME_REGEX = /\A(#{LABEL_REGEX_STRING}\.)*#{LABEL_REGEX_STRING}\z/i.freeze
23
+ JSON_POINTER_REGEX_STRING = '(\/([^~\/]|~[01])*)*'
24
+ JSON_POINTER_REGEX = /\A#{JSON_POINTER_REGEX_STRING}\z/.freeze
25
+ RELATIVE_JSON_POINTER_REGEX = /\A(0|[1-9]\d*)(#|#{JSON_POINTER_REGEX_STRING})?\z/.freeze
26
+
27
+ def initialize(schema)
28
+ @root = schema
29
+ end
30
+
31
+ def valid?(data, schema = root, pointer = '#', parent_uri = nil)
32
+ validate(data, schema, pointer, parent_uri).none?
33
+ end
34
+
35
+ def validate(data, schema = root, pointer = '#', parent_uri = nil)
36
+ return enum_for(:validate, data, schema, pointer, parent_uri) unless block_given?
37
+
38
+ return if schema == true
39
+ if schema == false
40
+ yield error(data, schema, pointer, 'schema')
41
+ return
42
+ end
43
+
44
+ return if schema.empty?
45
+
46
+ type = schema['type']
47
+ enum = schema['enum']
48
+ all_of = schema['allOf']
49
+ any_of = schema['anyOf']
50
+ one_of = schema['oneOf']
51
+ not_schema = schema['not']
52
+ if_schema = schema['if']
53
+ then_schema = schema['then']
54
+ else_schema = schema['else']
55
+ ref = schema['$ref']
56
+ id = schema['$id']
57
+
58
+ parent_uri = join_uri(parent_uri, id)
59
+
60
+ if ref
61
+ validate_ref(data, schema, pointer, parent_uri, ref, &Proc.new)
62
+ return
63
+ end
64
+
65
+ yield error(data, schema, pointer, 'enum') if enum && !enum.include?(data)
66
+ yield error(data, schema, pointer, 'const') if schema.key?('const') && schema['const'] != data
67
+
68
+ yield error(data, schema, pointer, 'allOf') if all_of && !all_of.all? { |subschema| valid?(data, subschema, pointer, parent_uri) }
69
+ yield error(data, schema, pointer, 'anyOf') if any_of && !any_of.any? { |subschema| valid?(data, subschema, pointer, parent_uri) }
70
+ yield error(data, schema, pointer, 'oneOf') if one_of && !one_of.one? { |subschema| valid?(data, subschema, pointer, parent_uri) }
71
+ yield error(data, schema, pointer, 'not') if !not_schema.nil? && valid?(data, not_schema, pointer, parent_uri)
72
+
73
+ if if_schema && valid?(data, if_schema, pointer, parent_uri)
74
+ yield error(data, schema, pointer, 'then') if !then_schema.nil? && !valid?(data, then_schema, pointer, parent_uri)
75
+ elsif if_schema
76
+ yield error(data, schema, pointer, 'else') if !else_schema.nil? && !valid?(data, else_schema, pointer, parent_uri)
77
+ end
78
+
79
+ case type
80
+ when nil
81
+ validate_class(data, schema, pointer, parent_uri, &Proc.new)
82
+ when String
83
+ validate_type(data, schema, pointer, parent_uri, type, &Proc.new)
84
+ when Array
85
+ if valid_type = type.find { |subtype| valid?(data, { 'type' => subtype }, pointer, parent_uri) }
86
+ validate_type(data, schema, pointer, parent_uri, valid_type, &Proc.new)
87
+ else
88
+ yield error(data, schema, pointer, 'type')
89
+ end
90
+ end
91
+ end
92
+
93
+ protected
94
+
95
+ def ids
96
+ @ids ||= resolve_ids(root)
97
+ end
98
+
99
+ private
100
+
101
+ attr_reader :root
102
+
103
+ def error(data, schema, pointer, type)
104
+ {
105
+ 'data' => data,
106
+ 'schema' => schema,
107
+ 'pointer' => pointer,
108
+ 'type' => type,
109
+ }
110
+ end
111
+
112
+ def validate_class(data, schema, pointer, parent_uri)
113
+ case data
114
+ when Integer
115
+ validate_integer(data, schema, pointer, &Proc.new)
116
+ when Numeric
117
+ validate_number(data, schema, pointer, &Proc.new)
118
+ when String
119
+ validate_string(data, schema, pointer, &Proc.new)
120
+ when Array
121
+ validate_array(data, schema, pointer, parent_uri, &Proc.new)
122
+ when Hash
123
+ validate_object(data, schema, pointer, parent_uri, &Proc.new)
124
+ end
125
+ end
126
+
127
+ def validate_type(data, schema, pointer, parent_uri, type)
128
+ case type
129
+ when 'null'
130
+ yield error(data, schema, pointer, 'null') unless data.nil?
131
+ when 'boolean'
132
+ yield error(data, schema, pointer, 'boolean') unless BOOLEANS.include?(data)
133
+ when 'number'
134
+ validate_number(data, schema, pointer, &Proc.new)
135
+ when 'integer'
136
+ validate_integer(data, schema, pointer, &Proc.new)
137
+ when 'string'
138
+ validate_string(data, schema, pointer, &Proc.new)
139
+ when 'array'
140
+ validate_array(data, schema, pointer, parent_uri, &Proc.new)
141
+ when 'object'
142
+ validate_object(data, schema, pointer, parent_uri, &Proc.new)
143
+ end
144
+ end
145
+
146
+ def validate_ref(data, schema, pointer, parent_uri, ref)
147
+ ref_uri = join_uri(parent_uri, ref)
148
+
149
+ if valid_json_pointer?(ref_uri.fragment)
150
+ ref_pointer = Hana::Pointer.new(URI.unescape(ref_uri.fragment || ''))
151
+ if ref.start_with?('#')
152
+ validate(data, ref_pointer.eval(root), pointer, pointer_uri(root, ref_pointer), &Proc.new)
153
+ else
154
+ ref_root = JSON.parse(Net::HTTP.get(ref_uri))
155
+ ref_object = self.class.new(ref_root)
156
+ ref_object.validate(data, ref_pointer.eval(ref_root), pointer, pointer_uri(ref_root, ref_pointer), &Proc.new)
157
+ end
158
+ elsif ids.key?(ref_uri.to_s)
159
+ validate(data, ids.fetch(ref_uri.to_s), pointer, ref_uri, &Proc.new)
160
+ else
161
+ ref_root = JSON.parse(Net::HTTP.get(ref_uri))
162
+ ref_object = self.class.new(ref_root)
163
+ ref_object.validate(data, ref_object.ids.fetch(ref_uri.to_s, ref_root), pointer, ref_uri, &Proc.new)
164
+ end
165
+ end
166
+
167
+ def validate_numeric(data, schema, pointer)
168
+ multiple_of = schema['multipleOf']
169
+ maximum = schema['maximum']
170
+ exclusive_maximum = schema['exclusiveMaximum']
171
+ minimum = schema['minimum']
172
+ exclusive_minimum = schema['exclusiveMinimum']
173
+
174
+ yield error(data, schema, pointer, 'maximum') if maximum && data > maximum
175
+ yield error(data, schema, pointer, 'exclusiveMaximum') if exclusive_maximum && data >= exclusive_maximum
176
+ yield error(data, schema, pointer, 'minimum') if minimum && data < minimum
177
+ yield error(data, schema, pointer, 'exclusiveMinimum') if exclusive_minimum && data <= exclusive_minimum
178
+
179
+ if multiple_of
180
+ quotient = data / multiple_of.to_f
181
+ yield error(data, schema, pointer, 'multipleOf') unless quotient.floor == quotient
182
+ end
183
+ end
184
+
185
+ def validate_number(data, schema, pointer)
186
+ unless data.is_a?(Numeric)
187
+ yield error(data, schema, pointer, 'number')
188
+ return
189
+ end
190
+
191
+ validate_numeric(data, schema, pointer, &Proc.new)
192
+ end
193
+
194
+ def validate_integer(data, schema, pointer)
195
+ if !data.is_a?(Numeric) || (!data.is_a?(Integer) && data.floor != data)
196
+ yield error(data, schema, pointer, 'integer')
197
+ return
198
+ end
199
+
200
+ validate_numeric(data, schema, pointer, &Proc.new)
201
+ end
202
+
203
+ def validate_string(data, schema, pointer)
204
+ unless data.is_a?(String)
205
+ yield error(data, schema, pointer, 'string')
206
+ return
207
+ end
208
+
209
+ max_length = schema['maxLength']
210
+ min_length = schema['minLength']
211
+ pattern = schema['pattern']
212
+ format = schema['format']
213
+ content_encoding = schema['contentEncoding']
214
+ content_media_type = schema['contentMediaType']
215
+
216
+ yield error(data, schema, pointer, 'maxLength') if max_length && data.size > max_length
217
+ yield error(data, schema, pointer, 'minLength') if min_length && data.size < min_length
218
+ yield error(data, schema, pointer, 'pattern') if pattern && !Regexp.new(pattern).match?(data)
219
+
220
+ validate_string_format(data, schema, pointer, format, &Proc.new) if format
221
+
222
+ if content_encoding || content_media_type
223
+ decoded_data = data
224
+
225
+ if content_encoding
226
+ decoded_data = case content_encoding.downcase
227
+ when 'base64'
228
+ safe_strict_decode64(data)
229
+ else # '7bit', '8bit', 'binary', 'quoted-printable'
230
+ raise NotImplementedError
231
+ end
232
+ yield error(data, schema, pointer, 'contentEncoding') unless decoded_data
233
+ end
234
+
235
+ if content_media_type && decoded_data
236
+ case content_media_type.downcase
237
+ when 'application/json'
238
+ yield error(data, schema, pointer, 'contentMediaType') unless valid_json?(decoded_data)
239
+ else
240
+ raise NotImplementedError
241
+ end
242
+ end
243
+ end
244
+ end
245
+
246
+ def validate_string_format(data, schema, pointer, format)
247
+ valid = case format
248
+ when 'date-time'
249
+ valid_date_time?(data)
250
+ when 'date'
251
+ valid_date_time?("#{data}T04:05:06.123456789+07:00")
252
+ when 'time'
253
+ valid_date_time?("2001-02-03T#{data}")
254
+ when 'email'
255
+ data.ascii_only? && EMAIL_REGEX.match?(data)
256
+ when 'idn-email'
257
+ EMAIL_REGEX.match?(data)
258
+ when 'hostname'
259
+ data.ascii_only? && valid_hostname?(data)
260
+ when 'idn-hostname'
261
+ valid_hostname?(data)
262
+ when 'ipv4'
263
+ valid_ip?(data, :v4)
264
+ when 'ipv6'
265
+ valid_ip?(data, :v6)
266
+ when 'uri'
267
+ data.ascii_only? && RDF::URI::IRI.match?(data)
268
+ when 'uri-reference'
269
+ data.ascii_only? && (RDF::URI::IRI.match?(data) || RDF::URI::IRELATIVE_REF.match?(data))
270
+ when 'iri'
271
+ RDF::URI::IRI.match?(data)
272
+ when 'iri-reference'
273
+ RDF::URI::IRI.match?(data) || RDF::URI::IRELATIVE_REF.match?(data)
274
+ when 'uri-template'
275
+ valid_uri_template?(data)
276
+ when 'json-pointer'
277
+ valid_json_pointer?(data)
278
+ when 'relative-json-pointer'
279
+ RELATIVE_JSON_POINTER_REGEX.match?(data)
280
+ when 'regex'
281
+ EcmaReValidator.valid?(data)
282
+ end
283
+ yield error(data, schema, pointer, 'format') unless valid
284
+ end
285
+
286
+ def validate_array(data, schema, pointer, parent_uri, &block)
287
+ unless data.is_a?(Array)
288
+ yield error(data, schema, pointer, 'array')
289
+ return
290
+ end
291
+
292
+ items = schema['items']
293
+ additional_items = schema['additionalItems']
294
+ max_items = schema['maxItems']
295
+ min_items = schema['minItems']
296
+ unique_items = schema['uniqueItems']
297
+ contains = schema['contains']
298
+
299
+ yield error(data, schema, pointer, 'maxItems') if max_items && data.size > max_items
300
+ yield error(data, schema, pointer, 'minItems') if min_items && data.size < min_items
301
+ yield error(data, schema, pointer, 'uniqueItems') if unique_items && data.size != data.uniq.size
302
+ yield error(data, schema, pointer, 'contains') if !contains.nil? && data.all? { |item| !valid?(item, contains, pointer, parent_uri) }
303
+
304
+ if items.is_a?(Array)
305
+ data.each_with_index do |item, index|
306
+ if index < items.size
307
+ validate(item, items[index], "#{pointer}/#{index}", parent_uri, &block)
308
+ elsif !additional_items.nil?
309
+ validate(item, additional_items, "#{pointer}/#{index}", parent_uri, &block)
310
+ else
311
+ break
312
+ end
313
+ end
314
+ elsif !items.nil?
315
+ data.each_with_index do |item, index|
316
+ validate(item, items, "#{pointer}/#{index}", parent_uri, &block)
317
+ end
318
+ end
319
+ end
320
+
321
+ def validate_object(data, schema, pointer, parent_uri, &block)
322
+ unless data.is_a?(Hash)
323
+ yield error(data, schema, pointer, 'object')
324
+ return
325
+ end
326
+
327
+ max_properties = schema['maxProperties']
328
+ min_properties = schema['minProperties']
329
+ required = schema['required']
330
+ properties = schema['properties']
331
+ pattern_properties = schema['patternProperties']
332
+ additional_properties = schema['additionalProperties']
333
+ dependencies = schema['dependencies']
334
+ property_names = schema['propertyNames']
335
+
336
+ if dependencies
337
+ dependencies.each do |key, value|
338
+ next unless data.key?(key)
339
+ subschema = value.is_a?(Array) ? { 'required' => value } : value
340
+ validate(data, subschema, pointer, parent_uri, &block)
341
+ end
342
+ end
343
+
344
+ yield error(data, schema, pointer, 'maxProperties') if max_properties && data.size > max_properties
345
+ yield error(data, schema, pointer, 'minProperties') if min_properties && data.size < min_properties
346
+ yield error(data, schema, pointer, 'required') if required && required.any? { |key| !data.key?(key) }
347
+
348
+ regex_pattern_properties = nil
349
+ data.each do |key, value|
350
+ validate(key, property_names, pointer, parent_uri, &block) unless property_names.nil?
351
+
352
+ matched_key = false
353
+
354
+ if properties && properties.key?(key)
355
+ validate(value, properties[key], "#{pointer}/#{key}", parent_uri, &block)
356
+ matched_key = true
357
+ end
358
+
359
+ if pattern_properties
360
+ regex_pattern_properties ||= pattern_properties.map do |pattern, property_schema|
361
+ [Regexp.new(pattern), property_schema]
362
+ end
363
+ regex_pattern_properties.each do |regex, property_schema|
364
+ if regex.match?(key)
365
+ validate(value, property_schema, "#{pointer}/#{key}", parent_uri, &block)
366
+ matched_key = true
367
+ end
368
+ end
369
+ end
370
+
371
+ next if matched_key
372
+
373
+ validate(value, additional_properties, "#{pointer}/#{key}", parent_uri, &block) unless additional_properties.nil?
374
+ end
375
+ end
376
+
377
+ def safe_strict_decode64(data)
378
+ begin
379
+ Base64.strict_decode64(data)
380
+ rescue ArgumentError => e
381
+ raise e unless e.message == 'invalid base64'
382
+ nil
383
+ end
384
+ end
385
+
386
+ def valid_json?(data)
387
+ JSON.parse(data)
388
+ true
389
+ rescue JSON::ParserError
390
+ false
391
+ end
392
+
393
+ def valid_date_time?(data)
394
+ DateTime.rfc3339(data)
395
+ true
396
+ rescue ArgumentError => e
397
+ raise e unless e.message == 'invalid date'
398
+ false
399
+ end
400
+
401
+ def valid_hostname?(data)
402
+ HOSTNAME_REGEX.match?(data) && data.split('.').all? { |label| label.size <= 63 }
403
+ end
404
+
405
+ def valid_ip?(data, type)
406
+ ip_address = IPAddr.new(data)
407
+ type == :v4 ? ip_address.ipv4? : ip_address.ipv6?
408
+ rescue IPAddr::InvalidAddressError
409
+ false
410
+ end
411
+
412
+ def valid_uri_template?(data)
413
+ URITemplate.new(data)
414
+ true
415
+ rescue URITemplate::Invalid
416
+ false
417
+ end
418
+
419
+ def valid_json_pointer?(data)
420
+ JSON_POINTER_REGEX.match?(data)
421
+ end
422
+
423
+ def join_uri(a, b)
424
+ if a && b
425
+ URI.join(a, b)
426
+ elsif b
427
+ URI.parse(b)
428
+ else
429
+ a
430
+ end
431
+ end
432
+
433
+ def pointer_uri(schema, pointer)
434
+ uri_parts = nil
435
+ pointer.reduce(schema) do |obj, token|
436
+ next obj.fetch(token.to_i) if obj.is_a?(Array)
437
+ if obj_id = obj['$id']
438
+ uri_parts ||= []
439
+ uri_parts << obj_id
440
+ end
441
+ obj.fetch(token)
442
+ end
443
+ uri_parts ? URI.join(*uri_parts) : nil
444
+ end
445
+
446
+ def resolve_ids(schema, ids = {}, parent_uri = nil)
447
+ if schema.is_a?(Array)
448
+ schema.each { |subschema| resolve_ids(subschema, ids, parent_uri) }
449
+ elsif schema.is_a?(Hash)
450
+ id = schema['$id']
451
+ uri = join_uri(parent_uri, id)
452
+ ids[uri.to_s] = schema unless uri == parent_uri
453
+ if definitions = schema['definitions']
454
+ definitions.each_value { |subschema| resolve_ids(subschema, ids, uri) }
455
+ end
456
+ end
457
+ ids
458
+ end
459
+ end
460
+ end
@@ -0,0 +1,3 @@
1
+ module JSONSchemer
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,155 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: json_schemer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - David Harsha
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-01-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: ecma-re-validator
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.1.2
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.1.2
69
+ - !ruby/object:Gem::Dependency
70
+ name: hana
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 1.3.3
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 1.3.3
83
+ - !ruby/object:Gem::Dependency
84
+ name: rdf
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 3.0.1
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 3.0.1
97
+ - !ruby/object:Gem::Dependency
98
+ name: uri_template
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.7.0
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 0.7.0
111
+ description:
112
+ email:
113
+ - davishmcclurg@gmail.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - ".gitignore"
119
+ - ".gitmodules"
120
+ - ".travis.yml"
121
+ - Gemfile
122
+ - Gemfile.lock
123
+ - LICENSE.txt
124
+ - README.md
125
+ - Rakefile
126
+ - bin/console
127
+ - bin/setup
128
+ - json_schemer.gemspec
129
+ - lib/json_schemer.rb
130
+ - lib/json_schemer/version.rb
131
+ homepage: https://github.com/davishmcclurg/json_schemer
132
+ licenses:
133
+ - MIT
134
+ metadata: {}
135
+ post_install_message:
136
+ rdoc_options: []
137
+ require_paths:
138
+ - lib
139
+ required_ruby_version: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ required_rubygems_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ requirements: []
150
+ rubyforge_project:
151
+ rubygems_version: 2.7.3
152
+ signing_key:
153
+ specification_version: 4
154
+ summary: JSON Schema draft-07 validator
155
+ test_files: []