json-schema 0.1.1 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/README.textile +11 -2
  2. data/lib/json-schema/validator.rb +394 -193
  3. metadata +4 -4
@@ -36,6 +36,16 @@ data = {
36
36
  }
37
37
 
38
38
  JSON::Validator.validate(schema, data)
39
+
40
+ data = {
41
+ "a" => "taco"
42
+ }
43
+
44
+ begin
45
+ JSON::Validator.validate2(schema, data)
46
+ rescue ValidationError
47
+ puts $!.message
48
+ end
39
49
  </pre>
40
50
 
41
51
 
@@ -90,6 +100,5 @@ In addition, the following hyper schema attributes are not implemented at this t
90
100
 
91
101
  h2. To Do
92
102
 
93
- * Massive re-factoring - the whole validation process needs to be broken down into something a bit more manageable
94
103
  * (Much) More testing
95
- * Better interface to validator to allow for error messages in validation to propogate
104
+ * Adding a persistence option to schemas that enables the validator to only load schemas once across multiple validations
@@ -4,8 +4,43 @@ require 'pathname'
4
4
  require 'bigdecimal'
5
5
 
6
6
  module JSON
7
+
8
+ class ValidationError < Exception
9
+ attr_reader :fragments, :schema
10
+
11
+ def initialize(message, fragments, schema)
12
+ @fragments = fragments
13
+ @schema = schema
14
+ super(message)
15
+ end
16
+ end
17
+
7
18
  class Validator
8
19
 
20
+ ValidationMethods = [
21
+ "type",
22
+ "disallow",
23
+ "minimum",
24
+ "maximum",
25
+ "minItems",
26
+ "maxItems",
27
+ "uniqueItems",
28
+ "pattern",
29
+ "minLength",
30
+ "maxLength",
31
+ "divisibleBy",
32
+ "enum",
33
+ "properties",
34
+ "patternProperties",
35
+ "additionalProperties",
36
+ "items",
37
+ "additionalItems",
38
+ "dependencies",
39
+ "extends",
40
+ "$ref"
41
+ ]
42
+
43
+
9
44
  def initialize(schema_data, data)
10
45
  @schemas = {}
11
46
  @base_schema = initialize_schema(schema_data)
@@ -16,114 +51,279 @@ module JSON
16
51
  end
17
52
 
18
53
 
54
+ # Run a simple true/false validation of data against a schema
19
55
  def validate()
20
- validate_schema(@base_schema, @data)
56
+ begin
57
+ validate_schema(@base_schema, @data, [])
58
+ return true
59
+ rescue ValidationError
60
+ return false
61
+ end
21
62
  end
22
63
 
23
64
 
24
- def validate_schema(current_schema, data)
25
- valid = true # Actually doing true/false on validation. Return at any point if invalid
65
+ # Validate data against a schema, returning nil if the data is valid. If the data is invalid,
66
+ # a ValidationError will be raised with links to the specific location that the first error
67
+ # occurred during validation
68
+ def validate2()
69
+ validate_schema(@base_schema, @data, [])
70
+ nil
71
+ end
72
+
73
+
74
+ # Validate the current schema
75
+ def validate_schema(current_schema, data, fragments)
26
76
 
27
- # Check the type
28
- if current_schema.schema['type']
29
- valid = validate_type(current_schema.schema['type'],data,current_schema)
30
- end
31
- return valid if !valid
32
- if current_schema.schema['disallow']
33
- valid = !validate_type(current_schema.schema['disallow'],data,current_schema)
77
+ ValidationMethods.each do |method|
78
+ if current_schema.schema[method]
79
+ self.send(("validate_" + method.sub('$','')).to_sym, current_schema, data, fragments)
80
+ end
34
81
  end
35
- return valid if !valid
36
82
 
37
- # Check the values
38
- if current_schema.schema['minimum'] && data.is_a?(Numeric)
39
- valid = current_schema.schema['exclusiveMinimum'] ? data > current_schema.schema['minimum'] : data >= current_schema.schema['minimum']
40
- end
41
- return valid if !valid
83
+ data
84
+ end
85
+
86
+
87
+ # Validate the type
88
+ def validate_type(current_schema, data, fragments, disallow=false)
89
+ union = true
42
90
 
