json-schema 0.1.1 → 0.1.3

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 (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