nxt_schema 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +86 -0
- data/LICENSE.txt +21 -0
- data/README.md +376 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/nxt_schema/callable.rb +74 -0
- data/lib/nxt_schema/callable_or_value.rb +72 -0
- data/lib/nxt_schema/dsl.rb +38 -0
- data/lib/nxt_schema/error_messages/en.yaml +15 -0
- data/lib/nxt_schema/error_messages.rb +40 -0
- data/lib/nxt_schema/errors/error.rb +7 -0
- data/lib/nxt_schema/errors/invalid_options_error.rb +5 -0
- data/lib/nxt_schema/errors/schema_not_applied_error.rb +5 -0
- data/lib/nxt_schema/errors.rb +4 -0
- data/lib/nxt_schema/node/base.rb +318 -0
- data/lib/nxt_schema/node/collection.rb +73 -0
- data/lib/nxt_schema/node/constructor.rb +9 -0
- data/lib/nxt_schema/node/default_value_evaluator.rb +20 -0
- data/lib/nxt_schema/node/error.rb +13 -0
- data/lib/nxt_schema/node/has_subnodes.rb +97 -0
- data/lib/nxt_schema/node/leaf.rb +43 -0
- data/lib/nxt_schema/node/maybe_evaluator.rb +23 -0
- data/lib/nxt_schema/node/schema.rb +147 -0
- data/lib/nxt_schema/node/template_store.rb +15 -0
- data/lib/nxt_schema/node/type_resolver.rb +24 -0
- data/lib/nxt_schema/node/validate_with_proxy.rb +41 -0
- data/lib/nxt_schema/node.rb +5 -0
- data/lib/nxt_schema/registry.rb +85 -0
- data/lib/nxt_schema/types.rb +10 -0
- data/lib/nxt_schema/undefined.rb +7 -0
- data/lib/nxt_schema/validators/attribute.rb +34 -0
- data/lib/nxt_schema/validators/equality.rb +33 -0
- data/lib/nxt_schema/validators/excluded.rb +23 -0
- data/lib/nxt_schema/validators/excludes.rb +23 -0
- data/lib/nxt_schema/validators/greater_than.rb +23 -0
- data/lib/nxt_schema/validators/greater_than_or_equal.rb +23 -0
- data/lib/nxt_schema/validators/included.rb +23 -0
- data/lib/nxt_schema/validators/includes.rb +23 -0
- data/lib/nxt_schema/validators/less_than.rb +23 -0
- data/lib/nxt_schema/validators/less_than_or_equal.rb +23 -0
- data/lib/nxt_schema/validators/optional_node.rb +26 -0
- data/lib/nxt_schema/validators/pattern.rb +24 -0
- data/lib/nxt_schema/validators/query.rb +28 -0
- data/lib/nxt_schema/validators/registry.rb +11 -0
- data/lib/nxt_schema/validators/validator.rb +17 -0
- data/lib/nxt_schema/version.rb +3 -0
- data/lib/nxt_schema.rb +69 -0
- data/nxt_schema.gemspec +46 -0
- metadata +211 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
module NxtSchema
|
2
|
+
module Node
|
3
|
+
class MaybeEvaluator
|
4
|
+
def initialize(node, evaluator, value)
|
5
|
+
@node = node
|
6
|
+
@evaluator = evaluator
|
7
|
+
@value = value
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :node, :evaluator, :value
|
11
|
+
|
12
|
+
def call
|
13
|
+
if evaluator.respond_to?(:call)
|
14
|
+
Callable.new(evaluator).call(node, value)
|
15
|
+
elsif value.is_a?(Symbol) && value.respond_to?(evaluator)
|
16
|
+
Callable.new(evaluator).bind(value).call(node, value)
|
17
|
+
else
|
18
|
+
value == evaluator
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
module NxtSchema
|
2
|
+
module Node
|
3
|
+
class Schema < Node::Base
|
4
|
+
def initialize(name:, type: NxtSchema::Types::Strict::Hash, parent_node:, **options, &block)
|
5
|
+
@template_store = TemplateStore.new
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
def apply(input, parent_node: self.parent_node, context: nil)
|
10
|
+
self.input = input
|
11
|
+
register_node(context)
|
12
|
+
|
13
|
+
self.parent_node = parent_node
|
14
|
+
self.schema_errors = { schema_errors_key => [] }
|
15
|
+
self.validation_errors = { schema_errors_key => [] }
|
16
|
+
self.value_store = {}
|
17
|
+
self.value = transform_keys(input)
|
18
|
+
|
19
|
+
if maybe_criteria_applies?(value)
|
20
|
+
self.value_store = value
|
21
|
+
else
|
22
|
+
self.value = value_or_default_value(value)
|
23
|
+
|
24
|
+
unless maybe_criteria_applies?(value)
|
25
|
+
self.value = coerce_value(value)
|
26
|
+
|
27
|
+
# TODO: We should not allow additional keys to be present per default?!
|
28
|
+
# TODO: Handle this here
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
sanitized_keys.each do |key|
|
33
|
+
node = template_store[key]
|
34
|
+
|
35
|
+
if allowed_additional_key?(key)
|
36
|
+
value_store[key] = input[key]
|
37
|
+
elsif node.presence? || input.key?(key)
|
38
|
+
node.apply(input[key], parent_node: self, context: context).schema_errors?
|
39
|
+
value_store[key] = node.value
|
40
|
+
schema_errors[key] = node.schema_errors
|
41
|
+
validation_errors[key] = node.validation_errors
|
42
|
+
else
|
43
|
+
evaluate_optional_option(node, input, key)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
self.value_store = coerce_value(value_store)
|
48
|
+
self.value = value_store
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
self_without_empty_schema_errors
|
53
|
+
rescue Dry::Types::ConstraintError, Dry::Types::CoercionError => error
|
54
|
+
add_schema_error(error.message)
|
55
|
+
self_without_empty_schema_errors
|
56
|
+
ensure
|
57
|
+
mark_as_applied
|
58
|
+
end
|
59
|
+
|
60
|
+
def optional(name, type, **options, &block)
|
61
|
+
raise_invalid_options_presence_options if options[:presence]
|
62
|
+
|
63
|
+
node(name, type, options.merge(optional: true), &block)
|
64
|
+
end
|
65
|
+
|
66
|
+
def present(name, type, **options, &block)
|
67
|
+
raise_invalid_options_presence_options if options[:optional]
|
68
|
+
|
69
|
+
node(name, type, options.merge(presence: true), &block)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def evaluate_optional_option(node, hash, key)
|
75
|
+
optional_option = node.options[:optional]
|
76
|
+
|
77
|
+
if optional_option.respond_to?(:call)
|
78
|
+
# Validator is added to the schema node!
|
79
|
+
add_validators(validator(:optional_node, optional_option, key))
|
80
|
+
elsif !optional_option
|
81
|
+
error_message = ErrorMessages.resolve(
|
82
|
+
locale,
|
83
|
+
:required_key_missing,
|
84
|
+
key: key,
|
85
|
+
target: hash
|
86
|
+
)
|
87
|
+
|
88
|
+
add_schema_error(error_message)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def transform_keys(hash)
|
93
|
+
return hash unless key_transformer && hash.respond_to?(:transform_keys!)
|
94
|
+
|
95
|
+
hash.transform_keys! { |key| Callable.new(key_transformer).bind(key).call(key) }
|
96
|
+
end
|
97
|
+
|
98
|
+
def key_transformer
|
99
|
+
@key_transformer ||= root.options.fetch(:transform_keys) { false }
|
100
|
+
end
|
101
|
+
|
102
|
+
def sanitized_keys
|
103
|
+
return template_store.keys if additional_keys_from_input.empty? || ignore_additional_keys?
|
104
|
+
return template_store.keys + additional_keys_from_input if additional_keys_allowed?
|
105
|
+
|
106
|
+
if restrict_additional_keys?
|
107
|
+
error_message = ErrorMessages.resolve(
|
108
|
+
locale,
|
109
|
+
:additional_keys_detected,
|
110
|
+
keys: additional_keys_from_input,
|
111
|
+
target: input
|
112
|
+
)
|
113
|
+
|
114
|
+
add_schema_error(error_message)
|
115
|
+
|
116
|
+
template_store.keys
|
117
|
+
else
|
118
|
+
raise Errors::InvalidOptionsError, "Invalid option for additional keys: #{additional_keys_strategy}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def allowed_additional_key?(key)
|
123
|
+
additional_keys_from_input.include?(key)
|
124
|
+
end
|
125
|
+
|
126
|
+
def additional_keys_from_input
|
127
|
+
(input&.keys || []) - template_store.keys
|
128
|
+
end
|
129
|
+
|
130
|
+
def additional_keys_allowed?
|
131
|
+
additional_keys_strategy.to_s == 'allow'
|
132
|
+
end
|
133
|
+
|
134
|
+
def ignore_additional_keys?
|
135
|
+
additional_keys_strategy.to_s == 'ignore'
|
136
|
+
end
|
137
|
+
|
138
|
+
def restrict_additional_keys?
|
139
|
+
additional_keys_strategy.to_s == 'restrict'
|
140
|
+
end
|
141
|
+
|
142
|
+
def raise_invalid_options_presence_options
|
143
|
+
raise InvalidOptionsError, 'Options :presence and :optional exclude each other'
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module NxtSchema
|
2
|
+
module Node
|
3
|
+
class TemplateStore < ::Hash
|
4
|
+
def push(node)
|
5
|
+
node_name = node.name
|
6
|
+
raise_key_error(node_name) if key?(node_name)
|
7
|
+
self[node_name] = node
|
8
|
+
end
|
9
|
+
|
10
|
+
def raise_key_error(key)
|
11
|
+
raise KeyError, "Node with name '#{key}' already registered! Node names must be unique!"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module NxtSchema
|
2
|
+
module Node
|
3
|
+
class TypeResolver
|
4
|
+
def resolve(type_system, type)
|
5
|
+
@resolve ||= {}
|
6
|
+
@resolve[type] ||= begin
|
7
|
+
if type.is_a?(Dry::Types::Type)
|
8
|
+
type
|
9
|
+
else
|
10
|
+
# Try to resolve in type system
|
11
|
+
type = type_system.const_get(type.to_s.classify)
|
12
|
+
|
13
|
+
if type.is_a?(Dry::Types::Type)
|
14
|
+
type
|
15
|
+
else
|
16
|
+
# in case it does not exist fallback to Types::Nominal
|
17
|
+
"NxtSchema::Types::Nominal::#{type.to_s.classify}".constantize
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module NxtSchema
|
2
|
+
module Node
|
3
|
+
class ValidateWithProxy
|
4
|
+
def initialize(node)
|
5
|
+
@node = node
|
6
|
+
@aggregated_errors = []
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :node
|
10
|
+
|
11
|
+
delegate_missing_to :node
|
12
|
+
|
13
|
+
def validate(&block)
|
14
|
+
result = instance_exec(&block)
|
15
|
+
return if result
|
16
|
+
|
17
|
+
copy_aggregated_errors_to_node
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_error(error)
|
21
|
+
aggregated_errors << error
|
22
|
+
false
|
23
|
+
end
|
24
|
+
|
25
|
+
def copy_aggregated_errors_to_node
|
26
|
+
aggregated_errors.each do |error|
|
27
|
+
node.add_error(error)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_reader :aggregated_errors
|
34
|
+
|
35
|
+
def validator(key, *args)
|
36
|
+
validator = node.validator(key, *args)
|
37
|
+
validator.call(self, node.value)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module NxtSchema
|
2
|
+
class Registry
|
3
|
+
def initialize(namespace_separator: '::', namespace: '')
|
4
|
+
@store = ActiveSupport::HashWithIndifferentAccess.new
|
5
|
+
@namespace_separator = namespace_separator
|
6
|
+
@namespace = namespace
|
7
|
+
end
|
8
|
+
|
9
|
+
delegate_missing_to :store
|
10
|
+
|
11
|
+
# register('strict::string')
|
12
|
+
# Registry[:strict].register
|
13
|
+
|
14
|
+
def register(key, value)
|
15
|
+
key = key.to_s
|
16
|
+
ensure_key_not_registered_already(key)
|
17
|
+
namespaced_store(key)[flat_key(key)] = value
|
18
|
+
end
|
19
|
+
|
20
|
+
def resolve(key, *args)
|
21
|
+
value = resolve_value(key)
|
22
|
+
return value unless value.respond_to?(:call)
|
23
|
+
|
24
|
+
value.call(*args)
|
25
|
+
end
|
26
|
+
|
27
|
+
def resolve_value(key)
|
28
|
+
key = key.to_s
|
29
|
+
parts = namespaced_key_parts(key)[0..-2]
|
30
|
+
|
31
|
+
namespaced_store = parts.inject(store) do |acc, key|
|
32
|
+
acc.fetch(key)
|
33
|
+
rescue KeyError
|
34
|
+
raise KeyError, "No registry found at #{key} in #{acc}"
|
35
|
+
end
|
36
|
+
|
37
|
+
begin
|
38
|
+
namespaced_store.fetch(flat_key(key))
|
39
|
+
rescue KeyError
|
40
|
+
raise KeyError, "Could not find #{flat_key(key)} in #{namespaced_store}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
attr_reader :store, :namespace_separator, :namespace
|
47
|
+
|
48
|
+
def namespaced_store(key)
|
49
|
+
parts = namespaced_key_parts(key)
|
50
|
+
|
51
|
+
current_parts = []
|
52
|
+
|
53
|
+
parts[0..-2].inject(store) do |acc, namespace|
|
54
|
+
current_parts << namespace
|
55
|
+
current_namespace = current_parts.join(namespace_separator)
|
56
|
+
|
57
|
+
acc.fetch(namespace) do
|
58
|
+
acc[namespace] = Registry.new(namespace: current_namespace)
|
59
|
+
acc = acc[namespace]
|
60
|
+
acc
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def namespaced_key_parts(key)
|
66
|
+
key.downcase.split(namespace_separator)
|
67
|
+
end
|
68
|
+
|
69
|
+
def flat_key(key)
|
70
|
+
namespaced_key_parts(key).last
|
71
|
+
end
|
72
|
+
|
73
|
+
def ensure_key_not_registered_already(key)
|
74
|
+
return unless namespaced_store(key).key?(flat_key(key))
|
75
|
+
|
76
|
+
raise KeyError, "Key: #{flat_key(key)} already registered in #{namespaced_store(key)}"
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_s
|
80
|
+
identifier = 'NxtSchema::Registry'
|
81
|
+
identifier << "#{namespace_separator}#{namespace}" unless namespace.blank?
|
82
|
+
identifier
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module NxtSchema
|
2
|
+
module Types
|
3
|
+
include Dry.Types()
|
4
|
+
|
5
|
+
StrippedString = Strict::String.constructor(->(string) { string&.strip })
|
6
|
+
StrippedNonBlankString = StrippedString.constrained(min_size: 1)
|
7
|
+
Enums = -> (*values) { Strict::String.enum(*values) } # Use as NxtSchema::Types::Enums[*ROLES]
|
8
|
+
SymbolizedEnums = -> (*values) { Coercible::Symbol.enum(*values) } # Use as NxtSchema::Types::SymboleEnums[*ROLES]
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module NxtSchema
|
2
|
+
module Validators
|
3
|
+
class Attribute < Validator
|
4
|
+
def initialize(method, expectation)
|
5
|
+
@method = method
|
6
|
+
@expectation = expectation
|
7
|
+
end
|
8
|
+
|
9
|
+
register_as :attribute, :attr
|
10
|
+
attr_reader :method, :expectation
|
11
|
+
|
12
|
+
# Query any attribute on a value with validator(:attribute, :size, ->(s) { s < 7 })
|
13
|
+
|
14
|
+
def build
|
15
|
+
lambda do |node, value|
|
16
|
+
raise ArgumentError, "#{value} does not respond to query: #{method}" unless value.respond_to?(method)
|
17
|
+
|
18
|
+
if expectation.call(value.send(method))
|
19
|
+
true
|
20
|
+
else
|
21
|
+
node.add_error(
|
22
|
+
translate_error(
|
23
|
+
node.locale,
|
24
|
+
attribute: value,
|
25
|
+
attribute_name: method,
|
26
|
+
value: value.send(method)
|
27
|
+
)
|
28
|
+
)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module NxtSchema
|
2
|
+
module Validators
|
3
|
+
class Equality < Validator
|
4
|
+
def initialize(expectation)
|
5
|
+
@expectation = expectation
|
6
|
+
end
|
7
|
+
|
8
|
+
register_as :equality, :eql
|
9
|
+
attr_reader :expectation
|
10
|
+
|
11
|
+
# Query for equality validator(:equality, 3)
|
12
|
+
# Query for equality validator(:eql, -> { 3 * 3 * 60 })
|
13
|
+
|
14
|
+
def build
|
15
|
+
lambda do |node, value|
|
16
|
+
expected_value = expectation.respond_to?(:call) ? Callable.new(expectation).call(node, value) : expectation
|
17
|
+
|
18
|
+
if value == expected_value
|
19
|
+
true
|
20
|
+
else
|
21
|
+
node.add_error(
|
22
|
+
translate_error(
|
23
|
+
node.locale,
|
24
|
+
actual: value,
|
25
|
+
expected: expected_value
|
26
|
+
)
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module NxtSchema
|
2
|
+
module Validators
|
3
|
+
class Excluded < Validator
|
4
|
+
def initialize(target)
|
5
|
+
@target = target
|
6
|
+
end
|
7
|
+
|
8
|
+
register_as :excluded
|
9
|
+
attr_reader :target
|
10
|
+
|
11
|
+
def build
|
12
|
+
lambda do |node, value|
|
13
|
+
if target.exclude?(value)
|
14
|
+
true
|
15
|
+
else
|
16
|
+
message = translate_error(node.locale, target: target, value: value)
|
17
|
+
node.add_error(message)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module NxtSchema
|
2
|
+
module Validators
|
3
|
+
class Excludes < Validator
|
4
|
+
def initialize(target)
|
5
|
+
@target = target
|
6
|
+
end
|
7
|
+
|
8
|
+
register_as :excludes
|
9
|
+
attr_reader :target
|
10
|
+
|
11
|
+
def build
|
12
|
+
lambda do |node, value|
|
13
|
+
if value.exclude?(target)
|
14
|
+
true
|
15
|
+
else
|
16
|
+
message = translate_error(node.locale, target: target, value: value)
|
17
|
+
node.add_error(message)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module NxtSchema
|
2
|
+
module Validators
|
3
|
+
class GreaterThan < Validator
|
4
|
+
def initialize(threshold)
|
5
|
+
@threshold = threshold
|
6
|
+
end
|
7
|
+
|
8
|
+
register_as :greater_than
|
9
|
+
attr_reader :threshold
|
10
|
+
|
11
|
+
def build
|
12
|
+
lambda do |node, value|
|
13
|
+
if value > threshold
|
14
|
+
true
|
15
|
+
else
|
16
|
+
message = translate_error(node.locale, value: value, threshold: threshold)
|
17
|
+
node.add_error(message)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module NxtSchema
|
2
|
+
module Validators
|
3
|
+
class GreaterThanOrEqual < Validator
|
4
|
+
def initialize(threshold)
|
5
|
+
@threshold = threshold
|
6
|
+
end
|
7
|
+
|
8
|
+
register_as :greater_than_or_equal, :gt_or_eql
|
9
|
+
attr_reader :threshold
|
10
|
+
|
11
|
+
def build
|
12
|
+
lambda do |node, value|
|
13
|
+
if value >= threshold
|
14
|
+
true
|
15
|
+
else
|
16
|
+
message = translate_error(node.locale, value: value, threshold: threshold)
|
17
|
+
node.add_error(message)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module NxtSchema
|
2
|
+
module Validators
|
3
|
+
class Included < Validator
|
4
|
+
def initialize(target)
|
5
|
+
@target = target
|
6
|
+
end
|
7
|
+
|
8
|
+
register_as :included
|
9
|
+
attr_reader :target
|
10
|
+
|
11
|
+
def build
|
12
|
+
lambda do |node, value|
|
13
|
+
if target.include?(value)
|
14
|
+
true
|
15
|
+
else
|
16
|
+
message = translate_error(node.locale, value: value, target: target)
|
17
|
+
node.add_error(message)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module NxtSchema
|
2
|
+
module Validators
|
3
|
+
class Includes < Validator
|
4
|
+
def initialize(target)
|
5
|
+
@target = target
|
6
|
+
end
|
7
|
+
|
8
|
+
register_as :includes
|
9
|
+
attr_reader :target
|
10
|
+
|
11
|
+
def build
|
12
|
+
lambda do |node, value|
|
13
|
+
if value.include?(target)
|
14
|
+
true
|
15
|
+
else
|
16
|
+
message = translate_error(node.locale, value: value, target: target)
|
17
|
+
node.add_error(message)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module NxtSchema
|
2
|
+
module Validators
|
3
|
+
class LessThan < Validator
|
4
|
+
def initialize(threshold)
|
5
|
+
@threshold = threshold
|
6
|
+
end
|
7
|
+
|
8
|
+
register_as :less_than
|
9
|
+
attr_reader :threshold
|
10
|
+
|
11
|
+
def build
|
12
|
+
lambda do |node, value|
|
13
|
+
if value < threshold
|
14
|
+
true
|
15
|
+
else
|
16
|
+
message = translate_error(node.locale, value: value, threshold: threshold)
|
17
|
+
node.add_error(message)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module NxtSchema
|
2
|
+
module Validators
|
3
|
+
class LessThanOrEqual < Validator
|
4
|
+
def initialize(threshold)
|
5
|
+
@threshold = threshold
|
6
|
+
end
|
7
|
+
|
8
|
+
register_as :less_than_or_equal, :lt_or_eql
|
9
|
+
attr_reader :threshold
|
10
|
+
|
11
|
+
def build
|
12
|
+
lambda do |node, value|
|
13
|
+
if value <= threshold
|
14
|
+
true
|
15
|
+
else
|
16
|
+
message = translate_error(node.locale, value: value, threshold: threshold)
|
17
|
+
node.add_error(message)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module NxtSchema
|
2
|
+
module Validators
|
3
|
+
class OptionalNode < Validator
|
4
|
+
def initialize(conditional, missing_key)
|
5
|
+
@conditional = conditional
|
6
|
+
@missing_key = missing_key
|
7
|
+
end
|
8
|
+
|
9
|
+
register_as :optional_node
|
10
|
+
attr_reader :conditional, :missing_key
|
11
|
+
|
12
|
+
def build
|
13
|
+
lambda do |node, value|
|
14
|
+
args = [node, value]
|
15
|
+
|
16
|
+
if conditional.call(*args.take(conditional.arity))
|
17
|
+
true
|
18
|
+
else
|
19
|
+
message = ErrorMessages.resolve(node.locale, :required_key_missing, key: missing_key, target: node.value)
|
20
|
+
node.add_error(message)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module NxtSchema
|
2
|
+
module Validators
|
3
|
+
class Pattern < Validator
|
4
|
+
def initialize(pattern)
|
5
|
+
@pattern = pattern
|
6
|
+
end
|
7
|
+
|
8
|
+
register_as :pattern, :format
|
9
|
+
attr_reader :pattern
|
10
|
+
|
11
|
+
def build
|
12
|
+
lambda do |node, value|
|
13
|
+
if value.match(pattern)
|
14
|
+
true
|
15
|
+
else
|
16
|
+
message = translate_error(node.locale, value: value, pattern: pattern)
|
17
|
+
node.add_error(message)
|
18
|
+
false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|