43
- if current_schema.schema['maximum'] && data.is_a?(Numeric)
44
- valid = current_schema.schema['exclusiveMaximum'] ? data < current_schema.schema['maximum'] : data <= current_schema.schema['maximum']
91
+ if disallow
92
+ types = current_schema.schema['disallow']
93
+ else
94
+ types = current_schema.schema['type']
45
95
  end
46
- return valid if !valid
47
96
 
48
- if current_schema.schema['minItems'] && data.is_a?(Array)
49
- valid = data.nitems >= current_schema.schema['minItems']
97
+ if !types.is_a?(Array)
98
+ types = [types]
99
+ union = false
50
100
  end
51
- return valid if !valid
101
+ valid = false
52
102
 
53
- if current_schema.schema['maxItems'] && data.is_a?(Array)
54
- valid = data.nitems <= current_schema.schema['maxItems']
103
+ types.each do |type|
104
+ if type.is_a?(String)
105
+ case type
106
+ when "string"
107
+ valid = data.is_a?(String)
108
+ when "number"
109
+ valid = data.is_a?(Numeric)
110
+ when "integer"
111
+ valid = data.is_a?(Integer)
112
+ when "boolean"
113
+ valid = (data.is_a?(TrueClass) || data.is_a?(FalseClass))
114
+ when "object"
115
+ valid = data.is_a?(Hash)
116
+ when "array"
117
+ valid = data.is_a?(Array)
118
+ when "null"
119
+ valid = data.is_a?(NilClass)
120
+ when "any"
121
+ valid = true
122
+ else
123
+ valid = true
124
+ end
125
+ elsif type.is_a?(Hash) && union
126
+ # Validate as a schema
127
+ schema = JSON::Schema.new(type,current_schema.uri)
128
+ begin
129
+ validate_schema(schema,data,fragments)
130
+ valid = true
131
+ rescue ValidationError
132
+ # We don't care that these schemas don't validate - we only care that one validated
133
+ end
134
+ end
135
+
136
+ break if valid
55
137
  end
56
- return valid if !valid
57
138
 
58
- if current_schema.schema['uniqueItems'] && data.is_a?(Array)
139
+ if (disallow)
140
+ if valid
141
+ message = "The property '#{build_fragment(fragments)}' matched one or more of the following types:"
142
+ types.each {|type| message += type.is_a?(String) ? " #{type}," : " (schema)," }
143
+ message.chop!
144
+ raise ValidationError.new(message, fragments, current_schema)
145
+ end
146
+ elsif !valid
147
+ message = "The property '#{build_fragment(fragments)}' did not match one or more of the following types:"
148
+ types.each {|type| message += type.is_a?(String) ? " #{type}," : " (schema)," }
149
+ message.chop!
150
+ raise ValidationError.new(message, fragments, current_schema)
151
+ end
152
+ end
153
+
154
+
155
+ # Validate the disallowed types
156
+ def validate_disallow(current_schema, data, fragments)
157
+ validate_type(current_schema, data, fragments, true)
158
+ end
159
+
160
+
161
+ # Validate the minimum value of a number
162
+ def validate_minimum(current_schema, data, fragments)
163
+ if data.is_a?(Numeric)
164
+ if (current_schema.schema['exclusiveMinimum'] ? data <= current_schema.schema['minimum'] : data < current_schema.schema['minimum'])
165
+ message = "The property '#{build_fragment(fragments)}' did not have a minimum value of #{current_schema.schema['minimum']}, "
166
+ message += current_schema.schema['exclusiveMinimum'] ? 'exclusively' : 'inclusively'
167
+ raise ValidationError.new(message, fragments, current_schema)
168
+ end
169
+ end
170
+ end
171
+
172
+
173
+ # Validate the maximum value of a number
174
+ def validate_maximum(current_schema, data, fragments)
175
+ if data.is_a?(Numeric)
176
+ if (current_schema.schema['exclusiveMaximum'] ? data >= current_schema.schema['maximum'] : data > current_schema.schema['maximum'])
177
+ message = "The property '#{build_fragment(fragments)}' did not have a maximum value of #{current_schema.schema['maximum']}, "
178
+ message += current_schema.schema['exclusiveMaximum'] ? 'exclusively' : 'inclusively'
179
+ raise ValidationError.new(message, fragments, current_schema)
180
+ end
181
+ end
182
+ end
183
+
184
+
185
+ # Validate the minimum number of items in an array
186
+ def validate_minItems(current_schema, data, fragments)
187
+ if data.is_a?(Array) && (data.nitems < current_schema.schema['minItems'])
188
+ message = "The property '#{build_fragment(fragments)}' did not contain a minimum number of items #{current_schema.schema['minItems']}"
189
+ raise ValidationError.new(message, fragments, current_schema)
190
+ end
191
+ end
192
+
193
+
194
+ # Validate the maximum number of items in an array
195
+ def validate_maxItems(current_schema, data, fragments)
196
+ if data.is_a?(Array) && (data.nitems > current_schema.schema['maxItems'])
197
+ message = "The property '#{build_fragment(fragments)}' did not contain a minimum number of items #{current_schema.schema['minItems']}"
198
+ raise ValidationError.new(message, fragments, current_schema)
199
+ end
200
+ end
201
+
202
+
203
+ # Validate the uniqueness of elements in an array
204
+ def validate_uniqueItems(current_schema, data, fragments)
205
+ if data.is_a?(Array)
59
206
  d = data.clone
