json-schema-pvdgm 2.3.1

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