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