dry-validation 0.8.0 → 0.9.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 +4 -4
- data/.travis.yml +1 -1
- data/CHANGELOG.md +39 -1
- data/benchmarks/benchmark_schema_invalid_huge.rb +52 -0
- data/benchmarks/profile_schema_huge_invalid.rb +30 -0
- data/config/errors.yml +3 -2
- data/dry-validation.gemspec +2 -2
- data/lib/dry/validation.rb +20 -32
- data/lib/dry/validation/constants.rb +6 -0
- data/lib/dry/validation/error.rb +5 -2
- data/lib/dry/validation/error_compiler.rb +46 -116
- data/lib/dry/validation/executor.rb +105 -0
- data/lib/dry/validation/hint_compiler.rb +36 -68
- data/lib/dry/validation/message.rb +86 -0
- data/lib/dry/validation/message_compiler.rb +141 -0
- data/lib/dry/validation/message_set.rb +70 -0
- data/lib/dry/validation/messages/abstract.rb +1 -1
- data/lib/dry/validation/messages/i18n.rb +5 -0
- data/lib/dry/validation/predicate_registry.rb +8 -3
- data/lib/dry/validation/result.rb +6 -7
- data/lib/dry/validation/schema.rb +21 -227
- data/lib/dry/validation/schema/check.rb +1 -1
- data/lib/dry/validation/schema/class_interface.rb +193 -0
- data/lib/dry/validation/schema/deprecated.rb +1 -2
- data/lib/dry/validation/schema/key.rb +4 -0
- data/lib/dry/validation/schema/value.rb +12 -7
- data/lib/dry/validation/schema_compiler.rb +20 -1
- data/lib/dry/validation/type_specs.rb +70 -0
- data/lib/dry/validation/version.rb +1 -1
- data/spec/fixtures/locales/pl.yml +1 -1
- data/spec/integration/custom_predicates_spec.rb +37 -0
- data/spec/integration/error_compiler_spec.rb +39 -39
- data/spec/integration/form/predicates/key_spec.rb +10 -18
- data/spec/integration/form/predicates/size/fixed_spec.rb +8 -12
- data/spec/integration/form/predicates/size/range_spec.rb +7 -7
- data/spec/integration/hints_spec.rb +17 -0
- data/spec/integration/messages/i18n_spec.rb +2 -2
- data/spec/integration/schema/check_rules_spec.rb +2 -2
- data/spec/integration/schema/defining_base_schema_spec.rb +38 -0
- data/spec/integration/schema/dynamic_predicate_args_spec.rb +18 -0
- data/spec/integration/schema/macros/each_spec.rb +2 -2
- data/spec/integration/schema/macros/input_spec.rb +102 -10
- data/spec/integration/schema/macros/maybe_spec.rb +30 -0
- data/spec/integration/schema/nested_schemas_spec.rb +200 -0
- data/spec/integration/schema/nested_values_spec.rb +3 -1
- data/spec/integration/schema/option_with_default_spec.rb +54 -20
- data/spec/integration/schema/predicates/size/fixed_spec.rb +10 -10
- data/spec/integration/schema/predicates/size/range_spec.rb +8 -10
- data/spec/unit/error_compiler_spec.rb +1 -1
- data/spec/unit/hint_compiler_spec.rb +2 -2
- metadata +18 -7
- data/examples/rule_ast.rb +0 -25
- data/lib/dry/validation/error_compiler/input.rb +0 -135
@@ -0,0 +1,193 @@
|
|
1
|
+
require 'dry/validation/messages/yaml'
|
2
|
+
require 'dry/validation/messages/i18n'
|
3
|
+
|
4
|
+
require 'dry/validation/type_specs'
|
5
|
+
|
6
|
+
module Dry
|
7
|
+
module Validation
|
8
|
+
class Schema
|
9
|
+
extend Dry::Configurable
|
10
|
+
extend TypeSpecs
|
11
|
+
|
12
|
+
NOOP_INPUT_PROCESSOR = -> input { input }
|
13
|
+
|
14
|
+
setting :path
|
15
|
+
setting :predicates, Logic::Predicates
|
16
|
+
setting :registry
|
17
|
+
setting :messages, :yaml
|
18
|
+
setting :messages_file
|
19
|
+
setting :namespace
|
20
|
+
setting :rules, []
|
21
|
+
setting :checks, []
|
22
|
+
setting :options, {}
|
23
|
+
setting :input, nil
|
24
|
+
setting :input_rule, nil
|
25
|
+
setting :dsl_extensions, nil
|
26
|
+
|
27
|
+
setting :input_processor_map, {
|
28
|
+
sanitizer: InputProcessorCompiler::Sanitizer.new,
|
29
|
+
json: InputProcessorCompiler::JSON.new,
|
30
|
+
form: InputProcessorCompiler::Form.new,
|
31
|
+
}.freeze
|
32
|
+
|
33
|
+
setting :type_specs, false
|
34
|
+
|
35
|
+
def self.new(rules = config.rules, **options)
|
36
|
+
super(rules, default_options.merge(options))
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.define(options = {}, &block)
|
40
|
+
source = options.fetch(:schema_class)
|
41
|
+
config = source.config
|
42
|
+
|
43
|
+
dsl_ext = config.dsl_extensions
|
44
|
+
|
45
|
+
dsl = Schema::Value.new(options.merge(registry: source.registry))
|
46
|
+
dsl_ext.__send__(:extend_object, dsl) if dsl_ext
|
47
|
+
dsl.predicates(options[:predicates]) if options.key?(:predicates)
|
48
|
+
dsl.instance_exec(&block) if block
|
49
|
+
|
50
|
+
target = dsl.schema_class
|
51
|
+
|
52
|
+
if config.input
|
53
|
+
config.input_rule = dsl.__send__(:infer_predicates, Array(target.config.input))
|
54
|
+
end
|
55
|
+
|
56
|
+
rules = target.config.rules + (options.fetch(:rules, []) + dsl.rules)
|
57
|
+
|
58
|
+
target.configure do |cfg|
|
59
|
+
cfg.rules = rules
|
60
|
+
cfg.checks = cfg.checks + dsl.checks
|
61
|
+
cfg.path = dsl.path
|
62
|
+
cfg.type_map = target.build_type_map(dsl.type_map) if cfg.type_specs
|
63
|
+
end
|
64
|
+
|
65
|
+
target
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.define!(options = {}, &block)
|
69
|
+
define(schema_class: self, &block)
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.predicates(predicate_set = nil)
|
73
|
+
if predicate_set
|
74
|
+
config.predicates = predicate_set
|
75
|
+
set_registry!
|
76
|
+
else
|
77
|
+
config.predicates
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.option(name, default = nil)
|
82
|
+
attr_reader(*name)
|
83
|
+
options.update(name => default)
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.create_class(target, other = nil, &block)
|
87
|
+
klass =
|
88
|
+
if other.is_a?(self)
|
89
|
+
Class.new(other.class)
|
90
|
+
elsif other.is_a?(Class) && other < Types::Struct
|
91
|
+
Validation.Schema(parent: target, build: false) do
|
92
|
+
other.schema.each { |attr, type| required(attr).filled(type) }
|
93
|
+
end
|
94
|
+
elsif other.respond_to?(:schema) && other.schema.is_a?(self)
|
95
|
+
Class.new(other.schema.class)
|
96
|
+
else
|
97
|
+
Validation.Schema(target.schema_class, parent: target, build: false, &block)
|
98
|
+
end
|
99
|
+
|
100
|
+
klass.config.path = target.path if other
|
101
|
+
klass.config.input_processor = :noop
|
102
|
+
|
103
|
+
klass
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.clone
|
107
|
+
klass = Class.new(self)
|
108
|
+
klass.config.rules = []
|
109
|
+
klass.config.registry = registry
|
110
|
+
klass
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.to_ast
|
114
|
+
[:schema, self]
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.registry
|
118
|
+
config.registry
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.type_map
|
122
|
+
config.type_map
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.rules
|
126
|
+
config.rules
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.options
|
130
|
+
config.options
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.messages
|
134
|
+
default = default_messages
|
135
|
+
|
136
|
+
if config.messages_file && config.namespace
|
137
|
+
default.merge(config.messages_file).namespaced(config.namespace)
|
138
|
+
elsif config.messages_file
|
139
|
+
default.merge(config.messages_file)
|
140
|
+
elsif config.namespace
|
141
|
+
default.namespaced(config.namespace)
|
142
|
+
else
|
143
|
+
default
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def self.default_messages
|
148
|
+
case config.messages
|
149
|
+
when :yaml then Messages.default
|
150
|
+
when :i18n then Messages::I18n.new
|
151
|
+
else
|
152
|
+
raise "+#{config.messages}+ is not a valid messages identifier"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def self.error_compiler
|
157
|
+
@error_compiler ||= ErrorCompiler.new(messages)
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.hint_compiler
|
161
|
+
@hint_compiler ||= HintCompiler.new(messages, rules: rule_ast)
|
162
|
+
end
|
163
|
+
|
164
|
+
def self.rule_ast
|
165
|
+
@rule_ast ||= config.rules.map(&:to_ast)
|
166
|
+
end
|
167
|
+
|
168
|
+
def self.default_options
|
169
|
+
@default_options ||= { predicate_registry: registry,
|
170
|
+
error_compiler: error_compiler,
|
171
|
+
hint_compiler: hint_compiler,
|
172
|
+
input_processor: input_processor,
|
173
|
+
checks: config.checks }
|
174
|
+
end
|
175
|
+
|
176
|
+
def self.inherited(klass)
|
177
|
+
super
|
178
|
+
|
179
|
+
klass.config.options = klass.config.options.dup
|
180
|
+
|
181
|
+
if registry && self != Schema
|
182
|
+
klass.config.registry = registry.new(self)
|
183
|
+
else
|
184
|
+
klass.set_registry!
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def self.set_registry!
|
189
|
+
config.registry = PredicateRegistry[self, config.predicates]
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
@@ -9,8 +9,7 @@ module Dry
|
|
9
9
|
if type_map.is_a?(Dry::Types::Safe) && config.input_processor != :noop
|
10
10
|
type_map
|
11
11
|
elsif type_map.size > 0 && config.input_processor != :noop
|
12
|
-
|
13
|
-
.public_send(config.hash_type, type_map)
|
12
|
+
build_hash_type(type_map)
|
14
13
|
elsif input_processor_compiler
|
15
14
|
input_processor_compiler.(rule_ast)
|
16
15
|
else
|
@@ -26,6 +26,10 @@ module Dry
|
|
26
26
|
create_rule([type, [name, value.each(*predicates, &block).to_ast]])
|
27
27
|
end
|
28
28
|
|
29
|
+
def schema(other = nil, &block)
|
30
|
+
create_rule([type, [name, value.schema(other, &block).to_ast]])
|
31
|
+
end
|
32
|
+
|
29
33
|
def hash?(&block)
|
30
34
|
predicate = registry[:hash?]
|
31
35
|
|
@@ -18,8 +18,8 @@ module Dry
|
|
18
18
|
@registry = options[:registry] = schema_class.predicates(mod)
|
19
19
|
end
|
20
20
|
|
21
|
-
def input(
|
22
|
-
schema_class.config.input =
|
21
|
+
def input(*predicates)
|
22
|
+
schema_class.config.input = predicates
|
23
23
|
self
|
24
24
|
end
|
25
25
|
|
@@ -115,9 +115,8 @@ module Dry
|
|
115
115
|
end
|
116
116
|
|
117
117
|
def configure(&block)
|
118
|
-
|
119
|
-
@
|
120
|
-
@registry = klass.registry
|
118
|
+
schema_class.class_eval(&block)
|
119
|
+
@registry = schema_class.registry
|
121
120
|
self
|
122
121
|
end
|
123
122
|
|
@@ -146,7 +145,7 @@ module Dry
|
|
146
145
|
end
|
147
146
|
|
148
147
|
def predicate(name, *args)
|
149
|
-
registry.ensure_valid_predicate(name, args)
|
148
|
+
registry.ensure_valid_predicate(name, args, schema_class)
|
150
149
|
registry[name].curry(*args)
|
151
150
|
end
|
152
151
|
|
@@ -164,9 +163,13 @@ module Dry
|
|
164
163
|
end
|
165
164
|
end
|
166
165
|
|
166
|
+
def dyn_arg?(name)
|
167
|
+
schema_class.instance_methods.include?(name)
|
168
|
+
end
|
169
|
+
|
167
170
|
private
|
168
171
|
|
169
|
-
def infer_predicates(predicates, infer_on)
|
172
|
+
def infer_predicates(predicates, infer_on = self)
|
170
173
|
predicates.map { |predicate|
|
171
174
|
name, *args = ::Kernel.Array(predicate).first
|
172
175
|
|
@@ -179,6 +182,8 @@ module Dry
|
|
179
182
|
end
|
180
183
|
|
181
184
|
def method_missing(meth, *args, &block)
|
185
|
+
return schema_class.instance_method(meth) if dyn_arg?(meth)
|
186
|
+
|
182
187
|
val_rule = create_rule([:val, predicate(meth, *args).to_ast])
|
183
188
|
|
184
189
|
if block
|
@@ -29,8 +29,27 @@ module Dry
|
|
29
29
|
end
|
30
30
|
|
31
31
|
class SchemaCompiler < Logic::RuleCompiler
|
32
|
+
attr_reader :schema, :options
|
33
|
+
|
34
|
+
def initialize(*args, options)
|
35
|
+
super(*args)
|
36
|
+
@options = options
|
37
|
+
@schema = predicates.schema
|
38
|
+
end
|
39
|
+
|
40
|
+
def visit_predicate(node)
|
41
|
+
super.evaluate_args!(schema)
|
42
|
+
end
|
43
|
+
|
32
44
|
def visit_schema(klass)
|
33
|
-
klass.
|
45
|
+
opt_keys = klass.config.options.keys
|
46
|
+
opt_vals = options.values_at(*opt_keys).compact
|
47
|
+
|
48
|
+
if opt_vals.empty?
|
49
|
+
klass.new
|
50
|
+
else
|
51
|
+
klass.new(klass.config.rules, Hash[opt_keys.zip(opt_vals)])
|
52
|
+
end
|
34
53
|
end
|
35
54
|
|
36
55
|
def visit_guard(node)
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Dry
|
2
|
+
module Validation
|
3
|
+
module TypeSpecs
|
4
|
+
def self.extended(klass)
|
5
|
+
super
|
6
|
+
klass.class_eval do
|
7
|
+
setting :input_processor, :noop
|
8
|
+
setting :hash_type, :weak
|
9
|
+
setting :type_map, {}
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def build_type_map(type_specs, category = config.input_processor)
|
14
|
+
if type_specs.is_a?(Array)
|
15
|
+
build_array_type(type_specs[0], category)
|
16
|
+
else
|
17
|
+
type_specs.reduce({}) { |res, (name, spec)|
|
18
|
+
res.update(name => resolve_spec(spec, category))
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def build_hash_type(spec = type_map)
|
24
|
+
lookup_type("hash", config.input_processor)
|
25
|
+
.public_send(config.hash_type, spec)
|
26
|
+
end
|
27
|
+
|
28
|
+
def build_array_type(spec, category)
|
29
|
+
member_schema = build_type_map(spec, category)
|
30
|
+
member_type = lookup_type("hash", category)
|
31
|
+
.public_send(config.hash_type, member_schema)
|
32
|
+
|
33
|
+
lookup_type("array", category).member(member_type)
|
34
|
+
end
|
35
|
+
|
36
|
+
def build_sum_type(spec, category)
|
37
|
+
spec
|
38
|
+
.map { |id| id.is_a?(Symbol) ? lookup_type(id, category) : id }
|
39
|
+
.reduce(:|)
|
40
|
+
end
|
41
|
+
|
42
|
+
def resolve_spec(spec, category)
|
43
|
+
case spec
|
44
|
+
when Symbol
|
45
|
+
lookup_type(spec, category)
|
46
|
+
when Hash
|
47
|
+
build_hash_type(spec)
|
48
|
+
when Array
|
49
|
+
if spec.size == 1
|
50
|
+
if spec[0].is_a?(Hash)
|
51
|
+
build_array_type(spec[0], category)
|
52
|
+
else
|
53
|
+
array_member = lookup_type(spec[0], category)
|
54
|
+
lookup_type("array", category).member(array_member)
|
55
|
+
end
|
56
|
+
else
|
57
|
+
build_sum_type(spec, category)
|
58
|
+
end
|
59
|
+
else
|
60
|
+
spec
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def lookup_type(name, category)
|
65
|
+
id = "#{category}.#{name}"
|
66
|
+
Types.type_keys.include?(id) ? Types[id] : Types[name.to_s]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -187,4 +187,41 @@ RSpec.describe Dry::Validation do
|
|
187
187
|
|
188
188
|
expect(schema.(foo: { bar: "1" })).to be_success
|
189
189
|
end
|
190
|
+
|
191
|
+
it 'works with interpolation of messages' do
|
192
|
+
schema = Dry::Validation.Schema do
|
193
|
+
configure do
|
194
|
+
option :categories, []
|
195
|
+
|
196
|
+
def self.messages
|
197
|
+
Dry::Validation::Messages.default.merge(
|
198
|
+
en: {
|
199
|
+
errors: {
|
200
|
+
valid_category?: 'must be one of the categories: %{categories}'
|
201
|
+
}
|
202
|
+
},
|
203
|
+
pl: {
|
204
|
+
errors: {
|
205
|
+
valid_category?: 'musi być jedną z: %{categories}'
|
206
|
+
}
|
207
|
+
}
|
208
|
+
)
|
209
|
+
end
|
210
|
+
|
211
|
+
def valid_category?(categories, value)
|
212
|
+
categories.include?(value)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
required(:category).filled(valid_category?: categories)
|
217
|
+
end.with(categories: %w(foo bar))
|
218
|
+
|
219
|
+
expect(schema.(category: 'baz').messages).to eql(
|
220
|
+
category: ['must be one of the categories: foo, bar']
|
221
|
+
)
|
222
|
+
|
223
|
+
expect(schema.(category: 'baz').messages(locale: :pl)).to eql(
|
224
|
+
category: ['musi być jedną z: foo, bar']
|
225
|
+
)
|
226
|
+
end
|
190
227
|
end
|
@@ -48,7 +48,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
48
48
|
end
|
49
49
|
|
50
50
|
it 'converts error ast into another format' do
|
51
|
-
expect(error_compiler.(ast)).to eql(
|
51
|
+
expect(error_compiler.(ast).to_h).to eql(
|
52
52
|
name: ["+name+ key is missing in the hash"],
|
53
53
|
gender: ["Please provide your gender"],
|
54
54
|
age: ["must be greater than 18"],
|
@@ -80,7 +80,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
80
80
|
end
|
81
81
|
|
82
82
|
it 'converts error ast into another format' do
|
83
|
-
expect(error_compiler.(ast)).to eql(
|
83
|
+
expect(error_compiler.(ast).to_h).to eql(
|
84
84
|
settings: { newsletter: ['must be false'] }
|
85
85
|
)
|
86
86
|
end
|
@@ -103,7 +103,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
103
103
|
end
|
104
104
|
|
105
105
|
it 'converts error ast into another format' do
|
106
|
-
expect(error_compiler.(ast)).to eql(
|
106
|
+
expect(error_compiler.(ast).to_h).to eql(
|
107
107
|
payments: { 1 => { method: ['+method+ key is missing in the hash'] } }
|
108
108
|
)
|
109
109
|
end
|
@@ -116,7 +116,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
116
116
|
[:input, [:num, [:result, ['2', [:val, p(:int?)]]]]]
|
117
117
|
)
|
118
118
|
|
119
|
-
expect(msg).to eql(
|
119
|
+
expect(msg).to eql('num must be an integer')
|
120
120
|
end
|
121
121
|
end
|
122
122
|
|
@@ -126,7 +126,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
126
126
|
[:input, [:email, [:result, ['oops', [:val, p(:email?)]]]]]
|
127
127
|
)
|
128
128
|
|
129
|
-
expect(msg).to eql(
|
129
|
+
expect(msg).to eql('adres email nie jest poprawny')
|
130
130
|
end
|
131
131
|
end
|
132
132
|
|
@@ -136,7 +136,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
136
136
|
[:input, [:tags, [:result, [nil, [:val, p(:empty?)]]]]]
|
137
137
|
)
|
138
138
|
|
139
|
-
expect(msg).to eql(
|
139
|
+
expect(msg).to eql('must be empty')
|
140
140
|
end
|
141
141
|
end
|
142
142
|
|
@@ -146,7 +146,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
146
146
|
[:input, [:num, [:result, [2, [:val, p(:excluded_from?, [1, 2, 3])]]]]]
|
147
147
|
)
|
148
148
|
|
149
|
-
expect(msg).to eql(
|
149
|
+
expect(msg).to eql('must not be one of: 1, 2, 3')
|
150
150
|
end
|
151
151
|
end
|
152
152
|
|
@@ -156,7 +156,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
156
156
|
[:input, [:array, [:result, [[1, 2, 3], [:val, p(:excludes?, 2)]]]]]
|
157
157
|
)
|
158
158
|
|
159
|
-
expect(msg).to eql(
|
159
|
+
expect(msg).to eql('must not include 2')
|
160
160
|
end
|
161
161
|
end
|
162
162
|
|
@@ -166,7 +166,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
166
166
|
[:input, [:num, [:result, [2, [:val, p(:included_in?, [1, 2, 3])]]]]]
|
167
167
|
)
|
168
168
|
|
169
|
-
expect(msg).to eql(
|
169
|
+
expect(msg).to eql('must be one of: 1, 2, 3')
|
170
170
|
end
|
171
171
|
end
|
172
172
|
|
@@ -176,7 +176,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
176
176
|
[:input, [:num, [:result, [[1, 2, 3], [:val, p(:includes?, 2)]]]]]
|
177
177
|
)
|
178
178
|
|
179
|
-
expect(msg).to eql(
|
179
|
+
expect(msg).to eql('must include 2')
|
180
180
|
end
|
181
181
|
end
|
182
182
|
|
@@ -186,7 +186,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
186
186
|
[:input, [:num, [:result, [2, [:val, p(:gt?, 3)]]]]]
|
187
187
|
)
|
188
188
|
|
189
|
-
expect(msg).to eql(
|
189
|
+
expect(msg).to eql('must be greater than 3')
|
190
190
|
end
|
191
191
|
end
|
192
192
|
|
@@ -196,7 +196,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
196
196
|
[:input, [:num, [:result, [2, [:val, p(:gteq?, 3)]]]]]
|
197
197
|
)
|
198
198
|
|
199
|
-
expect(msg).to eql(
|
199
|
+
expect(msg).to eql('must be greater than or equal to 3')
|
200
200
|
end
|
201
201
|
end
|
202
202
|
|
@@ -206,7 +206,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
206
206
|
[:input, [:num, [:result, [2, [:val, p(:lt?, 3)]]]]]
|
207
207
|
)
|
208
208
|
|
209
|
-
expect(msg).to eql(
|
209
|
+
expect(msg).to eql('must be less than 3')
|
210
210
|
end
|
211
211
|
end
|
212
212
|
|
@@ -216,7 +216,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
216
216
|
[:input, [:num, [:result, [2, [:val, p(:lteq?, 3)]]]]]
|
217
217
|
)
|
218
218
|
|
219
|
-
expect(msg).to eql(
|
219
|
+
expect(msg).to eql('must be less than or equal to 3')
|
220
220
|
end
|
221
221
|
end
|
222
222
|
|
@@ -226,7 +226,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
226
226
|
[:input, [:address, [:result, ['', [:val, p(:hash?, [])]]]]]
|
227
227
|
)
|
228
228
|
|
229
|
-
expect(msg).to eql(
|
229
|
+
expect(msg).to eql('must be a hash')
|
230
230
|
end
|
231
231
|
end
|
232
232
|
|
@@ -236,7 +236,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
236
236
|
[:input, [:phone_numbers, [:result, ['', [:val, p(:array?)]]]]]
|
237
237
|
)
|
238
238
|
|
239
|
-
expect(msg).to eql(
|
239
|
+
expect(msg).to eql('must be an array')
|
240
240
|
end
|
241
241
|
end
|
242
242
|
|
@@ -246,7 +246,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
246
246
|
[:input, [:num, [:result, ['2', [:val, p(:int?)]]]]]
|
247
247
|
)
|
248
248
|
|
249
|
-
expect(msg).to eql(
|
249
|
+
expect(msg).to eql('must be an integer')
|
250
250
|
end
|
251
251
|
end
|
252
252
|
|
@@ -256,7 +256,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
256
256
|
[:input, [:num, [:result, ['2', [:val, p(:float?)]]]]]
|
257
257
|
)
|
258
258
|
|
259
|
-
expect(msg).to eql(
|
259
|
+
expect(msg).to eql('must be a float')
|
260
260
|
end
|
261
261
|
end
|
262
262
|
|
@@ -266,7 +266,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
266
266
|
[:input, [:num, [:result, ['2', [:val, p(:decimal?)]]]]]
|
267
267
|
)
|
268
268
|
|
269
|
-
expect(msg).to eql(
|
269
|
+
expect(msg).to eql('must be a decimal')
|
270
270
|
end
|
271
271
|
end
|
272
272
|
|
@@ -276,7 +276,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
276
276
|
[:input, [:num, [:result, ['2', [:val, p(:date?)]]]]]
|
277
277
|
)
|
278
278
|
|
279
|
-
expect(msg).to eql(
|
279
|
+
expect(msg).to eql('must be a date')
|
280
280
|
end
|
281
281
|
end
|
282
282
|
|
@@ -286,7 +286,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
286
286
|
[:input, [:num, [:result, ['2', [:val, p(:date_time?)]]]]]
|
287
287
|
)
|
288
288
|
|
289
|
-
expect(msg).to eql(
|
289
|
+
expect(msg).to eql('must be a date time')
|
290
290
|
end
|
291
291
|
end
|
292
292
|
|
@@ -296,7 +296,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
296
296
|
[:input, [:num, [:result, ['2', [:val, p(:time?)]]]]]
|
297
297
|
)
|
298
298
|
|
299
|
-
expect(msg).to eql(
|
299
|
+
expect(msg).to eql('must be a time')
|
300
300
|
end
|
301
301
|
end
|
302
302
|
|
@@ -306,7 +306,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
306
306
|
[:input, [:num, [:result, ['abcd', [:val, p(:max_size?, 3)]]]]]
|
307
307
|
)
|
308
308
|
|
309
|
-
expect(msg).to eql(
|
309
|
+
expect(msg).to eql('size cannot be greater than 3')
|
310
310
|
end
|
311
311
|
end
|
312
312
|
|
@@ -316,7 +316,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
316
316
|
[:input, [:num, [:result, ['ab', [:val, p(:min_size?, 3)]]]]]
|
317
317
|
)
|
318
318
|
|
319
|
-
expect(msg).to eql(
|
319
|
+
expect(msg).to eql('size cannot be less than 3')
|
320
320
|
end
|
321
321
|
end
|
322
322
|
|
@@ -326,7 +326,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
326
326
|
[:input, [:num, [:result, [nil, [:val, p(:none?)]]]]]
|
327
327
|
)
|
328
328
|
|
329
|
-
expect(msg).to eql(
|
329
|
+
expect(msg).to eql('cannot be defined')
|
330
330
|
end
|
331
331
|
end
|
332
332
|
|
@@ -336,7 +336,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
336
336
|
[:input, [:numbers, [:result, [[1], [:val, p(:size?, 3)]]]]]
|
337
337
|
)
|
338
338
|
|
339
|
-
expect(msg).to eql(
|
339
|
+
expect(msg).to eql('size must be 3')
|
340
340
|
end
|
341
341
|
|
342
342
|
it 'returns valid message when val is array and arg is range' do
|
@@ -344,7 +344,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
344
344
|
[:input, [:numbers, [:result, [[1], [:val, p(:size?, 3..4)]]]]]
|
345
345
|
)
|
346
346
|
|
347
|
-
expect(msg).to eql(
|
347
|
+
expect(msg).to eql('size must be within 3 - 4')
|
348
348
|
end
|
349
349
|
|
350
350
|
it 'returns valid message when arg is int' do
|
@@ -352,7 +352,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
352
352
|
[:input, [:num, [:result, ['ab', [:val, p(:size?, 3)]]]]]
|
353
353
|
)
|
354
354
|
|
355
|
-
expect(msg).to eql(
|
355
|
+
expect(msg).to eql('length must be 3')
|
356
356
|
end
|
357
357
|
|
358
358
|
it 'returns valid message when arg is range' do
|
@@ -360,7 +360,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
360
360
|
[:input, [:num, [:result, ['ab', [:val, p(:size?, 3..4)]]]]]
|
361
361
|
)
|
362
362
|
|
363
|
-
expect(msg).to eql(
|
363
|
+
expect(msg).to eql('length must be within 3 - 4')
|
364
364
|
end
|
365
365
|
end
|
366
366
|
|
@@ -370,7 +370,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
370
370
|
[:input, [:num, [:result, [3, [:val, p(:str?)]]]]]
|
371
371
|
)
|
372
372
|
|
373
|
-
expect(msg).to eql(
|
373
|
+
expect(msg).to eql('must be a string')
|
374
374
|
end
|
375
375
|
end
|
376
376
|
|
@@ -380,7 +380,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
380
380
|
[:input, [:num, [:result, [3, [:val, p(:bool?)]]]]]
|
381
381
|
)
|
382
382
|
|
383
|
-
expect(msg).to eql(
|
383
|
+
expect(msg).to eql('must be boolean')
|
384
384
|
end
|
385
385
|
end
|
386
386
|
|
@@ -390,7 +390,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
390
390
|
[:input, [:str, [:result, ['Bar', [:val, p(:format?, /^F/)]]]]]
|
391
391
|
)
|
392
392
|
|
393
|
-
expect(msg).to eql(
|
393
|
+
expect(msg).to eql('is in invalid format')
|
394
394
|
end
|
395
395
|
end
|
396
396
|
|
@@ -400,7 +400,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
400
400
|
[:input, [:str, [:result, ["not a number", [:val, p(:number?)]]]]]
|
401
401
|
)
|
402
402
|
|
403
|
-
expect(msg).to eql(
|
403
|
+
expect(msg).to eql('must be a number')
|
404
404
|
end
|
405
405
|
end
|
406
406
|
|
@@ -410,7 +410,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
410
410
|
[:input, [:str, [:result, [1, [:val, p(:odd?)]]]]]
|
411
411
|
)
|
412
412
|
|
413
|
-
expect(msg).to eql(
|
413
|
+
expect(msg).to eql('must be odd')
|
414
414
|
end
|
415
415
|
end
|
416
416
|
|
@@ -420,7 +420,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
420
420
|
[:input, [:str, [:result, [2, [:val, p(:even?)]]]]]
|
421
421
|
)
|
422
422
|
|
423
|
-
expect(msg).to eql(
|
423
|
+
expect(msg).to eql('must be even')
|
424
424
|
end
|
425
425
|
end
|
426
426
|
|
@@ -430,7 +430,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
430
430
|
[:input, [:str, [:result, ['Foo', [:val, p(:eql?, 'Bar')]]]]]
|
431
431
|
)
|
432
432
|
|
433
|
-
expect(msg).to eql(
|
433
|
+
expect(msg).to eql('must be equal to Bar')
|
434
434
|
end
|
435
435
|
end
|
436
436
|
|
@@ -440,17 +440,17 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
440
440
|
[:input, [:str, [:result, ['Foo', [:val, p(:not_eql?, 'Foo')]]]]]
|
441
441
|
)
|
442
442
|
|
443
|
-
expect(msg).to eql(
|
443
|
+
expect(msg).to eql('must not be equal to Foo')
|
444
444
|
end
|
445
445
|
end
|
446
446
|
|
447
|
-
describe ':type
|
447
|
+
describe ':type?' do
|
448
448
|
it 'returns valid message' do
|
449
449
|
msg = error_compiler.visit(
|
450
450
|
[:input, [:age, [:result, ['1', [:val, p(:type?, Integer)]]]]]
|
451
451
|
)
|
452
452
|
|
453
|
-
expect(msg).to eql(
|
453
|
+
expect(msg).to eql('must be Integer')
|
454
454
|
end
|
455
455
|
end
|
456
456
|
end
|