schemacop 2.4.7 → 3.0.0.rc0
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 +2 -1
- data/CHANGELOG.md +8 -0
- data/README.md +41 -708
- data/README_V2.md +775 -0
- data/README_V3.md +683 -0
- data/Rakefile +8 -12
- data/VERSION +1 -1
- data/lib/schemacop.rb +35 -37
- 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/{node_supporting_field.rb → v2/node_supporting_field.rb} +8 -10
- data/lib/schemacop/{node_supporting_type.rb → v2/node_supporting_type.rb} +6 -3
- 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 +219 -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 +217 -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 +214 -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 +199 -0
- data/test/unit/schemacop/v3/any_of_node_test.rb +218 -0
- data/test/unit/schemacop/v3/array_node_test.rb +805 -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 +775 -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/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 -118
- 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,49 @@
|
|
1
|
+
module Schemacop
|
2
|
+
module V3
|
3
|
+
class NodeRegistry
|
4
|
+
@by_name = {}
|
5
|
+
@by_short_name = {}
|
6
|
+
@by_class = {}
|
7
|
+
|
8
|
+
def self.register(name, short_name, klass)
|
9
|
+
@by_name[name.to_sym] = klass
|
10
|
+
@by_short_name[short_name.to_sym] = klass
|
11
|
+
@by_class[klass] = { name: name.to_sym, short_name: short_name.to_sym }
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.dsl_methods(bang)
|
15
|
+
return @by_short_name.keys.map do |short_name|
|
16
|
+
if bang
|
17
|
+
["dsl_#{short_name}!", "dsl_#{short_name}?"]
|
18
|
+
else
|
19
|
+
["dsl_#{short_name}"]
|
20
|
+
end
|
21
|
+
end.flatten.map(&:to_sym)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.find(name_or_klass)
|
25
|
+
if name_or_klass.is_a?(Class)
|
26
|
+
return name_or_klass
|
27
|
+
else
|
28
|
+
return by_name(name_or_klass)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.by_name(name)
|
33
|
+
@by_name[name.to_sym]
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.by_short_name(short_name)
|
37
|
+
@by_short_name[short_name.to_sym]
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.name(klass)
|
41
|
+
@by_class[klass][:name]
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.short_name(klass)
|
45
|
+
@by_class[klass][:short_name]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Schemacop
|
2
|
+
module V3
|
3
|
+
class NumberNode < NumericNode
|
4
|
+
def as_json
|
5
|
+
process_json(ATTRIBUTES, type: :number)
|
6
|
+
end
|
7
|
+
|
8
|
+
def allowed_types
|
9
|
+
{
|
10
|
+
Integer => :integer,
|
11
|
+
Float => :float,
|
12
|
+
Rational => :rational,
|
13
|
+
BigDecimal => :big_decimal
|
14
|
+
}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Schemacop
|
2
|
+
module V3
|
3
|
+
# @abstract
|
4
|
+
class NumericNode < Node
|
5
|
+
ATTRIBUTES = %i[
|
6
|
+
minimum
|
7
|
+
exclusive_minimum
|
8
|
+
maximum
|
9
|
+
exclusive_maximum
|
10
|
+
multiple_of
|
11
|
+
].freeze
|
12
|
+
|
13
|
+
def self.allowed_options
|
14
|
+
super + ATTRIBUTES
|
15
|
+
end
|
16
|
+
|
17
|
+
def _validate(data, result:)
|
18
|
+
super_data = super
|
19
|
+
return if super_data.nil?
|
20
|
+
|
21
|
+
# Validate minimum #
|
22
|
+
if options[:minimum] && super_data < options[:minimum]
|
23
|
+
result.error "Value must have a minimum of #{options[:minimum]}."
|
24
|
+
end
|
25
|
+
|
26
|
+
if options[:exclusive_minimum] && super_data <= options[:exclusive_minimum]
|
27
|
+
result.error "Value must have an exclusive minimum of #{options[:exclusive_minimum]}."
|
28
|
+
end
|
29
|
+
|
30
|
+
# Validate maximum #
|
31
|
+
if options[:maximum] && super_data > options[:maximum]
|
32
|
+
result.error "Value must have a maximum of #{options[:maximum]}."
|
33
|
+
end
|
34
|
+
|
35
|
+
if options[:exclusive_maximum] && super_data >= options[:exclusive_maximum]
|
36
|
+
result.error "Value must have an exclusive maximum of #{options[:exclusive_maximum]}."
|
37
|
+
end
|
38
|
+
|
39
|
+
# Validate multiple of #
|
40
|
+
if options[:multiple_of] && !compare_float((super_data % options[:multiple_of]), 0.0)
|
41
|
+
result.error "Value must be a multiple of #{options[:multiple_of]}."
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def validate_self
|
46
|
+
# Check that the options have the correct type
|
47
|
+
ATTRIBUTES.each do |attribute|
|
48
|
+
next if options[attribute].nil?
|
49
|
+
next unless allowed_types.keys.none? { |c| options[attribute].send(type_assertion_method, c) }
|
50
|
+
|
51
|
+
collection = allowed_types.values.map { |t| "\"#{t}\"" }.uniq.sort.join(' or ')
|
52
|
+
fail "Option \"#{attribute}\" must be a #{collection}"
|
53
|
+
end
|
54
|
+
|
55
|
+
if options[:minimum] && options[:maximum] && options[:minimum] > options[:maximum]
|
56
|
+
fail 'Option "minimum" can\'t be greater than "maximum".'
|
57
|
+
end
|
58
|
+
|
59
|
+
if options[:exclusive_minimum] && options[:exclusive_maximum]\
|
60
|
+
&& options[:exclusive_minimum] > options[:exclusive_maximum]
|
61
|
+
fail 'Option "exclusive_minimum" can\'t be greater than "exclusive_maximum".'
|
62
|
+
end
|
63
|
+
|
64
|
+
if options[:multiple_of]&.zero?
|
65
|
+
fail 'Option "multiple_of" can\'t be 0.'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def compare_float(first, second)
|
72
|
+
(first - second).abs < Float::EPSILON
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Schemacop
|
2
|
+
module V3
|
3
|
+
class ObjectNode < Node
|
4
|
+
def self.allowed_options
|
5
|
+
super + %i[classes strict]
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.create(classes, **options, &block)
|
9
|
+
options[:classes] = classes
|
10
|
+
super(**options, &block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def as_json
|
14
|
+
{} # Not supported by Json Schema
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def allowed_types
|
20
|
+
Hash[@classes.map { |c| [c, c.name] }]
|
21
|
+
end
|
22
|
+
|
23
|
+
def init
|
24
|
+
@classes = Array(options.delete(:classes) || [])
|
25
|
+
@strict = options.delete(:strict)
|
26
|
+
@strict = true if @strict.nil?
|
27
|
+
end
|
28
|
+
|
29
|
+
def type_assertion_method
|
30
|
+
@strict ? :instance_of? : :is_a?
|
31
|
+
end
|
32
|
+
|
33
|
+
def validate_self
|
34
|
+
unless @strict.is_a?(TrueClass) || @strict.is_a?(FalseClass)
|
35
|
+
fail 'Option "strict" must be a "boolean".'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Schemacop
|
2
|
+
module V3
|
3
|
+
class OneOfNode < CombinationNode
|
4
|
+
def type
|
5
|
+
:oneOf
|
6
|
+
end
|
7
|
+
|
8
|
+
def _validate(data, result:)
|
9
|
+
super_data = super
|
10
|
+
return if super_data.nil?
|
11
|
+
|
12
|
+
matches = matches(super_data)
|
13
|
+
|
14
|
+
if matches.size == 1
|
15
|
+
matches.first._validate(super_data, result: result)
|
16
|
+
else
|
17
|
+
result.error "Matches #{matches.size} definitions but should match exactly 1."
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def validate_self
|
22
|
+
if @items.size < 2
|
23
|
+
fail 'Node "one_of" makes only sense with at least 2 items.'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Schemacop
|
2
|
+
module V3
|
3
|
+
class ReferenceNode < Node
|
4
|
+
def self.allowed_options
|
5
|
+
super + %i[path]
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.create(path, **options, &block)
|
9
|
+
options[:path] = path
|
10
|
+
super(**options, &block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def as_json
|
14
|
+
process_json([], '$ref': "#/definitions/#{@path}")
|
15
|
+
end
|
16
|
+
|
17
|
+
def _validate(data, result:)
|
18
|
+
super_data = super
|
19
|
+
return if super_data.nil?
|
20
|
+
|
21
|
+
# Lookup schema #
|
22
|
+
node = target
|
23
|
+
fail "Schema #{@path.to_s.inspect} not found." unless node
|
24
|
+
|
25
|
+
# Validate schema #
|
26
|
+
node._validate(super_data, result: result)
|
27
|
+
end
|
28
|
+
|
29
|
+
def target
|
30
|
+
schemas[@path] || Schemacop.context.schemas[@path] || GlobalContext.schema_for(@path)
|
31
|
+
end
|
32
|
+
|
33
|
+
def cast(data)
|
34
|
+
data = default if data.nil?
|
35
|
+
return target.cast(data)
|
36
|
+
end
|
37
|
+
|
38
|
+
def used_external_schemas
|
39
|
+
schemas.include?(@path) ? [] : [@path]
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
def init
|
45
|
+
@path = options.delete(:path)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Schemacop
|
2
|
+
class Result
|
3
|
+
attr_reader :current_path
|
4
|
+
attr_reader :errors
|
5
|
+
|
6
|
+
def initialize(root = nil, original_data = nil)
|
7
|
+
@current_path = []
|
8
|
+
@errors = {}
|
9
|
+
@root = root
|
10
|
+
@original_data = original_data
|
11
|
+
end
|
12
|
+
|
13
|
+
def valid?
|
14
|
+
errors.empty?
|
15
|
+
end
|
16
|
+
|
17
|
+
def data
|
18
|
+
if errors.any?
|
19
|
+
return nil
|
20
|
+
else
|
21
|
+
return @data ||= @root.cast(@original_data)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def error(message)
|
26
|
+
@errors[current_path] ||= []
|
27
|
+
@errors[current_path] << message
|
28
|
+
end
|
29
|
+
|
30
|
+
def messages_by_path
|
31
|
+
@errors.transform_keys { |k| "/#{k.join('/')}" }
|
32
|
+
end
|
33
|
+
|
34
|
+
def exception_message
|
35
|
+
messages.join("\n")
|
36
|
+
end
|
37
|
+
|
38
|
+
def messages
|
39
|
+
messages = []
|
40
|
+
|
41
|
+
@errors.each do |path, path_messages|
|
42
|
+
messages += path_messages.map do |path_message|
|
43
|
+
"/#{path.join('/')}: #{path_message}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
return messages
|
48
|
+
end
|
49
|
+
|
50
|
+
def in_path(segment)
|
51
|
+
prev_path = @current_path
|
52
|
+
@current_path += [segment]
|
53
|
+
yield
|
54
|
+
ensure
|
55
|
+
@current_path = prev_path
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module Schemacop
|
2
|
+
module V3
|
3
|
+
class StringNode < Node
|
4
|
+
ATTRIBUTES = %i[
|
5
|
+
min_length
|
6
|
+
max_length
|
7
|
+
pattern
|
8
|
+
format
|
9
|
+
].freeze
|
10
|
+
|
11
|
+
# rubocop:disable Layout/LineLength
|
12
|
+
FORMAT_PATTERNS = {
|
13
|
+
date: /^([0-9]{4})-?(1[0-2]|0[1-9])-?(3[01]|0[1-9]|[12][0-9])$/,
|
14
|
+
'date-time': /^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9]+)?(Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$/,
|
15
|
+
time: /^(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9]+)?(Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$/,
|
16
|
+
email: URI::MailTo::EMAIL_REGEXP,
|
17
|
+
boolean: /^(true|false)$/,
|
18
|
+
binary: nil,
|
19
|
+
integer: /^-?[0-9]+$/,
|
20
|
+
number: /^-?[0-9]+(\.[0-9]+)?$/
|
21
|
+
}.freeze
|
22
|
+
# rubocop:enable Layout/LineLength
|
23
|
+
|
24
|
+
def self.allowed_options
|
25
|
+
super + ATTRIBUTES - %i[cast_str] + %i[format_options]
|
26
|
+
end
|
27
|
+
|
28
|
+
def allowed_types
|
29
|
+
{ String => :string }
|
30
|
+
end
|
31
|
+
|
32
|
+
def as_json
|
33
|
+
process_json(ATTRIBUTES, type: :string)
|
34
|
+
end
|
35
|
+
|
36
|
+
def _validate(data, result:)
|
37
|
+
super_data = super
|
38
|
+
return if super_data.nil?
|
39
|
+
|
40
|
+
# Validate length #
|
41
|
+
length = super_data.size
|
42
|
+
|
43
|
+
if options[:min_length] && length < options[:min_length]
|
44
|
+
result.error "String is #{length} characters long but must be at least #{options[:min_length]}."
|
45
|
+
end
|
46
|
+
|
47
|
+
if options[:max_length] && length > options[:max_length]
|
48
|
+
result.error "String is #{length} characters long but must be at most #{options[:max_length]}."
|
49
|
+
end
|
50
|
+
|
51
|
+
# Validate pattern #
|
52
|
+
if options[:pattern] && !super_data.match?(Regexp.compile(options[:pattern]))
|
53
|
+
result.error "String does not match pattern #{options[:pattern].inspect}."
|
54
|
+
end
|
55
|
+
|
56
|
+
# Validate format #
|
57
|
+
if options[:format] && FORMAT_PATTERNS.include?(options[:format])
|
58
|
+
pattern = FORMAT_PATTERNS[options[:format]]
|
59
|
+
if pattern && !super_data.match?(pattern)
|
60
|
+
result.error "String does not match format #{options[:format].to_s.inspect}."
|
61
|
+
elsif options[:format_options] && Node.resolve_class(options[:format])
|
62
|
+
node = create(options[:format], **options[:format_options])
|
63
|
+
node._validate(cast(super_data), result: result)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def cast(value)
|
69
|
+
case options[:format]
|
70
|
+
when :boolean
|
71
|
+
return value == 'true'
|
72
|
+
when :date
|
73
|
+
return Date.parse(value)
|
74
|
+
when :'date-time'
|
75
|
+
return DateTime.parse(value)
|
76
|
+
when :time
|
77
|
+
Time.parse(value)
|
78
|
+
when :integer
|
79
|
+
return Integer(value)
|
80
|
+
when :number
|
81
|
+
return Float(value)
|
82
|
+
else
|
83
|
+
return value || default
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
protected
|
88
|
+
|
89
|
+
def init
|
90
|
+
if options.include?(:format)
|
91
|
+
options[:format] = options[:format].to_s.dasherize.to_sym
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def validate_self
|
96
|
+
if options.include?(:format) && !FORMAT_PATTERNS.include?(options[:format])
|
97
|
+
fail "Format #{options[:format].to_s.inspect} is not supported."
|
98
|
+
end
|
99
|
+
|
100
|
+
unless options[:min_length].nil? || options[:min_length].is_a?(Integer)
|
101
|
+
fail 'Option "min_length" must be an "integer"'
|
102
|
+
end
|
103
|
+
|
104
|
+
unless options[:max_length].nil? || options[:max_length].is_a?(Integer)
|
105
|
+
fail 'Option "max_length" must be an "integer"'
|
106
|
+
end
|
107
|
+
|
108
|
+
if options[:min_length] && options[:max_length] && options[:min_length] > options[:max_length]
|
109
|
+
fail 'Option "min_length" can\'t be greater than "max_length".'
|
110
|
+
end
|
111
|
+
|
112
|
+
if options[:pattern]
|
113
|
+
fail 'Option "pattern" must be a string.' unless options[:pattern].is_a?(String)
|
114
|
+
|
115
|
+
begin
|
116
|
+
Regexp.compile(options[:pattern])
|
117
|
+
rescue RegexpError => e
|
118
|
+
fail "Option \"pattern\" can't be parsed: #{e.message}."
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|