60
207
  dupes = d.uniq!
61
- valid = dupes.nil?
208
+ if dupes
209
+ message = "The property '#{build_fragment(fragments)}' contained duplicated array values"
210
+ raise ValidationError.new(message, fragments, current_schema)
211
+ end
62
212
  end
63
- return valid if !valid
64
-
65
- if current_schema.schema['pattern'] && data.is_a?(String)
213
+ end
214
+
215
+
216
+ # Validate a string matches a regex pattern
217
+ def validate_pattern(current_schema, data, fragments)
218
+ if data.is_a?(String)
66
219
  r = Regexp.new(current_schema.schema['pattern'])
67
- valid = !(r.match(data)).nil?
220
+ if (r.match(data)).nil?
221
+ message = "The property '#{build_fragment(fragments)}' did not match the regex '#{current_schema.schema['pattern']}'"
222
+ raise ValidationError.new(message, fragments, current_schema)
223
+ end
68
224
  end
69
- return valid if !valid
70
-
71
- if current_schema.schema['minLength'] && data.is_a?(String)
72
- valid = data.length >= current_schema.schema['minLength']
225
+ end
226
+
227
+
228
+ # Validate a string is at least of a certain length
229
+ def validate_minLength(current_schema, data, fragments)
230
+ if data.is_a?(String)
231
+ if data.length < current_schema.schema['minLength']
232
+ message = "The property '#{build_fragment(fragments)}' was not of a minimum string length of #{current_schema.schema['minLength']}"
233
+ raise ValidationError.new(message, fragments, current_schema)
234
+ end
73
235
  end
74
- return valid if !valid
75
-
76
- if current_schema.schema['maxLength'] && data.is_a?(String)
77
- valid = data.length <= current_schema.schema['maxLength']
236
+ end
237
+
238
+
239
+ # Validate a string is at maximum of a certain length
240
+ def validate_maxLength(current_schema, data, fragments)
241
+ if data.is_a?(String)
242
+ if data.length > current_schema.schema['maxLength']
243
+ message = "The property '#{build_fragment(fragments)}' was not of a maximum string length of #{current_schema.schema['maxLength']}"
244
+ raise ValidationError.new(message, fragments, current_schema)
245
+ end
78
246
  end
79
- return valid if !valid
80
-
81
- if current_schema.schema['divisibleBy'] && data.is_a?(Numeric)
82
- if current_schema.schema['divisibleBy'] == 0 || current_schema.schema['divisibleBy'] == 0.0
83
- return false
84
- else
85
- valid = (BigDecimal.new(data.to_s) % BigDecimal.new(current_schema.schema['divisibleBy'].to_s)).to_f == 0
247
+ end
248
+
249
+
250
+ # Validate a numeric is divisible by another numeric
251
+ def validate_divisibleBy(current_schema, data, fragments)
252
+ if data.is_a?(Numeric)
253
+ if current_schema.schema['divisibleBy'] == 0 ||
254
+ current_schema.schema['divisibleBy'] == 0.0 ||
255
+ (BigDecimal.new(data.to_s) % BigDecimal.new(current_schema.schema['divisibleBy'].to_s)).to_f != 0
256
+ message = "The property '#{build_fragment(fragments)}' was not divisible by #{current_schema.schema['divisibleBy']}"
257
+ raise ValidationError.new(message, fragments, current_schema)
86
258
  end
