json_schemer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []