schemacop 2.4.7 → 3.0.0.rc4

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 (173) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.releaser_config +0 -1
  4. data/.rubocop.yml +25 -1
  5. data/.travis.yml +3 -1
  6. data/CHANGELOG.md +37 -0
  7. data/README.md +53 -710
  8. data/README_V2.md +775 -0
  9. data/README_V3.md +1253 -0
  10. data/Rakefile +8 -12
  11. data/VERSION +1 -1
  12. data/lib/schemacop.rb +35 -37
  13. data/lib/schemacop/base_schema.rb +37 -0
  14. data/lib/schemacop/railtie.rb +10 -0
  15. data/lib/schemacop/schema.rb +1 -60
  16. data/lib/schemacop/schema2.rb +22 -0
  17. data/lib/schemacop/schema3.rb +21 -0
  18. data/lib/schemacop/scoped_env.rb +25 -13
  19. data/lib/schemacop/v2.rb +25 -0
  20. data/lib/schemacop/{caster.rb → v2/caster.rb} +17 -2
  21. data/lib/schemacop/{collector.rb → v2/collector.rb} +5 -2
  22. data/lib/schemacop/{dupper.rb → v2/dupper.rb} +1 -1
  23. data/lib/schemacop/{field_node.rb → v2/field_node.rb} +4 -3
  24. data/lib/schemacop/v2/node.rb +142 -0
  25. data/lib/schemacop/{node_resolver.rb → v2/node_resolver.rb} +1 -1
  26. data/lib/schemacop/v2/node_supporting_field.rb +70 -0
  27. data/lib/schemacop/{node_supporting_type.rb → v2/node_supporting_type.rb} +8 -5
  28. data/lib/schemacop/{node_with_block.rb → v2/node_with_block.rb} +3 -2
  29. data/lib/schemacop/v2/validator/array_validator.rb +32 -0
  30. data/lib/schemacop/{validator → v2/validator}/boolean_validator.rb +1 -1
  31. data/lib/schemacop/v2/validator/float_validator.rb +7 -0
  32. data/lib/schemacop/v2/validator/hash_validator.rb +37 -0
  33. data/lib/schemacop/v2/validator/integer_validator.rb +7 -0
  34. data/lib/schemacop/{validator → v2/validator}/nil_validator.rb +1 -1
  35. data/lib/schemacop/v2/validator/number_validator.rb +21 -0
  36. data/lib/schemacop/v2/validator/object_validator.rb +29 -0
  37. data/lib/schemacop/v2/validator/string_validator.rb +39 -0
  38. data/lib/schemacop/{validator → v2/validator}/symbol_validator.rb +1 -1
  39. data/lib/schemacop/v3.rb +45 -0
  40. data/lib/schemacop/v3/all_of_node.rb +27 -0
  41. data/lib/schemacop/v3/any_of_node.rb +28 -0
  42. data/lib/schemacop/v3/array_node.rb +218 -0
  43. data/lib/schemacop/v3/boolean_node.rb +16 -0
  44. data/lib/schemacop/v3/combination_node.rb +45 -0
  45. data/lib/schemacop/v3/context.rb +17 -0
  46. data/lib/schemacop/v3/dsl_scope.rb +46 -0
  47. data/lib/schemacop/v3/global_context.rb +114 -0
  48. data/lib/schemacop/v3/hash_node.rb +267 -0
  49. data/lib/schemacop/v3/integer_node.rb +13 -0
  50. data/lib/schemacop/v3/is_not_node.rb +32 -0
  51. data/lib/schemacop/v3/node.rb +219 -0
  52. data/lib/schemacop/v3/node_registry.rb +45 -0
  53. data/lib/schemacop/v3/number_node.rb +18 -0
  54. data/lib/schemacop/v3/numeric_node.rb +76 -0
  55. data/lib/schemacop/v3/object_node.rb +40 -0
  56. data/lib/schemacop/v3/one_of_node.rb +28 -0
  57. data/lib/schemacop/v3/reference_node.rb +55 -0
  58. data/lib/schemacop/v3/result.rb +58 -0
  59. data/lib/schemacop/v3/string_node.rb +132 -0
  60. data/lib/schemacop/v3/symbol_node.rb +13 -0
  61. data/schemacop.gemspec +24 -27
  62. data/test/lib/test_helper.rb +167 -0
  63. data/test/schemas/nested/group.rb +6 -0
  64. data/test/schemas/user.rb +7 -0
  65. data/test/unit/schemacop/v2/casting_test.rb +157 -0
  66. data/test/unit/schemacop/v2/collector_test.rb +47 -0
  67. data/test/unit/schemacop/v2/custom_check_test.rb +95 -0
  68. data/test/unit/schemacop/v2/custom_if_test.rb +97 -0
  69. data/test/unit/schemacop/v2/defaults_test.rb +95 -0
  70. data/test/unit/schemacop/v2/empty_test.rb +16 -0
  71. data/test/unit/schemacop/v2/nil_dis_allow_test.rb +43 -0
  72. data/test/unit/schemacop/v2/node_resolver_test.rb +28 -0
  73. data/test/unit/schemacop/v2/short_forms_test.rb +351 -0
  74. data/test/unit/schemacop/v2/types_test.rb +88 -0
  75. data/test/unit/schemacop/v2/validator_array_test.rb +99 -0
  76. data/test/unit/schemacop/v2/validator_boolean_test.rb +17 -0
  77. data/test/unit/schemacop/v2/validator_float_test.rb +59 -0
  78. data/test/unit/schemacop/v2/validator_hash_test.rb +106 -0
  79. data/test/unit/schemacop/v2/validator_integer_test.rb +48 -0
  80. data/test/unit/schemacop/v2/validator_nil_test.rb +15 -0
  81. data/test/unit/schemacop/v2/validator_number_test.rb +62 -0
  82. data/test/unit/schemacop/v2/validator_object_test.rb +141 -0
  83. data/test/unit/schemacop/v2/validator_string_test.rb +78 -0
  84. data/test/unit/schemacop/v2/validator_symbol_test.rb +18 -0
  85. data/test/unit/schemacop/v3/all_of_node_test.rb +198 -0
  86. data/test/unit/schemacop/v3/any_of_node_test.rb +218 -0
  87. data/test/unit/schemacop/v3/array_node_test.rb +815 -0
  88. data/test/unit/schemacop/v3/boolean_node_test.rb +126 -0
  89. data/test/unit/schemacop/v3/global_context_test.rb +166 -0
  90. data/test/unit/schemacop/v3/hash_node_test.rb +972 -0
  91. data/test/unit/schemacop/v3/integer_node_test.rb +323 -0
  92. data/test/unit/schemacop/v3/is_not_node_test.rb +173 -0
  93. data/test/unit/schemacop/v3/node_test.rb +162 -0
  94. data/test/unit/schemacop/v3/number_node_test.rb +292 -0
  95. data/test/unit/schemacop/v3/object_node_test.rb +170 -0
  96. data/test/unit/schemacop/v3/one_of_node_test.rb +187 -0
  97. data/test/unit/schemacop/v3/reference_node_test.rb +367 -0
  98. data/test/unit/schemacop/v3/string_node_test.rb +372 -0
  99. data/test/unit/schemacop/v3/symbol_node_test.rb +75 -0
  100. metadata +151 -145
  101. data/doc/Schemacop.html +0 -146
  102. data/doc/Schemacop/ArrayValidator.html +0 -329
  103. data/doc/Schemacop/BooleanValidator.html +0 -145
  104. data/doc/Schemacop/Caster.html +0 -379
  105. data/doc/Schemacop/Collector.html +0 -787
  106. data/doc/Schemacop/Dupper.html +0 -214
  107. data/doc/Schemacop/Exceptions.html +0 -115
  108. data/doc/Schemacop/Exceptions/InvalidSchemaError.html +0 -124
  109. data/doc/Schemacop/Exceptions/ValidationError.html +0 -124
  110. data/doc/Schemacop/FieldNode.html +0 -421
  111. data/doc/Schemacop/FloatValidator.html +0 -158
  112. data/doc/Schemacop/HashValidator.html +0 -293
  113. data/doc/Schemacop/IntegerValidator.html +0 -158
  114. data/doc/Schemacop/NilValidator.html +0 -145
  115. data/doc/Schemacop/Node.html +0 -1438
  116. data/doc/Schemacop/NodeResolver.html +0 -258
  117. data/doc/Schemacop/NodeSupportingField.html +0 -590
  118. data/doc/Schemacop/NodeSupportingType.html +0 -612
  119. data/doc/Schemacop/NodeWithBlock.html +0 -289
  120. data/doc/Schemacop/NumberValidator.html +0 -232
  121. data/doc/Schemacop/ObjectValidator.html +0 -298
  122. data/doc/Schemacop/RootNode.html +0 -171
  123. data/doc/Schemacop/Schema.html +0 -699
  124. data/doc/Schemacop/StringValidator.html +0 -295
  125. data/doc/Schemacop/SymbolValidator.html +0 -145
  126. data/doc/ScopedEnv.html +0 -351
  127. data/doc/_index.html +0 -379
  128. data/doc/class_list.html +0 -51
  129. data/doc/css/common.css +0 -1
  130. data/doc/css/full_list.css +0 -58
  131. data/doc/css/style.css +0 -496
  132. data/doc/file.README.html +0 -833
  133. data/doc/file_list.html +0 -56
  134. data/doc/frames.html +0 -17
  135. data/doc/index.html +0 -833
  136. data/doc/inheritance.graphml +0 -524
  137. data/doc/inheritance.pdf +0 -825
  138. data/doc/js/app.js +0 -303
  139. data/doc/js/full_list.js +0 -216
  140. data/doc/js/jquery.js +0 -4
  141. data/doc/method_list.html +0 -587
  142. data/doc/top-level-namespace.html +0 -112
  143. data/lib/schemacop/node.rb +0 -139
  144. data/lib/schemacop/node_supporting_field.rb +0 -58
  145. data/lib/schemacop/root_node.rb +0 -4
  146. data/lib/schemacop/validator/array_validator.rb +0 -30
  147. data/lib/schemacop/validator/float_validator.rb +0 -5
  148. data/lib/schemacop/validator/hash_validator.rb +0 -35
  149. data/lib/schemacop/validator/integer_validator.rb +0 -5
  150. data/lib/schemacop/validator/number_validator.rb +0 -19
  151. data/lib/schemacop/validator/object_validator.rb +0 -27
  152. data/lib/schemacop/validator/string_validator.rb +0 -37
  153. data/test/casting_test.rb +0 -118
  154. data/test/collector_test.rb +0 -45
  155. data/test/custom_check_test.rb +0 -93
  156. data/test/custom_if_test.rb +0 -95
  157. data/test/defaults_test.rb +0 -93
  158. data/test/empty_test.rb +0 -14
  159. data/test/nil_dis_allow_test.rb +0 -41
  160. data/test/node_resolver_test.rb +0 -26
  161. data/test/short_forms_test.rb +0 -349
  162. data/test/test_helper.rb +0 -13
  163. data/test/types_test.rb +0 -84
  164. data/test/validator_array_test.rb +0 -97
  165. data/test/validator_boolean_test.rb +0 -15
  166. data/test/validator_float_test.rb +0 -57
  167. data/test/validator_hash_test.rb +0 -93
  168. data/test/validator_integer_test.rb +0 -46
  169. data/test/validator_nil_test.rb +0 -13
  170. data/test/validator_number_test.rb +0 -60
  171. data/test/validator_object_test.rb +0 -139
  172. data/test/validator_string_test.rb +0 -76
  173. data/test/validator_symbol_test.rb +0 -16
@@ -0,0 +1,46 @@
1
+ module Schemacop
2
+ module V3
3
+ class DslScope
4
+ EXP_NAME = /^dsl_([a-z_]+)([?!])?$/.freeze
5
+
6
+ def initialize(node)
7
+ @node = node
8
+ @with_name = @node.class.supports_children_options[:name]
9
+ end
10
+
11
+ def method_missing(name, *args, **options, &block)
12
+ match = EXP_NAME.match(name)
13
+ return super unless match
14
+
15
+ base_name, req_optional = match.captures
16
+
17
+ if req_optional == '!'
18
+ options[:required] = true
19
+ elsif req_optional == '?'
20
+ options[:required] = false
21
+ end
22
+
23
+ options[:parent] = @node
24
+
25
+ if (klass = NodeRegistry.by_short_name(base_name))
26
+ if @with_name
27
+ options[:name] = args.shift
28
+ end
29
+ node = klass.create(*args, **options, &block)
30
+ @node.add_child node
31
+ return node
32
+ else
33
+ return super
34
+ end
35
+ end
36
+
37
+ def respond_to_missing?(name, *args)
38
+ match = EXP_NAME.match(name)
39
+ return super unless match
40
+
41
+ base_name, _req_optional = match.captures
42
+ return NodeRegistry.by_short_name(base_name).present? || super
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,114 @@
1
+ module Schemacop
2
+ module V3
3
+ class GlobalContext < Context
4
+ DSL_METHODS = %i[schema].freeze
5
+
6
+ def self.instance
7
+ @instance ||= new
8
+ end
9
+
10
+ def self.eager_load!
11
+ instance.eager_load!
12
+ end
13
+
14
+ def self.schemas
15
+ instance.schemas
16
+ end
17
+
18
+ def self.schema_for(path)
19
+ instance.schema_for(path)
20
+ end
21
+
22
+ def schema(type = :hash, **options, &block)
23
+ @current_schemas << Node.create(type, **options, &block)
24
+ end
25
+
26
+ def schema_for(path)
27
+ path = path.to_sym
28
+ load_schema(path) unless @eager_loaded
29
+ @schemas[path]
30
+ end
31
+
32
+ def eager_load!
33
+ @schemas = {}
34
+
35
+ fail "Global context can't be eager loaded more than once." if @eager_loaded
36
+
37
+ Schemacop.load_paths.each do |load_path|
38
+ Dir.glob(File.join(load_path, '**', '*.rb')).sort.each do |file|
39
+ load_file(file, load_path)
40
+ end
41
+ end
42
+
43
+ @eager_loaded = true
44
+ end
45
+
46
+ private
47
+
48
+ def initialize
49
+ super
50
+ @schemas = {}
51
+ @load_paths_by_schemas = {}
52
+ @eager_loaded = false
53
+ @current_virtual_path = nil
54
+ end
55
+
56
+ def path_for(virtual_path)
57
+ "#{virtual_path.to_s.underscore}.rb"
58
+ end
59
+
60
+ def virtual_path_for(path, load_path)
61
+ Pathname.new(path).relative_path_from(load_path).to_s.underscore.gsub(/\.rb$/, '').to_sym
62
+ end
63
+
64
+ def load_schema(virtual_path)
65
+ path = path_for(virtual_path)
66
+
67
+ @schemas = schemas.except(virtual_path).freeze
68
+ @load_paths_by_schemas = @load_paths_by_schemas.except(virtual_path)
69
+
70
+ Schemacop.load_paths.each do |load_path|
71
+ path_in_load_path = File.join(load_path, path)
72
+
73
+ if File.exist?(path_in_load_path)
74
+ load_file(path_in_load_path, load_path)
75
+ end
76
+ end
77
+ end
78
+
79
+ def load_file(path, load_path)
80
+ return false unless File.exist?(path)
81
+
82
+ # Determine virtual path
83
+ virtual_path = virtual_path_for(path, load_path)
84
+
85
+ # Run file and collect schemas
86
+ begin
87
+ @current_schemas = []
88
+ env = ScopedEnv.new(self, DSL_METHODS)
89
+ env.instance_eval IO.read(path)
90
+ rescue StandardError => e
91
+ fail "Could not load schema #{path.inspect}: #{e.message}"
92
+ end
93
+
94
+ # Load schemas
95
+ case @current_schemas.size
96
+ when 0
97
+ fail "Schema #{path.inspect} does not define any schema."
98
+ when 1
99
+ if @schemas.include?(virtual_path)
100
+ fail "Schema #{virtual_path.to_s.inspect} is defined in both load paths "\
101
+ "#{@load_paths_by_schemas[virtual_path].inspect} and #{load_path.inspect}."
102
+ end
103
+
104
+ @load_paths_by_schemas[virtual_path] = load_path
105
+ @schemas = @schemas.merge(virtual_path => @current_schemas.first)
106
+ else
107
+ fail "Schema #{path.inspect} defines multiple schemas."
108
+ end
109
+
110
+ return true
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,267 @@
1
+ module Schemacop
2
+ module V3
3
+ class HashNode < Node
4
+ ATTRIBUTES = %i[
5
+ type
6
+ min_properties
7
+ max_properties
8
+ dependencies
9
+ property_names
10
+ ].freeze
11
+
12
+ supports_children(name: true)
13
+
14
+ attr_reader :properties
15
+
16
+ def self.allowed_options
17
+ super + ATTRIBUTES - %i[dependencies] + %i[additional_properties]
18
+ end
19
+
20
+ def self.dsl_methods
21
+ super + NodeRegistry.dsl_methods(true) + %i[dsl_dep dsl_add]
22
+ end
23
+
24
+ def self.sanitize_exp(exp)
25
+ exp = exp.to_s
26
+ if exp.start_with?('(?-mix:')
27
+ exp = exp.to_s.gsub(/^\(\?-mix:/, '').gsub(/\)$/, '')
28
+ end
29
+ return exp
30
+ end
31
+
32
+ def add_child(node)
33
+ unless node.name
34
+ fail Exceptions::InvalidSchemaError, 'Child nodes must have a name.'
35
+ end
36
+
37
+ @properties[node.name] = node
38
+ end
39
+
40
+ def dsl_add(type, **options, &block)
41
+ if @options[:additional_properties].is_a?(Node)
42
+ fail Exceptions::InvalidSchemaError, 'You can only use "add" once to specify additional properties.'
43
+ end
44
+
45
+ @options[:additional_properties] = create(type, **options, &block)
46
+ end
47
+
48
+ def dsl_dep(source, *targets, **_kwargs)
49
+ @options[:dependencies] ||= {}
50
+ @options[:dependencies][source] = targets
51
+ end
52
+
53
+ def as_json
54
+ properties = {}
55
+ pattern_properties = {}
56
+
57
+ @properties.each do |name, property|
58
+ if name.is_a?(Regexp)
59
+ pattern_properties[name] = property
60
+ else
61
+ properties[name] = property
62
+ end
63
+ end
64
+
65
+ json = {}
66
+ json[:properties] = Hash[properties.values.map { |p| [p.name, p.as_json] }] if properties.any?
67
+ json[:patternProperties] = Hash[pattern_properties.values.map { |p| [self.class.sanitize_exp(p.name), p.as_json] }] if pattern_properties.any?
68
+
69
+ # In schemacop, by default, additional properties are not allowed,
70
+ # the users explicitly need to enable additional properties
71
+ if options[:additional_properties].is_a?(TrueClass)
72
+ json[:additionalProperties] = true
73
+ elsif options[:additional_properties].is_a?(Node)
74
+ json[:additionalProperties] = options[:additional_properties].as_json
75
+ else
76
+ json[:additionalProperties] = false
77
+ end
78
+
79
+ required_properties = @properties.values.select(&:required?).map(&:name)
80
+
81
+ if required_properties.any?
82
+ json[:required] = required_properties
83
+ end
84
+
85
+ return process_json(ATTRIBUTES, json)
86
+ end
87
+
88
+ def allowed_types
89
+ { Hash => :object }
90
+ end
91
+
92
+ def _validate(data, result: Result.new)
93
+ super_data = super
94
+ return if super_data.nil?
95
+
96
+ original_data_hash = super_data.dup
97
+ data_hash = super_data.with_indifferent_access
98
+
99
+ if original_data_hash.size != data_hash.size
100
+ ambiguous_properties = original_data_hash.keys - data_hash.keys
101
+
102
+ result.error "Has #{ambiguous_properties.size} ambiguous properties: #{ambiguous_properties}."
103
+ end
104
+
105
+ # Validate min_properties #
106
+ if options[:min_properties] && data_hash.size < options[:min_properties]
107
+ result.error "Has #{data_hash.size} properties but needs at least #{options[:min_properties]}."
108
+ end
109
+
110
+ # Validate max_properties #
111
+ if options[:max_properties] && data_hash.size > options[:max_properties]
112
+ result.error "Has #{data_hash.size} properties but needs at most #{options[:max_properties]}."
113
+ end
114
+
115
+ # Validate specified properties #
116
+ @properties.each_value do |node|
117
+ result.in_path(node.name) do
118
+ next if node.name.is_a?(Regexp)
119
+
120
+ node._validate(data_hash[node.name], result: result)
121
+ end
122
+ end
123
+
124
+ # Validate additional properties #
125
+ specified_properties = @properties.keys.to_set
126
+ additional_properties = data_hash.reject { |k, _v| specified_properties.include?(k.to_s) }
127
+
128
+ property_patterns = {}
129
+
130
+ @properties.each_value do |property|
131
+ if property.name.is_a?(Regexp)
132
+ property_patterns[property.name] = property
133
+ end
134
+ end
135
+
136
+ property_names = options[:property_names]
137
+ property_names = Regexp.compile(property_names) if property_names
138
+
139
+ additional_properties.each do |name, additional_property|
140
+ if property_names && !property_names.match?(name)
141
+ result.error "Property name #{name.inspect} does not match #{options[:property_names].inspect}."
142
+ end
143
+
144
+ if options[:additional_properties].is_a?(TrueClass)
145
+ next
146
+ elsif options[:additional_properties].is_a?(FalseClass) || options[:additional_properties].blank?
147
+ match = property_patterns.keys.find { |p| p.match?(name.to_s) }
148
+ if match
149
+ result.in_path(name) do
150
+ property_patterns[match]._validate(additional_property, result: result)
151
+ end
152
+ else
153
+ result.error "Obsolete property #{name.to_s.inspect}."
154
+ end
155
+ elsif options[:additional_properties].is_a?(Node)
156
+ result.in_path(name) do
157
+ options[:additional_properties]._validate(additional_property, result: result)
158
+ end
159
+ end
160
+ end
161
+
162
+ # Validate dependencies #
163
+ options[:dependencies]&.each do |source, targets|
164
+ targets.each do |target|
165
+ if data_hash[source].present? && data_hash[target].blank?
166
+ result.error "Missing property #{target.to_s.inspect} because #{source.to_s.inspect} is given."
167
+ end
168
+ end
169
+ end
170
+ end
171
+
172
+ def children
173
+ @properties.values
174
+ end
175
+
176
+ def cast(data)
177
+ result = {}.with_indifferent_access
178
+ data ||= default
179
+ return nil if data.nil?
180
+
181
+ data_hash = data.dup.with_indifferent_access
182
+
183
+ property_patterns = {}
184
+ as_names = []
185
+
186
+ @properties.each_value do |prop|
187
+ if prop.name.is_a?(Regexp)
188
+ property_patterns[prop.name] = prop
189
+ next
190
+ end
191
+
192
+ as_names << prop.as&.to_s if prop.as.present?
193
+
194
+ prop_name = prop.as&.to_s || prop.name
195
+
196
+ casted_data = prop.cast(data_hash[prop.name])
197
+
198
+ if casted_data.present? || data_hash.include?(prop.name)
199
+ result[prop_name] = casted_data
200
+ end
201
+
202
+ if result[prop_name].nil? && !data_hash.include?(prop.name) && !as_names.include?(prop.name)
203
+ result.delete(prop_name)
204
+ end
205
+ end
206
+
207
+ # Handle regex properties
208
+ specified_properties = @properties.keys.to_set
209
+ additional_properties = data_hash.reject { |k, _v| specified_properties.include?(k.to_s.to_sym) }
210
+
211
+ if additional_properties.any? && property_patterns.any?
212
+ additional_properties.each do |name, additional_property|
213
+ match_key = property_patterns.keys.find { |p| p.match?(name.to_s) }
214
+ match = property_patterns[match_key]
215
+ result[name] = match.cast(additional_property)
216
+ end
217
+ end
218
+
219
+ # Handle additional properties
220
+ if options[:additional_properties].is_a?(TrueClass)
221
+ result = data_hash.merge(result)
222
+ elsif options[:additional_properties].is_a?(Node)
223
+ specified_properties = @properties.keys.to_set
224
+ additional_properties = data_hash.reject { |k, _v| specified_properties.include?(k.to_s.to_sym) }
225
+ if additional_properties.any?
226
+ additional_properties_result = {}
227
+ additional_properties.each do |key, value|
228
+ additional_properties_result[key] = options[:additional_properties].cast(value)
229
+ end
230
+ result = additional_properties_result.merge(result)
231
+ end
232
+ end
233
+
234
+ return result
235
+ end
236
+
237
+ protected
238
+
239
+ def init
240
+ @properties = {}
241
+ @options[:type] = :object
242
+ unless @options[:additional_properties].nil? || @options[:additional_properties].is_a?(TrueClass) || @options[:additional_properties].is_a?(FalseClass)
243
+ fail Schemacop::Exceptions::InvalidSchemaError, 'Option "additional_properties" must be a boolean value'
244
+ end
245
+
246
+ # Default the additional_properties option to false if it's not given
247
+ if @options[:additional_properties].nil?
248
+ @options[:additional_properties] = false
249
+ end
250
+ end
251
+
252
+ def validate_self
253
+ unless options[:min_properties].nil? || options[:min_properties].is_a?(Integer)
254
+ fail 'Option "min_properties" must be an "integer"'
255
+ end
256
+
257
+ unless options[:max_properties].nil? || options[:max_properties].is_a?(Integer)
258
+ fail 'Option "max_properties" must be an "integer"'
259
+ end
260
+
261
+ if @properties.values.any? { |p| p.name.is_a?(Regexp) && p.required? }
262
+ fail 'Pattern properties can\'t be required.'
263
+ end
264
+ end
265
+ end
266
+ end
267
+ end
@@ -0,0 +1,13 @@
1
+ module Schemacop
2
+ module V3
3
+ class IntegerNode < NumericNode
4
+ def as_json
5
+ process_json(ATTRIBUTES, type: :integer)
6
+ end
7
+
8
+ def allowed_types
9
+ { Integer => :integer }
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,32 @@
1
+ module Schemacop
2
+ module V3
3
+ class IsNotNode < CombinationNode
4
+ def type
5
+ :not
6
+ end
7
+
8
+ def _validate(data, result:)
9
+ super_data = super
10
+ return if super_data.nil?
11
+
12
+ if matches(super_data).any?
13
+ result.error "Must not match schema: #{@items.first.as_json.as_json.inspect}."
14
+ end
15
+ end
16
+
17
+ def as_json
18
+ process_json([], type => @items.first.as_json)
19
+ end
20
+
21
+ def validate_self
22
+ if @items.count != 1
23
+ fail 'Node "is_not" only allows exactly one item.'
24
+ end
25
+ end
26
+
27
+ def cast(data)
28
+ data
29
+ end
30
+ end
31
+ end
32
+ end