87
259
  end
88
- return valid if !valid
89
-
90
- if current_schema.schema['enum']
91
- valid = current_schema.schema['enum'].include?(data)
260
+ end
261
+
262
+
263
+ # Validate an item matches at least one of an array of values
264
+ def validate_enum(current_schema, data, fragments)
265
+ if !current_schema.schema['enum'].include?(data)
266
+ message = "The property '#{build_fragment(fragments)}' did not match one of the following values:"
267
+ current_schema.schema['enum'].each {|val|
268
+ if val.is_a?(NilClass)
269
+ message += " null,"
270
+ elsif val.is_a?(Array)
271
+ message += " (array),"
272
+ elsif val.is_a?(Hash)
273
+ message += " (object),"
274
+ else
275
+ message += " #{val.to_s},"
276
+ end
277
+ }
278
+ message.chop!
279
+ raise ValidationError.new(message, fragments, current_schema)
92
280
  end
93
- return valid if !valid
94
-
95
-
96
- if current_schema.schema['properties'] && data.is_a?(Hash)
281
+ end
282
+
283
+
284
+ # Validate a set of properties of an object
285
+ def validate_properties(current_schema, data, fragments)
286
+ if data.is_a?(Hash)
97
287
  current_schema.schema['properties'].each do |property,property_schema|
98
- valid = false if (property_schema['required'] && !data.has_key?(property))
288
+ if (property_schema['required'] && !data.has_key?(property))
289
+ message = "The property '#{build_fragment(fragments)}' did not contain a required property of '#{property}'"
290
+ raise ValidationError.new(message, fragments, current_schema)
291
+ end
99
292
 
100
293
  if data.has_key?(property)
101
294
  schema = JSON::Schema.new(property_schema,current_schema.uri)
102
- valid = validate_schema(schema, data[property])
295
+ fragments << property
296
+ validate_schema(schema, data[property], fragments)
297
+ fragments.pop
103
298
  end
104
- return valid if !valid
105
299
  end
106
300
  end
107
- return valid if !valid
108
-
109
- if current_schema.schema['patternProperties'] && data.is_a?(Hash)
110
- current_schema.schema['properties'].each do |property,property_schema|
111
-
301
+ end
302
+
303
+
304
+ # Validate properties of an object against a schema when the property name matches a specific regex
305
+ def validate_patternProperties(current_schema, data, fragments)
306
+ if data.is_a?(Hash)
307
+ current_schema.schema['patternProperties'].each do |property,property_schema|
112
308
  r = Regexp.new(property)
113
309
 
114
310
  # Check each key in the data hash to see if it matches the regex
115
311
  data.each do |key,value|
116
312
  if r.match(key)
117
313
  schema = JSON::Schema.new(property_schema,current_schema.uri)
118
- valid = validate_schema(schema, data[key])
314
+ fragments << key
315
+ validate_schema(schema, data[key], fragments)
316
+ fragments.pop
119
317
  end
120
- return valid if !valid
121
318
  end
122
319
  end
123
320
  end
124
- return valid if !valid
125
-
126
- if current_schema.schema['additionalProperties'] && data.is_a?(Hash)
321
+ end
322
+
323
+
324
+ # Validate properties of an object that are not defined in the schema at least validate against a set of rules
325
+ def validate_additionalProperties(current_schema, data, fragments)
326
+ if data.is_a?(Hash)
127
327
  extra_properties = data.keys
128
328
 
129
329
  if current_schema.schema['properties']
@@ -142,162 +342,145 @@ module JSON
142
342
  end
143
343
  end
144
344
 
