json-schema-openc-fork 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. checksums.yaml +15 -0
  2. data/LICENSE.md +19 -0
  3. data/README.textile +452 -0
  4. data/lib/json-schema.rb +19 -0
  5. data/lib/json-schema/attribute.rb +43 -0
  6. data/lib/json-schema/attributes/additionalitems.rb +28 -0
  7. data/lib/json-schema/attributes/additionalproperties.rb +58 -0
  8. data/lib/json-schema/attributes/allof.rb +39 -0
  9. data/lib/json-schema/attributes/anyof.rb +47 -0
  10. data/lib/json-schema/attributes/dependencies.rb +44 -0
  11. data/lib/json-schema/attributes/disallow.rb +12 -0
  12. data/lib/json-schema/attributes/divisibleby.rb +22 -0
  13. data/lib/json-schema/attributes/enum.rb +24 -0
  14. data/lib/json-schema/attributes/extends.rb +50 -0
  15. data/lib/json-schema/attributes/format.rb +14 -0
  16. data/lib/json-schema/attributes/formats/custom.rb +21 -0
  17. data/lib/json-schema/attributes/formats/date.rb +24 -0
  18. data/lib/json-schema/attributes/formats/date_time.rb +36 -0
  19. data/lib/json-schema/attributes/formats/date_time_v4.rb +15 -0
  20. data/lib/json-schema/attributes/formats/ip.rb +41 -0
  21. data/lib/json-schema/attributes/formats/time.rb +22 -0
  22. data/lib/json-schema/attributes/formats/uri.rb +20 -0
  23. data/lib/json-schema/attributes/items.rb +26 -0
  24. data/lib/json-schema/attributes/limit.rb +179 -0
  25. data/lib/json-schema/attributes/maxdecimal.rb +18 -0
  26. data/lib/json-schema/attributes/multipleof.rb +11 -0
  27. data/lib/json-schema/attributes/not.rb +30 -0
  28. data/lib/json-schema/attributes/oneof.rb +56 -0
  29. data/lib/json-schema/attributes/pattern.rb +18 -0
  30. data/lib/json-schema/attributes/patternproperties.rb +22 -0
  31. data/lib/json-schema/attributes/properties.rb +74 -0
  32. data/lib/json-schema/attributes/properties_optional.rb +26 -0
  33. data/lib/json-schema/attributes/ref.rb +74 -0
  34. data/lib/json-schema/attributes/required.rb +28 -0
  35. data/lib/json-schema/attributes/type.rb +83 -0
  36. data/lib/json-schema/attributes/type_v4.rb +29 -0
  37. data/lib/json-schema/attributes/uniqueitems.rb +16 -0
  38. data/lib/json-schema/errors/custom_format_error.rb +6 -0
  39. data/lib/json-schema/errors/json_parse_error.rb +6 -0
  40. data/lib/json-schema/errors/schema_error.rb +6 -0
  41. data/lib/json-schema/errors/validation_error.rb +46 -0
  42. data/lib/json-schema/schema.rb +63 -0
  43. data/lib/json-schema/schema/reader.rb +113 -0
  44. data/lib/json-schema/schema/validator.rb +36 -0
  45. data/lib/json-schema/util/array_set.rb +14 -0
  46. data/lib/json-schema/util/uri.rb +16 -0
  47. data/lib/json-schema/util/uuid.rb +285 -0
  48. data/lib/json-schema/validator.rb +592 -0
  49. data/lib/json-schema/validators/draft1.rb +45 -0
  50. data/lib/json-schema/validators/draft2.rb +46 -0
  51. data/lib/json-schema/validators/draft3.rb +50 -0
  52. data/lib/json-schema/validators/draft4.rb +56 -0
  53. data/lib/json-schema/validators/hyper-draft4.rb +14 -0
  54. data/resources/draft-01.json +155 -0
  55. data/resources/draft-02.json +166 -0
  56. data/resources/draft-03.json +174 -0
  57. data/resources/draft-04.json +150 -0
  58. data/test/data/all_of_ref_data.json +3 -0
  59. data/test/data/any_of_ref_data.json +7 -0
  60. data/test/data/bad_data_1.json +3 -0
  61. data/test/data/good_data_1.json +3 -0
  62. data/test/data/one_of_ref_links_data.json +5 -0
  63. data/test/schemas/address_microformat.json +18 -0
  64. data/test/schemas/all_of_ref_base_schema.json +6 -0
  65. data/test/schemas/all_of_ref_schema.json +7 -0
  66. data/test/schemas/any_of_ref_jane_schema.json +4 -0
  67. data/test/schemas/any_of_ref_jimmy_schema.json +4 -0
  68. data/test/schemas/any_of_ref_john_schema.json +4 -0
  69. data/test/schemas/any_of_ref_schema.json +15 -0
  70. data/test/schemas/definition_schema.json +15 -0
  71. data/test/schemas/extends_and_additionalProperties-1-filename.schema.json +34 -0
  72. data/test/schemas/extends_and_additionalProperties-1-ref.schema.json +34 -0
  73. data/test/schemas/extends_and_additionalProperties-2-filename.schema.json +33 -0
  74. data/test/schemas/extends_and_additionalProperties-2-ref.schema.json +33 -0
  75. data/test/schemas/good_schema_1.json +10 -0
  76. data/test/schemas/good_schema_2.json +10 -0
  77. data/test/schemas/good_schema_extends1.json +10 -0
  78. data/test/schemas/good_schema_extends2.json +13 -0
  79. data/test/schemas/inner.schema.json +21 -0
  80. data/test/schemas/one_of_ref_links_schema.json +16 -0
  81. data/test/schemas/ref john with spaces schema.json +11 -0
  82. data/test/schemas/relative_definition_schema.json +8 -0
  83. data/test/schemas/self_link_schema.json +17 -0
  84. data/test/schemas/up_link_schema.json +17 -0
  85. data/test/test_all_of_ref_schema.rb +35 -0
  86. data/test/test_any_of_ref_schema.rb +35 -0
  87. data/test/test_bad_schema_ref.rb +39 -0
  88. data/test/test_common_test_suite.rb +66 -0
  89. data/test/test_custom_format.rb +116 -0
  90. data/test/test_definition.rb +15 -0
  91. data/test/test_extended_schema.rb +62 -0
  92. data/test/test_extends_and_additionalProperties.rb +52 -0
  93. data/test/test_files_v3.rb +43 -0
  94. data/test/test_fragment_resolution.rb +30 -0
  95. data/test/test_fragment_validation_with_ref.rb +34 -0
  96. data/test/test_full_validation.rb +208 -0
  97. data/test/test_helper.rb +47 -0
  98. data/test/test_initialize_data.rb +118 -0
  99. data/test/test_jsonschema_draft1.rb +171 -0
  100. data/test/test_jsonschema_draft2.rb +142 -0
  101. data/test/test_jsonschema_draft3.rb +502 -0
  102. data/test/test_jsonschema_draft4.rb +704 -0
  103. data/test/test_list_option.rb +21 -0
  104. data/test/test_merge_missing_values.rb +45 -0
  105. data/test/test_minitems.rb +16 -0
  106. data/test/test_one_of.rb +85 -0
  107. data/test/test_ruby_schema.rb +59 -0
  108. data/test/test_schema_loader.rb +74 -0
  109. data/test/test_schema_type_attribute.rb +20 -0
  110. data/test/test_schema_validation.rb +185 -0
  111. data/test/test_stringify.rb +48 -0
  112. data/test/test_uri_related.rb +67 -0
  113. data/test/test_validator.rb +53 -0
  114. metadata +284 -0
