json-schema 2.4.1 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +6 -14
  2. data/README.textile +58 -7
  3. data/lib/json-schema.rb +3 -1
  4. data/lib/json-schema/attributes/additionalitems.rb +14 -11
  5. data/lib/json-schema/attributes/additionalproperties.rb +33 -43
  6. data/lib/json-schema/attributes/anyof.rb +4 -0
  7. data/lib/json-schema/attributes/dependencies.rb +31 -19
  8. data/lib/json-schema/attributes/disallow.rb +2 -3
  9. data/lib/json-schema/attributes/divisibleby.rb +11 -7
  10. data/lib/json-schema/attributes/enum.rb +14 -16
  11. data/lib/json-schema/attributes/format.rb +4 -7
  12. data/lib/json-schema/attributes/formats/date_time_v4.rb +5 -8
  13. data/lib/json-schema/attributes/formats/ip.rb +41 -0
  14. data/lib/json-schema/attributes/formats/uri.rb +10 -8
  15. data/lib/json-schema/attributes/items.rb +15 -16
  16. data/lib/json-schema/attributes/limit.rb +179 -0
  17. data/lib/json-schema/attributes/maxdecimal.rb +7 -6
  18. data/lib/json-schema/attributes/multipleof.rb +4 -11
  19. data/lib/json-schema/attributes/not.rb +1 -1
  20. data/lib/json-schema/attributes/oneof.rb +15 -6
  21. data/lib/json-schema/attributes/pattern.rb +7 -6
  22. data/lib/json-schema/attributes/patternproperties.rb +9 -12
  23. data/lib/json-schema/attributes/properties.rb +55 -39
  24. data/lib/json-schema/attributes/properties_optional.rb +13 -12
  25. data/lib/json-schema/attributes/ref.rb +4 -4
  26. data/lib/json-schema/attributes/required.rb +16 -13
  27. data/lib/json-schema/attributes/type.rb +13 -18
  28. data/lib/json-schema/attributes/type_v4.rb +11 -18
  29. data/lib/json-schema/attributes/uniqueitems.rb +5 -7
  30. data/lib/json-schema/schema.rb +8 -8
  31. data/lib/json-schema/schema/#validator.rb# +37 -0
  32. data/lib/json-schema/schema/reader.rb +113 -0
  33. data/lib/json-schema/util/uri.rb +16 -0
  34. data/lib/json-schema/validator.rb +123 -128
  35. data/lib/json-schema/validators/draft1.rb +1 -1
  36. data/lib/json-schema/validators/draft2.rb +1 -1
  37. data/lib/json-schema/validators/draft3.rb +1 -1
  38. data/lib/json-schema/validators/draft4.rb +1 -1
  39. data/lib/json-schema/validators/hyper-draft4.rb +1 -1
  40. data/test/schemas/address_microformat.json +18 -0
  41. data/test/schemas/definition_schema.json +15 -0
  42. data/test/schemas/ref john with spaces schema.json +11 -0
  43. data/test/schemas/relative_definition_schema.json +8 -0
  44. data/test/test_all_of_ref_schema.rb +12 -15
  45. data/test/test_any_of_ref_schema.rb +7 -9
  46. data/test/test_bad_schema_ref.rb +18 -12
  47. data/test/test_common_test_suite.rb +45 -29
  48. data/test/test_custom_format.rb +2 -3
  49. data/test/test_definition.rb +15 -0
  50. data/test/test_extended_schema.rb +25 -31
  51. data/test/test_extends_and_additionalProperties.rb +23 -21
  52. data/test/test_files_v3.rb +14 -23
  53. data/test/test_fragment_resolution.rb +6 -7
  54. data/test/test_fragment_validation_with_ref.rb +2 -8
  55. data/test/test_full_validation.rb +2 -3
  56. data/test/test_helper.rb +46 -1
  57. data/test/test_initialize_data.rb +118 -0
  58. data/test/test_jsonschema_draft1.rb +48 -600
  59. data/test/test_jsonschema_draft2.rb +48 -699
  60. data/test/test_jsonschema_draft3.rb +91 -861
  61. data/test/test_jsonschema_draft4.rb +173 -812
  62. data/test/test_list_option.rb +6 -7
  63. data/test/{test_merge_misisng_values.rb → test_merge_missing_values.rb} +2 -3
  64. data/test/test_minitems.rb +2 -4
  65. data/test/test_one_of.rb +9 -19
  66. data/test/test_ruby_schema.rb +5 -14
  67. data/test/test_schema_loader.rb +74 -0
  68. data/test/test_schema_type_attribute.rb +2 -3
  69. data/test/test_schema_validation.rb +4 -5
  70. data/test/test_stringify.rb +2 -3
  71. data/test/test_uri_related.rb +67 -0
  72. data/test/test_validator.rb +53 -0
  73. metadata +129 -51
  74. data/lib/json-schema/attributes/dependencies_v4.rb +0 -27
  75. data/lib/json-schema/attributes/formats/ip4.rb +0 -20
  76. data/lib/json-schema/attributes/formats/ip6.rb +0 -20
  77. data/lib/json-schema/attributes/maximum.rb +0 -17
  78. data/lib/json-schema/attributes/maximum_inclusive.rb +0 -17
  79. data/lib/json-schema/attributes/maxitems.rb +0 -14
  80. data/lib/json-schema/attributes/maxlength.rb +0 -16
  81. data/lib/json-schema/attributes/maxproperties.rb +0 -14
  82. data/lib/json-schema/attributes/minimum.rb +0 -17
  83. data/lib/json-schema/attributes/minimum_inclusive.rb +0 -17
  84. data/lib/json-schema/attributes/minitems.rb +0 -14
  85. data/lib/json-schema/attributes/minlength.rb +0 -16
  86. data/lib/json-schema/attributes/minproperties.rb +0 -14
  87. data/lib/json-schema/attributes/properties_v4.rb +0 -58
  88. data/lib/json-schema/uri/file.rb +0 -36
@@ -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
@@ -1,16 +1,18 @@
1
1
  require 'json-schema/attribute'
2
- require 'uri'
2
+ require 'addressable/uri'
3
3
  module JSON
4
4
  class Schema
5
5
  class UriFormat < FormatAttribute
6
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 valid URI"
9
- begin
10
- URI.parse(URI.escape(data))
11
- rescue URI::InvalidURIError
12
- validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
13
- end
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])
14
16
  end
15
17
  end
16
18
  end
@@ -4,22 +4,21 @@ module JSON
4
4
  class Schema
5
5
  class ItemsAttribute < Attribute
6
6
  def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
- if data.is_a?(Array)
8
- if current_schema.schema['items'].is_a?(Hash)
9
- data.each_with_index do |item,i|
10
- schema = JSON::Schema.new(current_schema.schema['items'],current_schema.uri,validator)
11
- fragments << i.to_s
12
- schema.validate(item,fragments, processor, options)
13
- fragments.pop
14
- end
15
- elsif current_schema.schema['items'].is_a?(Array)
16
- current_schema.schema['items'].each_with_index do |item_schema,i|
17
- schema = JSON::Schema.new(item_schema,current_schema.uri,validator)
18
- fragments << i.to_s
19
- schema.validate(data[i],fragments, processor, options)
20
- fragments.pop
21
- end
22
- end
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
23
22
  end
24
23
  end
25
24
  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
@@ -4,12 +4,13 @@ module JSON
4
4
  class Schema
5
5
  class MaxDecimalAttribute < Attribute
6
6
  def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
- if data.is_a?(Numeric)
8
- s = data.to_s.split(".")[1]
9
- if s && s.length > current_schema.schema['maxDecimal']
10
- message = "The property '#{build_fragment(fragments)}' had more decimal places than the allowed #{current_schema.schema['maxDecimal']}"
11
- validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
12
- end
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])
13
14
  end
14
15
  end
15
16
  end
@@ -1,17 +1,10 @@
1
- require 'json-schema/attribute'
1
+ require 'json-schema/attributes/divisibleby'
2
2
 
3
3
  module JSON
4
4
  class Schema
5
- class MultipleOfAttribute < Attribute
6
- def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
- if data.is_a?(Numeric)
8
- if current_schema.schema['multipleOf'] == 0 ||
9
- current_schema.schema['multipleOf'] == 0.0 ||
10
- (BigDecimal.new(data.to_s) % BigDecimal.new(current_schema.schema['multipleOf'].to_s)).to_f != 0
11
- message = "The property '#{build_fragment(fragments)}' was not divisible by #{current_schema.schema['multipleOf']}"
12
- validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
13
- end
14
- end
5
+ class MultipleOfAttribute < DivisibleByAttribute
6
+ def self.keyword
7
+ 'multipleOf'
15
8
  end
16
9
  end
17
10
  end
@@ -4,10 +4,10 @@ module JSON
4
4
  class Schema
5
5
  class NotAttribute < Attribute
6
6
  def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
-
8
7
  schema = JSON::Schema.new(current_schema.schema['not'],current_schema.uri,validator)
9
8
  failed = true
10
9
  errors_copy = processor.validation_errors.clone
10
+
11
11
  begin
12
12
  schema.validate(data,fragments,processor,options)
13
13
  # If we're recording errors, we don't throw an exception. Instead, check the errors array length
@@ -5,25 +5,34 @@ module JSON
5
5
  class OneOfAttribute < Attribute
6
6
  def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
7
  validation_errors = 0
8
- current_schema.schema['oneOf'].each do |element|
8
+ one_of = current_schema.schema['oneOf']
9
+
10
+ original_data = data.is_a?(Hash) ? data.clone : data
11
+ success_data = nil
12
+
13
+ one_of.each do |element|
9
14
  schema = JSON::Schema.new(element,current_schema.uri,validator)
10
15
 
11
16
  begin
12
17
  # need to raise exceptions on error because
13
18
  # schema.validate doesn't reliably return true/false
14
19
  schema.validate(data,fragments,processor,options.merge(:record_errors => false))
20
+ success_data = data.is_a?(Hash) ? data.clone : data
15
21
  rescue ValidationError
16
22
  validation_errors += 1
17
23
  end
18
24
 
25
+ data = original_data
26
+ end
27
+
28
+ if validation_errors == one_of.length - 1
29
+ data = success_data
30
+ return
19
31
  end
20
32
 
21
- case validation_errors
22
- when current_schema.schema['oneOf'].length - 1 # correct, matched only one
23
- message = nil
24
- when current_schema.schema['oneOf'].length # didn't match any
33
+ if validation_errors == one_of.length
25
34
  message = "The property '#{build_fragment(fragments)}' of type #{data.class} did not match any of the required schemas"
26
- else # too many matches
35
+ else
27
36
  message = "The property '#{build_fragment(fragments)}' of type #{data.class} matched more than one of the required schemas"
28
37
  end
29
38
 
@@ -4,12 +4,13 @@ module JSON
4
4
  class Schema
5
5
  class PatternAttribute < Attribute
6
6
  def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
- if data.is_a?(String)
8
- r = Regexp.new(current_schema.schema['pattern'])
9
- if (r.match(data)).nil?
10
- message = "The property '#{build_fragment(fragments)}' value #{data.inspect} did not match the regex '#{current_schema.schema['pattern']}'"
11
- validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
12
- end
7
+ return unless data.is_a?(String)
8
+
9
+ pattern = current_schema.schema['pattern']
10
+ regexp = Regexp.new(pattern)
11
+ unless regexp.match(data)
12
+ message = "The property '#{build_fragment(fragments)}' value #{data.inspect} did not match the regex '#{pattern}'"
13
+ validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
13
14
  end
14
15
  end
15
16
  end
@@ -4,19 +4,16 @@ module JSON
4
4
  class Schema
5
5
  class PatternPropertiesAttribute < Attribute
6
6
  def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
- if data.is_a?(Hash)
8
- current_schema.schema['patternProperties'].each do |property,property_schema|
9
- r = Regexp.new(property)
7
+ return unless data.is_a?(Hash)
10
8
 
11
- # Check each key in the data hash to see if it matches the regex
12
- data.each do |key,value|
13
- if r.match(key)
14
- schema = JSON::Schema.new(property_schema,current_schema.uri,validator)
15
- fragments << key
16
- schema.validate(data[key],fragments,processor,options)
17
- fragments.pop
18
- end
19
- end
9
+ current_schema.schema['patternProperties'].each do |property, property_schema|
10
+ regexp = Regexp.new(property)
11
+
12
+ # Check each key in the data hash to see if it matches the regex
13
+ data.each do |key, value|
14
+ next unless regexp.match(key)
15
+ schema = JSON::Schema.new(property_schema, current_schema.uri, validator)
16
+ schema.validate(data[key], fragments + [key], processor, options)
20
17
  end
21
18
  end