145
- if current_schema.schema['additionalProperties'] == false
146
- valid = extra_properties.empty?
345
+ if current_schema.schema['additionalProperties'] == false && !extra_properties.empty?
346
+ message = "The property '#{build_fragment(fragments)}' contains additional properties outside of the schema when none are allowed"
347
+ raise ValidationError.new(message, fragments, current_schema)
147
348
  elsif current_schema.schema['additionalProperties'].is_a?(Hash)
148
349
  data.each do |key,value|
149
350
  schema = JSON::Schema.new(current_schema.schema['additionalProperties'],current_schema.uri)
150
- valid = validate_schema(schema,value)
151
- return valid if !valid
351
+ fragments << key
352
+ validate_schema(schema, value, fragments)
353
+ fragments.pop
152
354
  end
153
355
  end
154
356
  end
155
- return valid if !valid
156
-
157
- if current_schema.schema['items'] && data.is_a?(Array)
158
- if current_schema.schema['items'].is_a?(Hash)
159
- data.each do |item|
160
- schema = JSON::Schema.new(current_schema.schema['items'],current_schema.uri)
161
- valid = validate_schema(schema,item)
162
- return valid if !valid
163
- end
164
- elsif current_schema.schema['items'].is_a?(Array)
165
- current_schema.schema['items'].each_with_index do |item_schema,i|
166
- schema = JSON::Schema.new(item_schema,current_schema.uri)
167
- valid = validate_schema(schema,data[i])
168
- return valid if !valid
169
- end
170
- end
357
+ end
358
+
359
+
360
+ # Validate items in an array match a schema or a set of schemas
361
+ def validate_items(current_schema, data, fragments)
362
+ if data.is_a?(Array)
363
+ if current_schema.schema['items'].is_a?(Hash)
364
+ data.each_with_index do |item,i|
365
+ schema = JSON::Schema.new(current_schema.schema['items'],current_schema.uri)
366
+ fragments << i.to_s
367
+ validate_schema(schema,item,fragments)
368
+ fragments.pop
369
+ end
370
+ elsif current_schema.schema['items'].is_a?(Array)
371
+ current_schema.schema['items'].each_with_index do |item_schema,i|
372
+ schema = JSON::Schema.new(item_schema,current_schema.uri)
373
+ fragments << i.to_s
374
+ validate_schema(schema,data[i],fragments)
375
+ fragments.pop
376
+ end
377
+ end
171
378
  end
172
- return valid if !valid
173
-
174
- if current_schema.schema['additionalItems'] && data.is_a?(Array) && current_schema.schema['items'].is_a?(array)
175
- if current_schema.schema['additionalItems'] == false
176
- return current_schema.schema['additionalItems'].length == data.length
379
+ end
380
+
381
+
382
+ # Validate items in an array that are not part of the schema at least match a set of rules
383
+ def validate_additionalItems(current_schema, data, fragments)
384
+ if data.is_a?(Array) && current_schema.schema['items'].is_a?(Array)
385
+ if current_schema.schema['additionalItems'] == false && current_schema.schema['items'].length != data.length
386
+ message = "The property '#{build_fragment(fragments)}' contains additional array elements outside of the schema when none are allowed"
387
+ raise ValidationError.new(message, fragments, current_schema)
177
388
  elsif current_schema.schema['additionaItems'].is_a?(Hash)
178
- data.each do |item|
179
- schema = JSON::Schema.new(current_schema.schema['additionalItems'],current_schema.uri)
180
- valid = validate_schema(schema, item)
181
- return valid if !valid
389
+ schema = JSON::Schema.new(current_schema.schema['additionalItems'],current_schema.uri)
390
+ data.each_with_index do |item,i|
391
+ if i >= current_schema.schema['items'].length
392
+ fragments << i.to_s
393
+ validate_schema(schema, item, fragments)
394
+ fragments.pop
395
+ end
182
396
  end
183
397
  end
184
398
  end
185
- return valid if !valid
186
-
187
- if current_schema.schema['dependences'] && data.is_a?(Hash)
399
+ end
400
+
401
+
402
+ # Validate the dependencies of a property
403
+ def validate_dependencies(current_schema, data, fragments)
404
+ if data.is_a?(Hash)
188
405
  current_schema.schema['dependencies'].each do |property,dependency_value|
189
406
  if data.has_key?(property)
