json-schema-pvdgm 2.3.1

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.
Files changed (96) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.md +19 -0
  3. data/README.textile +354 -0
  4. data/lib/json-schema.rb +25 -0
  5. data/lib/json-schema/attributes/additionalitems.rb +23 -0
  6. data/lib/json-schema/attributes/additionalproperties.rb +67 -0
  7. data/lib/json-schema/attributes/allof.rb +37 -0
  8. data/lib/json-schema/attributes/anyof.rb +41 -0
  9. data/lib/json-schema/attributes/dependencies.rb +30 -0
  10. data/lib/json-schema/attributes/dependencies_v4.rb +20 -0
  11. data/lib/json-schema/attributes/disallow.rb +11 -0
  12. data/lib/json-schema/attributes/divisibleby.rb +16 -0
  13. data/lib/json-schema/attributes/enum.rb +24 -0
  14. data/lib/json-schema/attributes/extends.rb +49 -0
  15. data/lib/json-schema/attributes/format.rb +123 -0
  16. data/lib/json-schema/attributes/items.rb +25 -0
  17. data/lib/json-schema/attributes/maxdecimal.rb +15 -0
  18. data/lib/json-schema/attributes/maximum.rb +15 -0
  19. data/lib/json-schema/attributes/maximum_inclusive.rb +15 -0
  20. data/lib/json-schema/attributes/maxitems.rb +12 -0
  21. data/lib/json-schema/attributes/maxlength.rb +14 -0
  22. data/lib/json-schema/attributes/maxproperties.rb +12 -0
  23. data/lib/json-schema/attributes/minimum.rb +15 -0
  24. data/lib/json-schema/attributes/minimum_inclusive.rb +15 -0
  25. data/lib/json-schema/attributes/minitems.rb +12 -0
  26. data/lib/json-schema/attributes/minlength.rb +14 -0
  27. data/lib/json-schema/attributes/minproperties.rb +12 -0
  28. data/lib/json-schema/attributes/multipleof.rb +16 -0
  29. data/lib/json-schema/attributes/not.rb +28 -0
  30. data/lib/json-schema/attributes/oneof.rb +32 -0
  31. data/lib/json-schema/attributes/pattern.rb +15 -0
  32. data/lib/json-schema/attributes/patternproperties.rb +23 -0
  33. data/lib/json-schema/attributes/properties.rb +58 -0
  34. data/lib/json-schema/attributes/properties_optional.rb +23 -0
  35. data/lib/json-schema/attributes/properties_v4.rb +57 -0
  36. data/lib/json-schema/attributes/ref.rb +70 -0
  37. data/lib/json-schema/attributes/required.rb +23 -0
  38. data/lib/json-schema/attributes/type.rb +102 -0
  39. data/lib/json-schema/attributes/type_v4.rb +54 -0
  40. data/lib/json-schema/attributes/uniqueitems.rb +16 -0
  41. data/lib/json-schema/model_validator.rb +85 -0
  42. data/lib/json-schema/schema.rb +73 -0
  43. data/lib/json-schema/uri/file.rb +36 -0
  44. data/lib/json-schema/uri/uuid.rb +285 -0
  45. data/lib/json-schema/util/array_set.rb +14 -0
  46. data/lib/json-schema/util/hash.rb +8 -0
  47. data/lib/json-schema/validator.rb +672 -0
  48. data/lib/json-schema/validators/draft1.rb +32 -0
  49. data/lib/json-schema/validators/draft2.rb +33 -0
  50. data/lib/json-schema/validators/draft3.rb +38 -0
  51. data/lib/json-schema/validators/draft4.rb +45 -0
  52. data/resources/draft-01.json +155 -0
  53. data/resources/draft-02.json +166 -0
  54. data/resources/draft-03.json +174 -0
  55. data/resources/draft-04.json +150 -0
  56. data/test/data/all_of_ref_data.json +3 -0
  57. data/test/data/any_of_ref_data.json +7 -0
  58. data/test/data/bad_data_1.json +3 -0
  59. data/test/data/good_data_1.json +3 -0
  60. data/test/data/one_of_ref_links_data.json +5 -0
  61. data/test/schemas/all_of_ref_base_schema.json +6 -0
  62. data/test/schemas/all_of_ref_schema.json +7 -0
  63. data/test/schemas/any_of_ref_jane_schema.json +4 -0
  64. data/test/schemas/any_of_ref_jimmy_schema.json +4 -0
  65. data/test/schemas/any_of_ref_john_schema.json +4 -0
  66. data/test/schemas/any_of_ref_schema.json +15 -0
  67. data/test/schemas/extends_and_additionalProperties-1-filename.schema.json +34 -0
  68. data/test/schemas/extends_and_additionalProperties-1-ref.schema.json +34 -0
  69. data/test/schemas/extends_and_additionalProperties-2-filename.schema.json +33 -0
  70. data/test/schemas/extends_and_additionalProperties-2-ref.schema.json +33 -0
  71. data/test/schemas/good_schema_1.json +10 -0
  72. data/test/schemas/good_schema_2.json +10 -0
  73. data/test/schemas/good_schema_extends1.json +10 -0
  74. data/test/schemas/good_schema_extends2.json +13 -0
  75. data/test/schemas/inner.schema.json +21 -0
  76. data/test/schemas/one_of_ref_links_schema.json +16 -0
  77. data/test/schemas/self_link_schema.json +17 -0
  78. data/test/schemas/up_link_schema.json +17 -0
  79. data/test/test_all_of_ref_schema.rb +11 -0
  80. data/test/test_any_of_ref_schema.rb +11 -0
  81. data/test/test_bad_schema_ref.rb +33 -0
  82. data/test/test_extended_schema.rb +68 -0
  83. data/test/test_extends_and_additionalProperties.rb +50 -0
  84. data/test/test_files_v3.rb +52 -0
  85. data/test/test_fragment_resolution.rb +31 -0
  86. data/test/test_full_validation.rb +209 -0
  87. data/test/test_jsonschema_draft1.rb +701 -0
  88. data/test/test_jsonschema_draft2.rb +773 -0
  89. data/test/test_jsonschema_draft3.rb +1236 -0
  90. data/test/test_jsonschema_draft4.rb +1356 -0
  91. data/test/test_model_validator.rb +52 -0
  92. data/test/test_one_of.rb +42 -0
  93. data/test/test_ruby_schema.rb +38 -0
  94. data/test/test_schema_type_attribute.rb +21 -0
  95. data/test/test_schema_validation.rb +85 -0
  96. metadata +180 -0