22
19
  end
@@ -3,55 +3,71 @@ require 'json-schema/attribute'
3
3
  module JSON
4
4
  class Schema
5
5
  class PropertiesAttribute < Attribute
6
+ def self.required?(schema, options)
7
+ schema.fetch('required') { options[:strict] }
8
+ end
9
+
6
10
  def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
- if data.is_a?(Hash)
8
- current_schema.schema['properties'].each do |property,property_schema|
9
- if !data.has_key?(property.to_s) &&
10
- property_schema['default'] &&
11
- !property_schema['readonly'] &&
12
- options[:insert_defaults]
13
- default = property_schema['default']
14
- data[property.to_s] = (default.is_a?(Hash) ? default.clone : default)
15
- end
11
+ return unless data.is_a?(Hash)
16
12
 
17
- if property_schema.fetch('required') { options[:strict] } && !data.has_key?(property.to_s)
18
- message = "The property '#{build_fragment(fragments)}' did not contain a required property of '#{property}'"
19
- validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
20
- end
13
+ schema = current_schema.schema
14
+ schema['properties'].each do |property, property_schema|
15
+ property = property.to_s
21
16
 
22
- if data.has_key?(property.to_s)
23
- schema = JSON::Schema.new(property_schema,current_schema.uri,validator)
24
- fragments << property.to_s
25
- schema.validate(data[property.to_s],fragments,processor,options)
26
- fragments.pop
27
- end
17
+ if !data.key?(property) &&
18
+ options[:insert_defaults] &&
19
+ property_schema['default'] &&
20
+ !property_schema['readonly']
21
+ default = property_schema['default']
22
+ data[property] = default.is_a?(Hash) ? default.clone : default
23
+ end
24
+
25
+ if required?(property_schema, options) && !data.has_key?(property)
26
+ message = "The property '#{build_fragment(fragments)}' did not contain a required property of '#{property}'"
27
+ validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
28
+ end
29
+
30
+ if data.has_key?(property)
31
+ expected_schema = JSON::Schema.new(property_schema, current_schema.uri, validator)
32
+ expected_schema.validate(data[property], fragments + [property], processor, options)
28
33
  end
34
+ end
35
+
36
+ # When strict is true, ensure no undefined properties exist in the data
37
+ return unless options[:strict] == true && !schema.key?('additionalProperties')
38
+
39
+ diff = data.select do |k, v|
40
+ k = k.to_s
29
41
 
30
- # When strict is true, ensure no undefined properties exist in the data
31
- if (options[:strict] == true && !current_schema.schema.has_key?('additionalProperties'))
32
- diff = data.select do |k,v|
33
- if current_schema.schema.has_key?('patternProperties')
34
- match = false
35
- current_schema.schema['patternProperties'].each do |property,property_schema|
36
- r = Regexp.new(property)
37
- if r.match(k)
38
- match = true
39
- break
40
- end
41
- end
42
-
43
- !current_schema.schema['properties'].has_key?(k.to_s) && !match
44
- else
45
- !current_schema.schema['properties'].has_key?(k.to_s)
42
+ if schema.has_key?('patternProperties')
43
+ match = false
44
+ schema['patternProperties'].each do |property, property_schema|
45
+ regexp = Regexp.new(property)
46
+ if regexp.match(k)
47
+ match = true
48
+ break
46
49
  end
47
50
  end
48
51
 
49
- if diff.size > 0
50
- message = "The property '#{build_fragment(fragments)}' contained undefined properties: '#{diff.keys.join(", ")}'"
51
- validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
52
- end
52
+ !schema['properties'].has_key?(k) && !match
53
+ else
54
+ !schema['properties'].has_key?(k)
53
55
  end
54
56
  end
57
+
58
+ if diff.size > 0
59
+ properties = data.to_a.map { |(key, _)| key }.join(', ')
60
+ message = "The property '#{build_fragment(fragments)}' contained undefined properties: '#{properties}"
61
+ validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
62
+ end
63
+ end
64
+ end
65
+
66
+ class PropertiesV4Attribute < PropertiesAttribute
67
+ # draft4 relies on its own RequiredAttribute validation at a higher level, rather than
68
+ # as an attribute of individual properties.
69
+ def self.required?(schema, options)
70
+ options[:strict] == true
55
71
  end
56
72
  end
57
73
  end