@@ -0,0 +1,36 @@
1
+ require 'json-schema/attribute'
2
+
3
+ module JSON
4
+ class Schema
5
+ class DateTimeFormat < FormatAttribute
6
+ REGEXP = /\A\d{4}-\d{2}-\d{2}T(\d{2}):(\d{2}):(\d{2})([\.,]\d+)?(Z|[+-](\d{2})(:?\d{2})?)?\z/
7
+
8
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
9
+ # Timestamp in restricted ISO-8601 YYYY-MM-DDThh:mm:ssZ with optional decimal fraction of the second
10
+ if data.is_a?(String)
11
+ 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"
12
+ if (m = REGEXP.match(data))
13
+ parts = data.split("T")
14
+
15
+ begin
16
+ Date.parse(parts[0])
17
+ rescue Exception
18
+ validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
19
+ return
20
+ end
21
+
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
+ end
29
+ else
30
+ validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,15 @@
1
+ require 'json-schema/attribute'
2
+
3
+ module JSON
4
+ class Schema
5
+ class DateTimeV4Format < FormatAttribute
6
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
+ return unless data.is_a?(String)
8
+ DateTime.rfc3339(data)
9
+ rescue ArgumentError
10
+ error_message = "The property '#{build_fragment(fragments)}' must be a valid RFC3339 date/time string"
11
+ validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,41 @@
1
+ require 'json-schema/attributes/format'
2
+ require 'ipaddr'
3
+ require 'socket'
4
+
5
+ module JSON
6
+ class Schema
7
+ class IPFormat < FormatAttribute
8
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
9
+ return unless data.is_a?(String)
10
+
11
+ begin
12
+ ip = IPAddr.new(data)
13
+ rescue ArgumentError => e
14
+ raise e unless e.message == 'invalid address'
15
+ end
16
+
17
+ family = ip_version == 6 ? Socket::AF_INET6 : Socket::AF_INET
18
+ unless ip && ip.family == family
19
+ error_message = "The property '#{build_fragment(fragments)}' must be a valid IPv#{ip_version} address"
20
+ validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
21
+ end
22
+ end
23
+
24
+ def self.ip_version
25
+ raise NotImplementedError
26
+ end
27
+ end
28
+
29
+ class IP4Format < IPFormat
30
+ def self.ip_version
31
+ 4
32
+ end
33
+ end
34
+
35
+ class IP6Format < IPFormat
36
+ def self.ip_version
37
+ 6
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,22 @@
1
+ require 'json-schema/attribute'
2
+
3
+ module JSON
4
+ class Schema
5
+ class TimeFormat < FormatAttribute
6
+ REGEXP = /\A(\d{2}):(\d{2}):(\d{2})\z/
7
+
8
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
9
+ if data.is_a?(String)
10
+ error_message = "The property '#{build_fragment(fragments)}' must be a time in the format of hh:mm:ss"
11
+ if (m = REGEXP.match(data))
12
+ validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[1].to_i > 23
13
+ validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[2].to_i > 59
14
+ validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[3].to_i > 59
15
+ else
16
+ validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ require 'json-schema/attribute'
2
+ require 'addressable/uri'
3
+ module JSON
4
+ class Schema
5
+ class UriFormat < FormatAttribute
6
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
+ return unless data.is_a?(String)
8
+ error_message = "The property '#{build_fragment(fragments)}' must be a valid URI"
9
+ begin
10
+ # TODO
11
+ # Addressable only throws an exception on to_s for invalid URI strings, although it
12
+ # probably should throughout parse already - https://github.com/sporkmonger/addressable/issues/177
13
+ Addressable::URI.parse(data).to_s
14
+ rescue Addressable::URI::InvalidURIError
15
+ validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,26 @@
1
+ require 'json-schema/attribute'
2
+
3
+ module JSON
4
+ class Schema
5
+ class ItemsAttribute < Attribute
6
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
+ return unless data.is_a?(Array)
8
+
9
+ items = current_schema.schema['items']
10
+ case items
11
+ when Hash
12
+ schema = JSON::Schema.new(items, current_schema.uri, validator)
13
+ data.each_with_index do |item, i|
14
+ schema.validate(item, fragments + [i.to_s], processor, options)
15
+ end
16
+
17
+ when Array
18
+ items.each_with_index do |item_schema, i|
19
+ schema = JSON::Schema.new(item_schema, current_schema.uri, validator)
20
+ schema.validate(data[i], fragments + [i.to_s], processor, options)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,179 @@
1
+ require 'json-schema/attribute'
2
+
3
+ module JSON
4
+ class Schema
5
+ class LimitAttribute < Attribute
6
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
+ schema = current_schema.schema
8
+ return unless data.is_a?(acceptable_type) && invalid?(schema, value(data))
9
+
10
+ property = build_fragment(fragments)
11
+ description = error_message(schema)
12
+ message = format("The property '%s' %s", property, description)
13
+ validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
14
+ end
15
+
16
+ def self.invalid?(schema, data)
17
+ exclusive = exclusive?(schema)
18
+ limit = limit(schema)
19
+
20
+ if limit_name.start_with?('max')
21
+ exclusive ? data >= limit : data > limit
22
+ else
23
+ exclusive ? data <= limit : data < limit
24
+ end
25
+ end
26
+
27
+ def self.limit(schema)
28
+ schema[limit_name]
29
+ end
30
+
31
+ def self.exclusive?(schema)
32
+ false
33
+ end
34
+
35
+ def self.value(data)
36
+ data
37
+ end
38
+
39
+ def self.acceptable_type
40
+ raise NotImplementedError
41
+ end
42
+
43
+ def self.error_message(schema)
44
+ raise NotImplementedError
45
+ end
46
+
47
+ def self.limit_name
48
+ raise NotImplementedError
49
+ end
50
+ end
51
+
52
+ class MinLengthAttribute < LimitAttribute
53
+ def self.acceptable_type
54
+ String
55
+ end
56
+
57
+ def self.limit_name
58
+ 'minLength'
59
+ end
60
+
61
+ def self.error_message(schema)
62
+ "was not of a minimum string length of #{limit(schema)}"
63
+ end
64
+
65
+ def self.value(data)
66
+ data.length
67
+ end
68
+ end
69
+
70
+ class MaxLengthAttribute < MinLengthAttribute
71
+ def self.limit_name
72
+ 'maxLength'
73
+ end
74
+
75
+ def self.error_message(schema)
76
+ "was not of a maximum string length of #{limit(schema)}"
77
+ end
78
+ end
79
+
80
+ class MinItemsAttribute < LimitAttribute
81
+ def self.acceptable_type
82
+ Array
83
+ end
84
+
85
+ def self.value(data)
86
+ data.length
87
+ end
88
+
89
+ def self.limit_name
90
+ 'minItems'
91
+ end
92
+
93
+ def self.error_message(schema)
94
+ "did not contain a minimum number of items #{limit(schema)}"
95
+ end
96
+ end
97
+
98
+ class MaxItemsAttribute < MinItemsAttribute
99
+ def self.limit_name
100
+ 'maxItems'
101
+ end
102
+
103
+ def self.error_message(schema)
104
+ "had more items than the allowed #{limit(schema)}"
105
+ end
106
+ end
107
+
108
+ class MinPropertiesAttribute < LimitAttribute
109
+ def self.acceptable_type
110
+ Hash
111
+ end
112
+
113
+ def self.value(data)
114
+ data.size
115
+ end
116
+
117
+ def self.limit_name
118
+ 'minProperties'
119
+ end
120
+
121
+ def self.error_message(schema)
122
+ "did not contain a minimum number of properties #{limit(schema)}"
123
+ end
124
+ end
125
+
126
+ class MaxPropertiesAttribute < MinPropertiesAttribute
127
+ def self.limit_name
128
+ 'maxProperties'
129
+ end
130
+
131
+ def self.error_message(schema)
132
+ "had more properties than the allowed #{limit(schema)}"
133
+ end
134
+ end
135
+
136
+ class NumericLimitAttribute < LimitAttribute
137
+ def self.acceptable_type
138
+ Numeric
139
+ end
140
+
141
+ def self.error_message(schema)
142
+ exclusivity = exclusive?(schema) ? 'exclusively' : 'inclusively'
143
+ format("did not have a %s value of %s, %s", limit_name, limit(schema), exclusivity)
144
+ end
145
+ end
146
+
147
+ class MaximumAttribute < NumericLimitAttribute
148
+ def self.limit_name
149
+ 'maximum'
150
+ end
151
+
152
+ def self.exclusive?(schema)
153
+ schema['exclusiveMaximum']
154
+ end
155
+ end
156
+
157
+ class MaximumInclusiveAttribute < MaximumAttribute
158
+ def self.exclusive?(schema)
159
+ schema['maximumCanEqual'] == false
160
+ end
161
+ end
162
+
163
+ class MinimumAttribute < NumericLimitAttribute
164
+ def self.limit_name
165
+ 'minimum'
166
+ end
167
+
168
+ def self.exclusive?(schema)
169
+ schema['exclusiveMinimum']
170
+ end
171
+ end
172
+
173
+ class MinimumInclusiveAttribute < MinimumAttribute
174
+ def self.exclusive?(schema)
175
+ schema['minimumCanEqual'] == false
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,18 @@
1
+ require 'json-schema/attribute'
2
+
3
+ module JSON
4
+ class Schema
5
+ class MaxDecimalAttribute < Attribute
6
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
+ return unless data.is_a?(Numeric)
8
+
9
+ max_decimal_places = current_schema.schema['maxDecimal']
10
+ s = data.to_s.split(".")[1]
11
+ if s && s.length > max_decimal_places
12
+ message = "The property '#{build_fragment(fragments)}' had more decimal places than the allowed #{max_decimal_places}"
13
+ validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,11 @@
1
+ require 'json-schema/attributes/divisibleby'
2
+
3
+ module JSON
4
+ class Schema
5
+ class MultipleOfAttribute < DivisibleByAttribute
6
+ def self.keyword
7
+ 'multipleOf'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,30 @@
1
+ require 'json-schema/attribute'
2
+
3
+ module JSON
4
+ class Schema
5
+ class NotAttribute < Attribute
6
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
+ schema = JSON::Schema.new(current_schema.schema['not'],current_schema.uri,validator)
8
+ failed = true
9
+ errors_copy = processor.validation_errors.clone
10
+
11
+ begin
12
+ schema.validate(data,fragments,processor,options)
13
+ # If we're recording errors, we don't throw an exception. Instead, check the errors array length
14
+ if options[:record_errors] && errors_copy.length != processor.validation_errors.length
15
+ processor.validation_errors.replace(errors_copy)
16
+ else
17
+ message = "The property '#{build_fragment(fragments)}' of type #{data.class} matched the disallowed schema"
18
+ failed = false
19
+ end
20
+ rescue
21
+ # Yay, we failed validation.
22
+ end
23
+
24
+ unless failed
25
+ validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,56 @@
1
+ require 'json-schema/attribute'
2
+
3
+ module JSON
4
+ class Schema
5
+ class OneOfAttribute < Attribute
6
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
+ errors = Hash.new { |hsh, k| hsh[k] = [] }
8
+
9
+ validation_errors = 0
10
+ one_of = current_schema.schema['oneOf']
11
+
12
+ original_data = data.is_a?(Hash) ? data.clone : data
13
+ success_data = nil
14
+
15
+ valid = false
16
+
17
+ one_of.each_with_index do |element, schema_index|
18
+ schema = JSON::Schema.new(element,current_schema.uri,validator)
19
+ pre_validation_error_count = validation_errors(processor).count
20
+ begin
21
+ schema.validate(data,fragments,processor,options)
22
+ success_data = data.is_a?(Hash) ? data.clone : data
23
+ valid = true
24
+ rescue ValidationError
25
+ valid = false
26
+ end
27
+
28
+ diff = validation_errors(processor).count - pre_validation_error_count
29
+ valid = false if diff > 0
30
+ validation_errors += 1 if !valid
31
+ while diff > 0
32
+ diff = diff - 1
33
+ errors["oneOf ##{schema_index}"].push(validation_errors(processor).pop)
34
+ end
35
+ data = original_data
36
+ end
37
+
38
+
39
+
40
+ if validation_errors == one_of.length - 1
41
+ data = success_data
42
+ return
43
+ end
44
+
45
+ if validation_errors == one_of.length
46
+ message = "The property '#{build_fragment(fragments)}' of type #{data.class} did not match any of the required schemas"
47
+ else
48
+ message = "The property '#{build_fragment(fragments)}' of type #{data.class} matched more than one of the required schemas"
49
+ end
50
+
51
+ validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) if message
52
+ validation_errors(processor).last.sub_errors = errors if message
53
+ end
54
+ end
55
+ end
56
+ end