190
- if dependency_value.is_a?(String)
191
- valid = data.has_key?(dependency_value)
407
+ if dependency_value.is_a?(String) && !data.has_key?(dependency_value)
408
+ message = "The property '#{build_fragment(fragments)}' has a property '#{property}' that depends on a missing property '#{dependency_value}'"
409
+ raise ValidationError.new(message, fragments, current_schema)
192
410
  elsif dependency_value.is_a?(Array)
193
411
  dependency_value.each do |value|
194
- valid = data.has_key?(value)
195
- break if !valid
412
+ if !data.has_key?(value)
413
+ message = "The property '#{build_fragment(fragments)}' has a property '#{property}' that depends on a missing property '#{value}'"
414
+ raise ValidationError.new(message, fragments, current_schema)
415
+ end
196
416
  end
197
417
  else
198
418
  schema = JSON::Schema.new(dependency_value,current_schema.uri)
199
- valid = validate_schema(schema, data)
200
- end
201
- return valid if !valid
202
- end
203
- end
204
- end
205
- return valid if !valid
206
-
207
- # We're referencing another schema
208
- if current_schema.schema['$ref']
209
- temp_uri = URI.parse(current_schema.schema['$ref'])
210
- if temp_uri.relative?
211
- temp_uri = current_schema.uri.clone
212
- # Check for absolute path
213
- path = current_schema.schema['$ref'].split("#")[0]
214
- if path[0,1] == "/"
215
- temp_uri.path = Pathname.new(path).cleanpath
216
- else
217
- temp_uri.path = (Pathname.new(current_schema.uri.path).parent + path).cleanpath
218
- end
219
- temp_uri.fragment = current_schema.schema['$ref'].split("#")[1]
220
- end
221
- temp_uri.fragment = "" if temp_uri.fragment.nil?
222
-
223
- # Grab the parent schema from the schema list
224
- schema_key = temp_uri.to_s.split("#")[0]
225
- ref_schema = @schemas[schema_key]
226
-
227
- if ref_schema
228
- # Perform fragment resolution to retrieve the appropriate level for the schema
229
- target_schema = ref_schema.schema
230
- fragments = temp_uri.fragment.split("/")
231
- fragments.each do |fragment|
232
- if fragment && fragment != ''
233
- if target_schema.is_a?(Array)
234
- target_schema = target_schema[fragment.to_i]
235
- else
236
- target_schema = target_schema[fragment]
237
- end
419
+ validate_schema(schema, data, fragments)
238
420
  end
239
421
  end
240
-
241
- # We have the schema finally, build it and validate!
242
- schema = JSON::Schema.new(target_schema,temp_uri)
243
- valid = validate_schema(schema, data)
244
- end
245
- end
246
-
247
- if current_schema.schema['extends']
248
- schemas = current_schema.schema['extends']
249
- schemas = [schemas] if !schema.is_a?(Array)
250
- schemas.each do |s|
251
- schema = JSON::Schema.new(s,current_schema.uri)
252
- valid = validate_schema(schema, item)
253
- return valid if !valid
254
422
  end
255
423
  end
256
-
257
- valid
258
424
  end
259
425
 
260
426
 
261
- def validate_type(types, data,current_schema)
262
- union = true
263
- if !types.is_a?(Array)
264
- types = [types]
265
- union = true
427
+ # Validate extensions of other schemas
428
+ def validate_extends(current_schema, data, fragments)
429
+ schemas = current_schema.schema['extends']
430
+ schemas = [schemas] if !schemas.is_a?(Array)
431
+ schemas.each do |s|
432
+ schema = JSON::Schema.new(s,current_schema.uri)
433
+ validate_schema(schema, data, fragments)
266
434
  end