@@ -0,0 +1,14 @@
1
+ # This is a hack that I don't want to ever use anywhere else or repeat EVER, but we need enums to be
2
+ # an Array to pass schema validation. But we also want fast lookup! And we can't use sets because of
3
+ # backport support... so...
4
+
5
+ class ArraySet < Array
6
+ def include?(obj)
7
+ # On first invocation create a HASH (yeah, yeah) to act as our set given the array values
8
+ if !defined? @array_values
9
+ @array_values = {}
10
+ self.each {|x| @array_values[x] = 1}
11
+ end
12
+ @array_values.has_key? obj
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ class Hash
2
+ if !{}.respond_to?("default_proc=")
3
+ def default_proc=(blk)
4
+ self.replace(Hash.new(&blk).merge(self))
5
+ end
6
+ end
7
+ end
8
+
@@ -0,0 +1,672 @@
1
+ require 'uri'
2
+ require 'open-uri'
3
+ require 'pathname'
4
+ require 'bigdecimal'
5
+ require 'digest/sha1'
6
+ require 'date'
7
+ require 'thread'
8
+ require 'yaml'
9
+
10
+ module JSON
11
+
12
+ class Schema
13
+ class ValidationError < StandardError
14
+ attr_accessor :fragments, :schema, :failed_attribute, :sub_errors, :error_details
15
+
16
+ def initialize(message, fragments, failed_attribute, schema, error_details={})
17
+ @fragments = fragments.clone
18
+ @schema = schema
19
+ @sub_errors = []
20
+ @failed_attribute = failed_attribute
21
+ @error_details = error_details
22
+ message = "#{message} in schema #{schema.uri}"
23
+ super(message)
24
+ end
25
+
26
+ def to_string
27
+ if @sub_errors.empty?
28
+ message
29
+ else
30
+ full_message = message + "\n The schema specific errors were:\n"
31
+ @sub_errors.each{|e| full_message = full_message + " - " + e.to_string + "\n"}
32
+ full_message
33
+ end
34
+ end
35
+
36
+ def to_hash
37
+ base = {:schema => @schema.uri, :fragment => ::JSON::Schema::Attribute.build_fragment(fragments), :message => message, :failed_attribute => @failed_attribute.to_s.split(":").last.split("Attribute").first, :error_details => @error_details}
38
+ if !@sub_errors.empty?
39
+ base[:errors] = @sub_errors.map{|e| e.to_hash}
40
+ end
41
+ base
42
+ end
43
+ end
44
+
45
+ class SchemaError < StandardError
46
+ end
47
+
48
+ class JsonParseError < StandardError
49
+ end
50
+
51
+ class Attribute
52
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
53
+ end
54
+
55
+ def self.build_fragment(fragments)
56
+ "#/#{fragments.join('/')}"
57
+ end
58
+
59
+ def self.last_fragment_as_symbol(fragments)
60
+ return nil if fragments.empty?
61
+ fragments.first.to_sym
62
+ end
63
+
64
+ def self.validation_error(processor, message, fragments, current_schema, failed_attribute, record_errors, error_details={})
65
+ error = ValidationError.new(message, fragments, failed_attribute, current_schema, error_details)
66
+ if record_errors
67
+ processor.validation_error(error)
68
+ else
69
+ raise error
70
+ end
71
+ end
72
+
73
+ def self.validation_errors(validator)
74
+ validator.validation_errors
75
+ end
76
+ end
77
+
78
+ class Validator
79
+ attr_accessor :attributes, :uri
80
+
81
+ def initialize()
82
+ @attributes = {}
83
+ @uri = nil
84
+ end
85
+
86
+ def extend_schema_definition(schema_uri)
87
+ u = URI.parse(schema_uri)
88
+ validator = JSON::Validator.validators["#{u.scheme}://#{u.host}#{u.path}"]
89
+ if validator.nil?
90
+ raise SchemaError.new("Schema not found: #{u.scheme}://#{u.host}#{u.path}")
91
+ end
92
+ @attributes.merge!(validator.attributes)
93
+ end
94
+
95
+ def to_s
96
+ "#{@uri.scheme}://#{uri.host}#{uri.path}"
97
+ end
98
+
99
+ def validate(current_schema, data, fragments, processor, options = {})
100
+ current_schema.schema.each do |attr_name,attribute|
101
+ if @attributes.has_key?(attr_name.to_s)
102
+ @attributes[attr_name.to_s].validate(current_schema, data, fragments, processor, self, options)
103
+ end
104
+ end
105
+ data
106
+ end
107
+ end
108
+ end
109
+
110
+
111
+ class Validator
112
+
113
+ @@schemas = {}
114
+ @@cache_schemas = false
115
+ @@default_opts = {
116
+ :list => false,
117
+ :version => nil,
118
+ :validate_schema => false,
119
+ :record_errors => false,
120
+ :errors_as_objects => false,
121
+ :insert_defaults => false,
122
+ :clear_cache => true,
123
+ :strict => false
124
+ }
125
+ @@validators = {}
126
+ @@default_validator = nil
127
+ @@available_json_backends = []
128
+ @@json_backend = nil
129
+ @@serializer = nil
130
+ @@mutex = Mutex.new
131
+
132
+ def self.version_string_for(version)
133
+ # I'm not a fan of this, but it's quick and dirty to get it working for now
134
+ return "draft-04" unless version
135
+ case version.to_s
136
+ when "draft4", "http://json-schema.org/draft-04/schema#"
137
+ "draft-04"
138
+ when "draft3", "http://json-schema.org/draft-03/schema#"
139
+ "draft-03"
140
+ when "draft2"
141
+ "draft-02"
142
+ when "draft1"
143
+ "draft-01"
144
+ else
145
+ raise JSON::Schema::SchemaError.new("The requested JSON schema version is not supported")
146
+ end
147
+ end
148
+
149
+ def self.metaschema_for(version_string)
150
+ File.join(Pathname.new(File.dirname(__FILE__)).parent.parent, "resources", "#{version_string}.json").to_s
151
+ end
152
+
153
+ def initialize(schema_data, data, opts={})
154
+ @options = @@default_opts.clone.merge(opts)
155
+ @errors = []
156
+
157
+ # I'm not a fan of this, but it's quick and dirty to get it working for now
158
+ version_string = "draft-04"
159
+ if @options[:version]
160
+ version_string = @options[:version] = self.class.version_string_for(@options[:version])
161
+ u = URI.parse("http://json-schema.org/#{@options[:version]}/schema#")
162
+ validator = JSON::Validator.validators["#{u.scheme}://#{u.host}#{u.path}"]
163
+ @options[:version] = validator
164
+ end
165
+
166
+ @validation_options = @options[:record_errors] ? {:record_errors => true} : {}
167
+ @validation_options[:insert_defaults] = true if @options[:insert_defaults]
168
+ @validation_options[:strict] = true if @options[:strict] == true
169
+
170
+ @@mutex.synchronize { @base_schema = initialize_schema(schema_data) }
171
+ @data = initialize_data(data)
172
+ @@mutex.synchronize { build_schemas(@base_schema) }
173
+
174
+ # validate the schema, if requested
175
+ if @options[:validate_schema]
176
+ begin
177
+ if @base_schema.schema["$schema"]
178
+ version_string = @options[:version] = self.class.version_string_for(@base_schema.schema["$schema"])
179
+ end
180
+ # Don't clear the cache during metaschema validation!
181
+ meta_validator = JSON::Validator.new(self.class.metaschema_for(version_string), @base_schema.schema, {:clear_cache => false})
182
+ meta_validator.validate
183
+ rescue JSON::Schema::ValidationError, JSON::Schema::SchemaError
184
+ raise $!
185
+ end
186
+ end
187
+
188
+ # If the :fragment option is set, try and validate against the fragment
189
+ if opts[:fragment]
190
+ @base_schema = schema_from_fragment(@base_schema, opts[:fragment])
191
+ end
192
+ end
193
+
194
+ def schema_from_fragment(base_schema, fragment)
195
+ fragments = fragment.split("/")
196
+
197
+ # ensure the first element was a hash, per the fragment spec
198
+ if fragments.shift != "#"
199
+ raise JSON::Schema::SchemaError.new("Invalid fragment syntax in :fragment option")
200
+ end
201
+
202
+ fragments.each do |f|
203
+ if base_schema.is_a?(JSON::Schema) #test if fragment is a JSON:Schema instance
204
+ if !base_schema.schema.has_key?(f)
205
+ raise JSON::Schema::SchemaError.new("Invalid fragment resolution for :fragment option")
206
+ end
207
+ base_schema = base_schema.schema[f]
208
+ elsif base_schema.is_a?(Hash)
209
+ if !base_schema.has_key?(f)
210
+ raise JSON::Schema::SchemaError.new("Invalid fragment resolution for :fragment option")
211
+ end
212
+ base_schema = initialize_schema(base_schema[f]) #need to return a Schema instance for validation to work
213
+ elsif base_schema.is_a?(Array)
214
+ if base_schema[f.to_i].nil?
215
+ raise JSON::Schema::SchemaError.new("Invalid fragment resolution for :fragment option")
216
+ end
217
+ base_schema = initialize_schema(base_schema[f.to_i])
218
+ else
219
+ raise JSON::Schema::SchemaError.new("Invalid schema encountered when resolving :fragment option")
220
+ end
221
+ end
222
+ if @options[:list] #check if the schema is validating a list
223
+ base_schema.schema = schema_to_list(base_schema.schema)
224
+ end
225
+ base_schema
226
+ end
227
+
228
+ # Run a simple true/false validation of data against a schema
229
+ def validate()
230
+ begin
231
+ @base_schema.validate(@data,[],self,@validation_options)
232
+ if @validation_options[:clear_cache] == true
233
+ Validator.clear_cache
234
+ end
235
+ if @options[:errors_as_objects]
236
+ return @errors.map{|e| e.to_hash}
237
+ else
238
+ return @errors.map{|e| e.to_string}
239
+ end
240
+ rescue JSON::Schema::ValidationError
241
+ if @validation_options[:clear_cache] == true
242
+ Validator.clear_cache
243
+ end
244
+ raise $!
245
+ end
246
+ end
247
+
248
+
249
+ def load_ref_schema(parent_schema,ref)
250
+ uri = URI.parse(ref)
251
+ if uri.relative?
252
+ uri = parent_schema.uri.clone
253
+
254
+ # Check for absolute path
255
+ path = ref.split("#")[0]
256
+
257
+ # This is a self reference and thus the schema does not need to be re-loaded
258
+ if path.nil? || path == ''
259
+ return
260
+ end
261
+
262
+ if path && path[0,1] == '/'
263
+ uri.path = Pathname.new(path).cleanpath.to_s
264
+ else
265
+ uri = parent_schema.uri.merge(path)
266
+ end
267
+ uri.fragment = ''
268
+ end
269
+
270
+ if Validator.schemas[uri.to_s].nil?
271
+ schema = JSON::Schema.new(JSON::Validator.parse(open(uri.to_s).read), uri, @options[:version])
272
+ Validator.add_schema(schema)
273
+ build_schemas(schema)
274
+ end
275
+ end
276
+
277
+
278
+ # Build all schemas with IDs, mapping out the namespace
279
+ def build_schemas(parent_schema)
280
+ # Build ref schemas if they exist
281
+ if parent_schema.schema["$ref"]
282
+ load_ref_schema(parent_schema, parent_schema.schema["$ref"])
283
+ end
284
+ if parent_schema.schema["extends"]
285
+ if parent_schema.schema["extends"].is_a?(String)
286
+ load_ref_schema(parent_schema, parent_schema.schema["extends"])
287
+ elsif parent_schema.schema["extends"].is_a?(Array)
288
+ parent_schema.schema["extends"].each do |type|
289
+ handle_schema(parent_schema, type)
290
+ end
291
+ end
292
+ end
293
+
294
+ # handle validations that always contain schemas
295
+ ["allOf", "anyOf", "oneOf", "not"].each do |key|
296
+ if parent_schema.schema.has_key?(key)
297
+ validations = parent_schema.schema[key]
298
+ validations = [validations] unless validations.is_a?(Array)
299
+ validations.each {|v| handle_schema(parent_schema, v) }
300
+ end
301
+ end
302
+
303
+ # Check for schemas in union types
304
+ ["type", "disallow"].each do |key|
305
+ if parent_schema.schema[key] && parent_schema.schema[key].is_a?(Array)
306
+ parent_schema.schema[key].each_with_index do |type,i|
307
+ if type.is_a?(Hash)
308
+ handle_schema(parent_schema, type)
309
+ end
310
+ end
311
+ end
312
+ end
313
+
314
+ # "definitions" are schemas in V4
315
+ if parent_schema.schema["definitions"]
316
+ parent_schema.schema["definitions"].each do |k,v|
317
+ handle_schema(parent_schema, v)
318
+ end
319
+ end
320
+
321
+ # All properties are schemas
322
+ if parent_schema.schema["properties"]
323
+ parent_schema.schema["properties"].each do |k,v|
324
+ handle_schema(parent_schema, v)
325
+ end
326
+ end
327
+ if parent_schema.schema["patternProperties"]
328
+ parent_schema.schema["patternProperties"].each do |k,v|
329
+ handle_schema(parent_schema, v)
330
+ end
331
+ end
332
+
333
+ # Items are always schemas
334
+ if parent_schema.schema["items"]
335
+ items = parent_schema.schema["items"].clone
336
+ single = false
337
+ if !items.is_a?(Array)
338
+ items = [items]
339
+ single = true
340
+ end
341
+ items.each_with_index do |item,i|
342
+ handle_schema(parent_schema, item)
343
+ end
344
+ end
345
+
346
+ # Convert enum to a ArraySet
347
+ if parent_schema.schema["enum"] && parent_schema.schema["enum"].is_a?(Array)
348
+ parent_schema.schema["enum"] = ArraySet.new(parent_schema.schema["enum"])
349
+ end
350
+
351
+ # Each of these might be schemas
352
+ ["additionalProperties", "additionalItems", "dependencies", "extends"].each do |key|
353
+ if parent_schema.schema[key].is_a?(Hash)
354
+ handle_schema(parent_schema, parent_schema.schema[key])
355
+ end
356
+ end
357
+
358
+ end
359
+
360
+ # Either load a reference schema or create a new schema
361
+ def handle_schema(parent_schema, obj)
362
+ if obj.is_a?(Hash)
363
+ schema_uri = parent_schema.uri.clone
364
+ schema = JSON::Schema.new(obj,schema_uri,parent_schema.validator)
365
+ if obj['id']
366
+ Validator.add_schema(schema)
367
+ end
368
+ build_schemas(schema)
369
+ end
370
+ end
371
+
372
+ def validation_error(error)
373
+ @errors.push(error)
374
+ end
375
+
376
+ def validation_errors
377
+ @errors
378
+ end
379
+
380
+
381
+ class << self
382
+ def validate(schema, data,opts={})
383
+ begin
384
+ validator = JSON::Validator.new(schema, data, opts)
385
+ validator.validate
386
+ return true
387
+ rescue JSON::Schema::ValidationError, JSON::Schema::SchemaError
388
+ return false
389
+ end
390
+ end
391
+
392
+ def validate_json(schema, data, opts={})
393
+ validate(schema, data, opts.merge(:json => true))
394
+ end
395
+
396
+ def validate_uri(schema, data, opts={})
397
+ validate(schema, data, opts.merge(:uri => true))
398
+ end
399
+
400
+ def validate!(schema, data,opts={})
401
+ validator = JSON::Validator.new(schema, data, opts)
402
+ validator.validate
403
+ return true
404
+ end
405
+ alias_method 'validate2', 'validate!'
406
+
407
+ def validate_json!(schema, data, opts={})
408
+ validate!(schema, data, opts.merge(:json => true))
409
+ end
410
+
411
+ def validate_uri!(schema, data, opts={})
412
+ validate!(schema, data, opts.merge(:uri => true))
413
+ end
414
+
415
+ def fully_validate(schema, data, opts={})
416
+ opts[:record_errors] = true
417
+ validator = JSON::Validator.new(schema, data, opts)
418
+ validator.validate
419
+ end
420
+
421
+ def fully_validate_schema(schema, opts={})
422
+ data = schema
423
+ schema = metaschema_for(version_string_for(opts[:version]))
424
+ fully_validate(schema, data, opts)
425
+ end
426
+
427
+ def fully_validate_json(schema, data, opts={})
428
+ fully_validate(schema, data, opts.merge(:json => true))
429
+ end
430
+
431
+ def fully_validate_uri(schema, data, opts={})
432
+ fully_validate(schema, data, opts.merge(:uri => true))
433
+ end
434
+
435
+ def clear_cache
436
+ @@schemas = {} if @@cache_schemas == false
437
+ end
438
+
439
+ def schemas
440
+ @@schemas
441
+ end
442
+
443
+ def add_schema(schema)
444
+ @@schemas[schema.uri.to_s] = schema if @@schemas[schema.uri.to_s].nil?
445
+ end
446
+
447
+ def cache_schemas=(val)
448
+ warn "[DEPRECATION NOTICE] Schema caching is now a validation option. Schemas will still be cached if this is set to true, but this method will be removed in version >= 3. Please use the :clear_cache validation option instead."
449
+ @@cache_schemas = val == true ? true : false
450
+ end
451
+
452
+ def validators
453
+ @@validators
454
+ end
455
+
456
+ def default_validator
457
+ @@default_validator
458
+ end
459
+
460
+ def register_validator(v)
461
+ @@validators[v.to_s] = v
462
+ end
463
+
464
+ def register_default_validator(v)
465
+ @@default_validator = v
466
+ end
467
+
468
+ def json_backend
469
+ if defined?(MultiJson)
470
+ MultiJson.respond_to?(:adapter) ? MultiJson.adapter : MultiJson.engine
471
+ else
472
+ @@json_backend
473
+ end
474
+ end
475
+
476
+ def json_backend=(backend)
477
+ if defined?(MultiJson)
478
+ backend = backend == 'json' ? 'json_gem' : backend
479
+ MultiJson.respond_to?(:use) ? MultiJson.use(backend) : MultiJson.engine = backend
480
+ else
481
+ backend = backend.to_s
482
+ if @@available_json_backends.include?(backend)
483
+ @@json_backend = backend
484
+ else
485
+ raise JSON::Schema::JsonParseError.new("The JSON backend '#{backend}' could not be found.")
486
+ end
487
+ end
488
+ end
489
+
490
+ def parse(s)
491
+ if defined?(MultiJson)
492
+ MultiJson.respond_to?(:adapter) ? MultiJson.load(s) : MultiJson.decode(s)
493
+ else
494
+ case @@json_backend.to_s
495
+ when 'json'
496
+ JSON.parse(s)
497
+ when 'yajl'
498
+ json = StringIO.new(s)
499
+ parser = Yajl::Parser.new
500
+ parser.parse(json)
501
+ else
502
+ raise JSON::Schema::JsonParseError.new("No supported JSON parsers found. The following parsers are suported:\n * yajl-ruby\n * json")
503
+ end
504
+ end
505
+ end
506
+
507
+ if !defined?(MultiJson)
508
+ if begin
509
+ Gem::Specification::find_by_name('json')
510
+ rescue Gem::LoadError
511
+ false
512
+ rescue
513
+ Gem.available?('json')
514
+ end
515
+ require 'json'
516
+ @@available_json_backends << 'json'
517
+ @@json_backend = 'json'
518
+ end
519
+
520
+ # Try force-loading json for rubies > 1.9.2
521
+ begin
522
+ require 'json'
523
+ @@available_json_backends << 'json'
524
+ @@json_backend = 'json'
525
+ rescue LoadError
526
+ end
527
+
528
+ if begin
529
+ Gem::Specification::find_by_name('yajl-ruby')
530
+ rescue Gem::LoadError
531
+ false
532
+ rescue
533
+ Gem.available?('yajl-ruby')
534
+ end
535
+ require 'yajl'
536
+ @@available_json_backends << 'yajl'
537
+ @@json_backend = 'yajl'
538
+ end
539
+
540
+ if @@json_backend == 'yajl'
541
+ @@serializer = lambda{|o| Yajl::Encoder.encode(o) }
542
+ else
543
+ @@serializer = lambda{|o|
544
+ YAML.dump(o)
545
+ }
546
+ end
547
+ end
548
+ end
549
+
550
+ private
551
+
552
+ if begin
553
+ Gem::Specification::find_by_name('uuidtools')
554
+ rescue Gem::LoadError
555
+ false
556
+ rescue
557
+ Gem.available?('uuidtools')
558
+ end
559
+ require 'uuidtools'
560
+ @@fake_uri_generator = lambda{|s| UUIDTools::UUID.sha1_create(UUIDTools::UUID_URL_NAMESPACE, s).to_s }
561
+ else
562
+ require 'json-schema/uri/uuid'
563
+ @@fake_uri_generator = lambda{|s| JSON::Util::UUID.create_v5(s,JSON::Util::UUID::Nil).to_s }
564
+ end
565
+
566
+ def serialize schema
567
+ if defined?(MultiJson)
568
+ MultiJson.respond_to?(:dump) ? MultiJson.dump(schema) : MultiJson.encode(schema)
569
+ else
570
+ @@serializer.call(schema)
571
+ end
572
+ end
573
+
574
+ def fake_uri schema
575
+ @@fake_uri_generator.call(schema)
576
+ end
577
+
578
+ def schema_to_list(schema)
579
+ new_schema = {"type" => "array", "items" => schema}
580
+ if !schema["$schema"].nil?
581
+ new_schema["$schema"] = schema["$schema"]
582
+ end
583
+
584
+ new_schema
585
+ end
586
+
587
+ def initialize_schema(schema)
588
+ if schema.is_a?(String)
589
+ begin
590
+ # Build a fake URI for this
591
+ schema_uri = URI.parse(fake_uri(schema))
592
+ schema = JSON::Validator.parse(schema)
593
+ if @options[:list] && @options[:fragment].nil?
594
+ schema = schema_to_list(schema)
595
+ end
596
+ schema = JSON::Schema.new(schema,schema_uri,@options[:version])
597
+ Validator.add_schema(schema)
598
+ rescue
599
+ # Build a uri for it
600
+ schema_uri = URI.parse(schema)
601
+ if schema_uri.relative?
602
+ # Check for absolute path
603
+ if schema[0,1] == '/'
604
+ schema_uri = URI.parse("file://#{schema}")
605
+ else
606
+ schema_uri = URI.parse("file://#{Dir.pwd}/#{schema}")
607
+ end
608
+ end
609
+ if Validator.schemas[schema_uri.to_s].nil?
610
+ schema = JSON::Validator.parse(open(schema_uri.to_s).read)
611
+ if @options[:list] && @options[:fragment].nil?
612
+ schema = schema_to_list(schema)
613
+ end
614
+ schema = JSON::Schema.new(schema,schema_uri,@options[:version])
615
+ Validator.add_schema(schema)
616
+ else
617
+ schema = Validator.schemas[schema_uri.to_s]
618
+ end
619
+ end
620
+ elsif schema.is_a?(Hash)
621
+ if @options[:list] && @options[:fragment].nil?
622
+ schema = schema_to_list(schema)
623
+ end
624
+ schema_uri = URI.parse(fake_uri(serialize(schema)))
625
+ schema = JSON::Schema.new(schema,schema_uri,@options[:version])
626
+ Validator.add_schema(schema)
627
+ else
628
+ raise "Invalid schema - must be either a string or a hash"
629
+ end
630
+
631
+ schema
632
+ end
633
+
634
+
635
+ def initialize_data(data)
636
+ if @options[:json]
637
+ data = JSON::Validator.parse(data)
638
+ elsif @options[:uri]
639
+ json_uri = URI.parse(data)
640
+ if json_uri.relative?
641
+ if data[0,1] == '/'
642
+ json_uri = URI.parse("file://#{data}")
643
+ else
644
+ json_uri = URI.parse("file://#{Dir.pwd}/#{data}")
645
+ end
646
+ end
647
+ data = JSON::Validator.parse(open(json_uri.to_s).read)
648
+ elsif data.is_a?(String)
649
+ begin
650
+ data = JSON::Validator.parse(data)
651
+ rescue
652
+ begin
653
+ json_uri = URI.parse(data)
654
+ if json_uri.relative?
655
+ if data[0,1] == '/'
656
+ json_uri = URI.parse("file://#{data}")
657
+ else
658
+ json_uri = URI.parse("file://#{Dir.pwd}/#{data}")
659
+ end
660
+ end
661
+ data = JSON::Validator.parse(open(json_uri.to_s).read)
662
+ rescue
663
+ # Silently discard the error - the data will not change
664
+ end
665
+ end
666
+ end
667
+ JSON::Schema.add_indifferent_access(data)
668
+ data
669
+ end
670
+
671
+ end
672
+ end