nxt_schema 0.1.0
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 +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
|