json-schema 2.2.5 → 2.3.0
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.
- checksums.yaml +8 -8
- data/README.textile +1 -1
- data/lib/json-schema.rb +1 -0
- data/lib/json-schema/attribute.rb +43 -0
- data/lib/json-schema/attributes/additionalitems.rb +3 -1
- data/lib/json-schema/attributes/additionalproperties.rb +1 -0
- data/lib/json-schema/attributes/allof.rb +6 -4
- data/lib/json-schema/attributes/anyof.rb +7 -5
- data/lib/json-schema/attributes/dependencies.rb +3 -1
- data/lib/json-schema/attributes/dependencies_v4.rb +3 -1
- data/lib/json-schema/attributes/disallow.rb +3 -1
- data/lib/json-schema/attributes/divisibleby.rb +3 -1
- data/lib/json-schema/attributes/enum.rb +3 -1
- data/lib/json-schema/attributes/extends.rb +1 -0
- data/lib/json-schema/attributes/format.rb +6 -112
- data/lib/json-schema/attributes/formats/custom.rb +22 -0
- data/lib/json-schema/attributes/formats/date.rb +25 -0
- data/lib/json-schema/attributes/formats/date_time.rb +35 -0
- data/lib/json-schema/attributes/formats/ip4.rb +22 -0
- data/lib/json-schema/attributes/formats/ip6.rb +30 -0
- data/lib/json-schema/attributes/formats/time.rb +22 -0
- data/lib/json-schema/attributes/formats/uri.rb +18 -0
- data/lib/json-schema/attributes/items.rb +3 -1
- data/lib/json-schema/attributes/maxdecimal.rb +3 -1
- data/lib/json-schema/attributes/maximum.rb +3 -1
- data/lib/json-schema/attributes/maximum_inclusive.rb +2 -0
- data/lib/json-schema/attributes/maxitems.rb +2 -0
- data/lib/json-schema/attributes/maxlength.rb +3 -1
- data/lib/json-schema/attributes/maxproperties.rb +3 -1
- data/lib/json-schema/attributes/minimum.rb +3 -1
- data/lib/json-schema/attributes/minimum_inclusive.rb +3 -1
- data/lib/json-schema/attributes/minitems.rb +4 -2
- data/lib/json-schema/attributes/minlength.rb +3 -1
- data/lib/json-schema/attributes/minproperties.rb +3 -1
- data/lib/json-schema/attributes/multipleof.rb +3 -1
- data/lib/json-schema/attributes/not.rb +3 -1
- data/lib/json-schema/attributes/oneof.rb +2 -0
- data/lib/json-schema/attributes/pattern.rb +3 -1
- data/lib/json-schema/attributes/patternproperties.rb +3 -1
- data/lib/json-schema/attributes/properties.rb +4 -2
- data/lib/json-schema/attributes/properties_optional.rb +3 -1
- data/lib/json-schema/attributes/properties_v4.rb +2 -0
- data/lib/json-schema/attributes/ref.rb +3 -0
- data/lib/json-schema/attributes/required.rb +2 -0
- data/lib/json-schema/attributes/type.rb +6 -20
- data/lib/json-schema/attributes/type_v4.rb +3 -21
- data/lib/json-schema/attributes/uniqueitems.rb +3 -1
- data/lib/json-schema/errors/custom_format_error.rb +6 -0
- data/lib/json-schema/errors/json_parse_error.rb +6 -0
- data/lib/json-schema/errors/schema_error.rb +6 -0
- data/lib/json-schema/errors/validation_error.rb +46 -0
- data/lib/json-schema/schema.rb +1 -5
- data/lib/json-schema/schema/validator.rb +31 -0
- data/lib/json-schema/validator.rb +61 -126
- data/lib/json-schema/validators/draft1.rb +14 -1
- data/lib/json-schema/validators/draft2.rb +14 -1
- data/lib/json-schema/validators/draft3.rb +14 -2
- data/lib/json-schema/validators/draft4.rb +12 -1
- data/test/test_all_of_ref_schema.rb +29 -2
- data/test/test_any_of_ref_schema.rb +26 -0
- data/test/test_bad_schema_ref.rb +2 -2
- data/test/test_common_test_suite.rb +53 -0
- data/test/test_custom_format.rb +117 -0
- data/test/test_jsonschema_draft1.rb +12 -0
- data/test/test_jsonschema_draft2.rb +12 -0
- data/test/test_jsonschema_draft3.rb +31 -0
- data/test/test_jsonschema_draft4.rb +14 -48
- data/test/test_minitems.rb +18 -0
- metadata +21 -4
- data/test/test_suite.rb +0 -71
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YzRhZDY5NzVjYTlkZTM4MDU4YjI1Mjg5YWI3MmJkMDdhNjFiZGE3OA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YWJhZDY3ZWRlZDBlNWFiNDc0ZTM5MzcyZjkyNWJkNWViOGNjN2NiMA==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
OTYzYzFkYzlkMjM5NjY0ZjBmNTVjM2IwZmM0ODQxZWQwMDk5OGE5MTliMmQw
|
10
|
+
MmMwYjAwNWE3NzkzMDYzNzUzN2Q0M2RhMjNmNTFlZGYzZDE0MmMxMTRjM2I2
|
11
|
+
NGM2ZWIxMmE0N2NjMjBjMjRmZTk3NDYyY2Q2YTcyMjNhYzEzMTY=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
YWY3ZjZkMTcxMWRhNDA0MWQxODk3MWQwNzc0NjgyZTc2ZjE5YzE4MjYwYjFj
|
14
|
+
NzI5N2I3NDRhYWM0Y2U4MjhmNDMxZTc1YWYwZmRhNWMxODk5ZjU1YzliZmFm
|
15
|
+
YmY4MTA0ZWRlMzYyMWQ1NDA3MDk5MzUwYmJkN2ExNmQ0NDU5ZjA=
|
data/README.textile
CHANGED
data/lib/json-schema.rb
CHANGED
@@ -20,5 +20,6 @@ require 'json-schema/util/array_set'
|
|
20
20
|
require 'json-schema/schema'
|
21
21
|
require 'json-schema/validator'
|
22
22
|
Dir[File.join(File.dirname(__FILE__), "json-schema/attributes/*.rb")].each {|file| require file }
|
23
|
+
Dir[File.join(File.dirname(__FILE__), "json-schema/attributes/formats/*.rb")].each {|file| require file }
|
23
24
|
Dir[File.join(File.dirname(__FILE__), "json-schema/validators/*.rb")].sort!.each {|file| require file }
|
24
25
|
require 'json-schema/uri/file'
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'json-schema/errors/validation_error'
|
2
|
+
|
3
|
+
module JSON
|
4
|
+
class Schema
|
5
|
+
class Attribute
|
6
|
+
def self.validate(current_schema, data, fragments, processor, validator, options = {})
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.build_fragment(fragments)
|
10
|
+
"#/#{fragments.join('/')}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.validation_error(processor, message, fragments, current_schema, failed_attribute, record_errors)
|
14
|
+
error = ValidationError.new(message, fragments, failed_attribute, current_schema)
|
15
|
+
if record_errors
|
16
|
+
processor.validation_error(error)
|
17
|
+
else
|
18
|
+
raise error
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.validation_errors(validator)
|
23
|
+
validator.validation_errors
|
24
|
+
end
|
25
|
+
|
26
|
+
TYPE_CLASS_MAPPINGS = {
|
27
|
+
"string" => String,
|
28
|
+
"number" => Numeric,
|
29
|
+
"integer" => Integer,
|
30
|
+
"boolean" => [TrueClass, FalseClass],
|
31
|
+
"object" => Hash,
|
32
|
+
"array" => Array,
|
33
|
+
"null" => NilClass,
|
34
|
+
"any" => Object
|
35
|
+
}
|
36
|
+
|
37
|
+
def self.data_valid_for_type?(data, type)
|
38
|
+
valid_classes = TYPE_CLASS_MAPPINGS.fetch(type) { return true }
|
39
|
+
Array(valid_classes).any? { |c| data.is_a?(c) }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -1,12 +1,14 @@
|
|
1
|
+
require 'json-schema/attribute'
|
2
|
+
|
1
3
|
module JSON
|
2
4
|
class Schema
|
3
5
|
class AllOfAttribute < Attribute
|
4
6
|
def self.validate(current_schema, data, fragments, processor, validator, options = {})
|
5
|
-
# Create an
|
6
|
-
errors = []
|
7
|
+
# Create an hash to hold errors that are generated during validation
|
8
|
+
errors = Hash.new { |hsh, k| hsh[k] = [] }
|
7
9
|
valid = true
|
8
10
|
|
9
|
-
current_schema.schema['allOf'].
|
11
|
+
current_schema.schema['allOf'].each_with_index do |element, schema_index|
|
10
12
|
schema = JSON::Schema.new(element,current_schema.uri,validator)
|
11
13
|
|
12
14
|
# We're going to add a little cruft here to try and maintain any validation errors that occur in the allOf
|
@@ -22,7 +24,7 @@ module JSON
|
|
22
24
|
diff = validation_errors(processor).count - pre_validation_error_count
|
23
25
|
while diff > 0
|
24
26
|
diff = diff - 1
|
25
|
-
errors.push(validation_errors(processor).pop)
|
27
|
+
errors["allOf ##{schema_index}"].push(validation_errors(processor).pop)
|
26
28
|
end
|
27
29
|
end
|
28
30
|
|
@@ -1,12 +1,14 @@
|
|
1
|
+
require 'json-schema/attribute'
|
2
|
+
|
1
3
|
module JSON
|
2
4
|
class Schema
|
3
5
|
class AnyOfAttribute < Attribute
|
4
6
|
def self.validate(current_schema, data, fragments, processor, validator, options = {})
|
5
|
-
# Create
|
6
|
-
errors = []
|
7
|
+
# Create a hash to hold errors that are generated during validation
|
8
|
+
errors = Hash.new { |hsh, k| hsh[k] = [] }
|
7
9
|
valid = false
|
8
10
|
|
9
|
-
current_schema.schema['anyOf'].
|
11
|
+
current_schema.schema['anyOf'].each_with_index do |element, schema_index|
|
10
12
|
schema = JSON::Schema.new(element,current_schema.uri,validator)
|
11
13
|
|
12
14
|
# We're going to add a little cruft here to try and maintain any validation errors that occur in the anyOf
|
@@ -24,7 +26,7 @@ module JSON
|
|
24
26
|
valid = false if diff > 0
|
25
27
|
while diff > 0
|
26
28
|
diff = diff - 1
|
27
|
-
errors.push(validation_errors(processor).pop)
|
29
|
+
errors["anyOf ##{schema_index}"].push(validation_errors(processor).pop)
|
28
30
|
end
|
29
31
|
|
30
32
|
break if valid
|
@@ -38,4 +40,4 @@ module JSON
|
|
38
40
|
end
|
39
41
|
end
|
40
42
|
end
|
41
|
-
end
|
43
|
+
end
|
@@ -1,121 +1,15 @@
|
|
1
|
+
require 'json-schema/attribute'
|
1
2
|
require 'uri'
|
2
3
|
|
3
4
|
module JSON
|
4
5
|
class Schema
|
5
6
|
class FormatAttribute < Attribute
|
6
|
-
def self.validate(current_schema, data, fragments, processor, validator, options = {})
|
7
|
-
case current_schema.schema['format']
|
8
|
-
|
9
|
-
# Timestamp in restricted ISO-8601 YYYY-MM-DDThh:mm:ssZ with optional decimal fraction of the second
|
10
|
-
when 'date-time'
|
11
|
-
if data.is_a?(String)
|
12
|
-
error_message = "The property '#{build_fragment(fragments)}' must be a date/time in the ISO-8601 format of YYYY-MM-DDThh:mm:ssZ or YYYY-MM-DDThh:mm:ss.ssZ"
|
13
|
-
r = Regexp.new('^\d\d\d\d-\d\d-\d\dT(\d\d):(\d\d):(\d\d)([\.,]\d+)?(Z|[+-](\d\d)(:?\d\d)?)?$')
|
14
|
-
if (m = r.match(data))
|
15
|
-
parts = data.split("T")
|
16
|
-
begin
|
17
|
-
Date.parse(parts[0])
|
18
|
-
rescue Exception
|
19
|
-
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
|
20
|
-
return
|
21
|
-
end
|
22
|
-
begin
|
23
|
-
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[1].to_i > 23
|
24
|
-
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[2].to_i > 59
|
25
|
-
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[3].to_i > 59
|
26
|
-
rescue Exception
|
27
|
-
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
|
28
|
-
return
|
29
|
-
end
|
30
|
-
else
|
31
|
-
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
|
32
|
-
return
|
33
|
-
end
|
34
|
-
end
|
35
|
-
# Date in the format of YYYY-MM-DD
|
36
|
-
when 'date'
|
37
|
-
if data.is_a?(String)
|
38
|
-
error_message = "The property '#{build_fragment(fragments)}' must be a date in the format of YYYY-MM-DD"
|
39
|
-
r = Regexp.new('^\d\d\d\d-\d\d-\d\d$')
|
40
|
-
if (m = r.match(data))
|
41
|
-
begin
|
42
|
-
Date.parse(data)
|
43
|
-
rescue Exception
|
44
|
-
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
|
45
|
-
return
|
46
|
-
end
|
47
|
-
else
|
48
|
-
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
|
49
|
-
return
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
# Time in the format of HH:MM:SS
|
54
|
-
when 'time'
|
55
|
-
if data.is_a?(String)
|
56
|
-
error_message = "The property '#{build_fragment(fragments)}' must be a time in the format of hh:mm:ss"
|
57
|
-
r = Regexp.new('^(\d\d):(\d\d):(\d\d)$')
|
58
|
-
if (m = r.match(data))
|
59
|
-
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[1].to_i > 23
|
60
|
-
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[2].to_i > 59
|
61
|
-
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[3].to_i > 59
|
62
|
-
else
|
63
|
-
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
|
64
|
-
return
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
# IPv4 in dotted-quad format
|
69
|
-
when 'ip-address', 'ipv4'
|
70
|
-
if data.is_a?(String)
|
71
|
-
error_message = "The property '#{build_fragment(fragments)}' must be a valid IPv4 address"
|
72
|
-
r = Regexp.new('^(\d+){1,3}\.(\d+){1,3}\.(\d+){1,3}\.(\d+){1,3}$')
|
73
|
-
if (m = r.match(data))
|
74
|
-
1.upto(4) do |x|
|
75
|
-
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[x].to_i > 255
|
76
|
-
end
|
77
|
-
else
|
78
|
-
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
|
79
|
-
return
|
80
|
-
end
|
81
|
-
end
|
82
7
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
if (m = r.match(data))
|
89
|
-
# All characters are valid, now validate structure
|
90
|
-
parts = data.split(":")
|
91
|
-
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if parts.length > 8
|
92
|
-
condensed_zeros = false
|
93
|
-
parts.each do |part|
|
94
|
-
if part.length == 0
|
95
|
-
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if condensed_zeros
|
96
|
-
condensed_zeros = true
|
97
|
-
end
|
98
|
-
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if part.length > 4
|
99
|
-
end
|
100
|
-
else
|
101
|
-
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
|
102
|
-
return
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
when 'uri'
|
107
|
-
if data.is_a?(String)
|
108
|
-
error_message = "The property '#{build_fragment(fragments)}' must be a valid URI"
|
109
|
-
begin
|
110
|
-
URI.parse(URI.escape(data))
|
111
|
-
rescue URI::InvalidURIError
|
112
|
-
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
when 'hostname'
|
117
|
-
|
118
|
-
when 'email'
|
8
|
+
def self.validate(current_schema, data, fragments, processor, validator, options = {})
|
9
|
+
if self.data_valid_for_type?(data, current_schema.schema['type'])
|
10
|
+
format = current_schema.schema['format'].to_s
|
11
|
+
validator = validator.formats[format]
|
12
|
+
validator.validate(current_schema, data, fragments, processor, validator, options) unless validator.nil?
|
119
13
|
end
|
120
14
|
end
|
121
15
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'json-schema/attribute'
|
2
|
+
require 'json-schema/errors/custom_format_error'
|
3
|
+
|
4
|
+
require 'uri'
|
5
|
+
module JSON
|
6
|
+
class Schema
|
7
|
+
class CustomFormat < FormatAttribute
|
8
|
+
def initialize(validation_proc)
|
9
|
+
@validation_proc = validation_proc
|
10
|
+
end
|
11
|
+
|
12
|
+
def validate(current_schema, data, fragments, processor, validator, options = {})
|
13
|
+
begin
|
14
|
+
@validation_proc.call data
|
15
|
+
rescue JSON::Schema::CustomFormatError => e
|
16
|
+
message = "The property '#{self.class.build_fragment(fragments)}' #{e.message}"
|
17
|
+
self.class.validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'json-schema/attribute'
|
2
|
+
require 'uri'
|
3
|
+
module JSON
|
4
|
+
class Schema
|
5
|
+
class DateFormat < FormatAttribute
|
6
|
+
def self.validate(current_schema, data, fragments, processor, validator, options = {})
|
7
|
+
if data.is_a?(String)
|
8
|
+
error_message = "The property '#{build_fragment(fragments)}' must be a date in the format of YYYY-MM-DD"
|
9
|
+
r = Regexp.new('^\d\d\d\d-\d\d-\d\d$')
|
10
|
+
if (r.match(data))
|
11
|
+
begin
|
12
|
+
Date.parse(data)
|
13
|
+
rescue Exception
|
14
|
+
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
|
15
|
+
return
|
16
|
+
end
|
17
|
+
else
|
18
|
+
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
|
19
|
+
return
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'json-schema/attribute'
|
2
|
+
require 'uri'
|
3
|
+
module JSON
|
4
|
+
class Schema
|
5
|
+
class DateTimeFormat < FormatAttribute
|
6
|
+
def self.validate(current_schema, data, fragments, processor, validator, options = {})
|
7
|
+
# Timestamp in restricted ISO-8601 YYYY-MM-DDThh:mm:ssZ with optional decimal fraction of the second
|
8
|
+
if data.is_a?(String)
|
9
|
+
error_message = "The property '#{build_fragment(fragments)}' must be a date/time in the ISO-8601 format of YYYY-MM-DDThh:mm:ssZ or YYYY-MM-DDThh:mm:ss.ssZ"
|
10
|
+
r = Regexp.new('^\d\d\d\d-\d\d-\d\dT(\d\d):(\d\d):(\d\d)([\.,]\d+)?(Z|[+-](\d\d)(:?\d\d)?)?$')
|
11
|
+
if (m = r.match(data))
|
12
|
+
parts = data.split("T")
|
13
|
+
begin
|
14
|
+
Date.parse(parts[0])
|
15
|
+
rescue Exception
|
16
|
+
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
|
17
|
+
return
|
18
|
+
end
|
19
|
+
begin
|
20
|
+
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[1].to_i > 23
|
21
|
+
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[2].to_i > 59
|
22
|
+
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[3].to_i > 59
|
23
|
+
rescue Exception
|
24
|
+
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
|
25
|
+
return
|
26
|
+
end
|
27
|
+
else
|
28
|
+
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
|
29
|
+
return
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|