267
- valid = false
268
-
269
- types.each do |type|
270
- if type.is_a?(String)
271
- case type
272
- when "string"
273
- valid = data.is_a?(String)
274
- when "number"
275
- valid = data.is_a?(Numeric)
276
- when "integer"
277
- valid = data.is_a?(Integer)
278
- when "boolean"
279
- valid = (data.is_a?(TrueClass) || data.is_a?(FalseClass))
280
- when "object"
281
- valid = data.is_a?(Hash)
282
- when "array"
283
- valid = data.is_a?(Array)
284
- when "null"
285
- valid = data.is_a?(NilClass)
286
- else
287
- valid = true
435
+ end
436
+
437
+
438
+ # Validate schema references
439
+ def validate_ref(current_schema, data, fragments)
440
+ temp_uri = URI.parse(current_schema.schema['$ref'])
441
+ if temp_uri.relative?
442
+ temp_uri = current_schema.uri.clone
443
+ # Check for absolute path
444
+ path = current_schema.schema['$ref'].split("#")[0]
445
+ if path[0,1] == "/"
446
+ temp_uri.path = Pathname.new(path).cleanpath
447
+ else
448
+ temp_uri.path = (Pathname.new(current_schema.uri.path).parent + path).cleanpath
449
+ end
450
+ temp_uri.fragment = current_schema.schema['$ref'].split("#")[1]
451
+ end
452
+ temp_uri.fragment = "" if temp_uri.fragment.nil?
453
+
454
+ # Grab the parent schema from the schema list
455
+ schema_key = temp_uri.to_s.split("#")[0]
456
+ ref_schema = @schemas[schema_key]
457
+
458
+ if ref_schema
459
+ # Perform fragment resolution to retrieve the appropriate level for the schema
460
+ target_schema = ref_schema.schema
461
+ fragments = temp_uri.fragment.split("/")
462
+ fragments.each do |fragment|
463
+ if fragment && fragment != ''
464
+ if target_schema.is_a?(Array)
465
+ target_schema = target_schema[fragment.to_i]
466
+ else
467
+ target_schema = target_schema[fragment]
468
+ end
288
469
  end
289
- elsif type.is_a?(Hash) && union
290
- # Validate as a schema
291
- schema = JSON::Schema.new(type,current_schema.uri)
292
- valid = validate_schema(schema,data)
293
470
  end
294
-
295
- return valid if valid
471
+
472
+ # We have the schema finally, build it and validate!
473
+ schema = JSON::Schema.new(target_schema,temp_uri)
474
+ validate_schema(schema, data, fragments)
296
475
  end
297
-
298
- valid
299
476
  end
300
477
 
478
+
479
+ def build_fragment(fragments)
480
+ "#/#{fragments.join('/')}"
481
+ end
482
+
483
+
301
484
  def load_ref_schema(parent_schema,ref)
302
485
  uri = URI.parse(ref)
303
486
  if uri.relative?
@@ -315,7 +498,6 @@ module JSON
315
498
  if @schemas[uri.to_s].nil?
316
499
  begin
317
500
  schema = JSON::Schema.new(JSON.parse(open(uri.to_s).read), uri)
318
- @schemas[uri.to_s] = schema
319
501
  build_schemas(schema)
320
502
  rescue
321
503
  # Failures will occur when this URI cannot be referenced yet. Don't worry about it,
@@ -436,6 +618,19 @@ module JSON
436
618
  build_schemas(schema)
437
619
  end
438
620
  end
621
+
622
+ if parent_schema.schema["extends"].is_a?(Hash)
623
+ if parent_schema.schema["extends"]['$ref']
624
+ load_ref_schema(parent_schema, parent_schema.schema["extends"]['$ref'])
625
+ else
626
+ schema_uri = parent_schema.uri.clone
627
+ schema = JSON::Schema.new(parent_schema.schema["extends"],schema_uri)
628
+ if parent_schema.schema["extends"]['id']
629
+ @schemas[schema.uri.to_s] = schema
630
+ end
631
+ build_schemas(schema)
632
+ end
633
+ end
439
634
  end
440
635
 
441
636
 
@@ -444,7 +639,13 @@ module JSON
444
639
  validator = JSON::Validator.new(schema, data)
445
640
  validator.validate
446
641
  end
642
+
643
+ def validate2(schema, data)
644
+ validator = JSON::Validator.new(schema, data)
645
+ validator.validate2
646
+ end
447
647
  end
648
+
448
649
 
449
650
 
450
651
  private
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json-schema
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
4
+ hash: 29
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 1
10
- version: 0.1.1
9
+ - 3
10
+ version: 0.1.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Kenny Hoxworth
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-11-29 00:00:00 -05:00
18
+ date: 2010-11-30 00:00:00 -05:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency