dry-validation 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/CHANGELOG.md +39 -1
  4. data/benchmarks/benchmark_schema_invalid_huge.rb +52 -0
  5. data/benchmarks/profile_schema_huge_invalid.rb +30 -0
  6. data/config/errors.yml +3 -2
  7. data/dry-validation.gemspec +2 -2
  8. data/lib/dry/validation.rb +20 -32
  9. data/lib/dry/validation/constants.rb +6 -0
  10. data/lib/dry/validation/error.rb +5 -2
  11. data/lib/dry/validation/error_compiler.rb +46 -116
  12. data/lib/dry/validation/executor.rb +105 -0
  13. data/lib/dry/validation/hint_compiler.rb +36 -68
  14. data/lib/dry/validation/message.rb +86 -0
  15. data/lib/dry/validation/message_compiler.rb +141 -0
  16. data/lib/dry/validation/message_set.rb +70 -0
  17. data/lib/dry/validation/messages/abstract.rb +1 -1
  18. data/lib/dry/validation/messages/i18n.rb +5 -0
  19. data/lib/dry/validation/predicate_registry.rb +8 -3
  20. data/lib/dry/validation/result.rb +6 -7
  21. data/lib/dry/validation/schema.rb +21 -227
  22. data/lib/dry/validation/schema/check.rb +1 -1
  23. data/lib/dry/validation/schema/class_interface.rb +193 -0
  24. data/lib/dry/validation/schema/deprecated.rb +1 -2
  25. data/lib/dry/validation/schema/key.rb +4 -0
  26. data/lib/dry/validation/schema/value.rb +12 -7
  27. data/lib/dry/validation/schema_compiler.rb +20 -1
  28. data/lib/dry/validation/type_specs.rb +70 -0
  29. data/lib/dry/validation/version.rb +1 -1
  30. data/spec/fixtures/locales/pl.yml +1 -1
  31. data/spec/integration/custom_predicates_spec.rb +37 -0
  32. data/spec/integration/error_compiler_spec.rb +39 -39
  33. data/spec/integration/form/predicates/key_spec.rb +10 -18
  34. data/spec/integration/form/predicates/size/fixed_spec.rb +8 -12
  35. data/spec/integration/form/predicates/size/range_spec.rb +7 -7
  36. data/spec/integration/hints_spec.rb +17 -0
  37. data/spec/integration/messages/i18n_spec.rb +2 -2
  38. data/spec/integration/schema/check_rules_spec.rb +2 -2
  39. data/spec/integration/schema/defining_base_schema_spec.rb +38 -0
  40. data/spec/integration/schema/dynamic_predicate_args_spec.rb +18 -0
  41. data/spec/integration/schema/macros/each_spec.rb +2 -2
  42. data/spec/integration/schema/macros/input_spec.rb +102 -10
  43. data/spec/integration/schema/macros/maybe_spec.rb +30 -0
  44. data/spec/integration/schema/nested_schemas_spec.rb +200 -0
  45. data/spec/integration/schema/nested_values_spec.rb +3 -1
  46. data/spec/integration/schema/option_with_default_spec.rb +54 -20
  47. data/spec/integration/schema/predicates/size/fixed_spec.rb +10 -10
  48. data/spec/integration/schema/predicates/size/range_spec.rb +8 -10
  49. data/spec/unit/error_compiler_spec.rb +1 -1
  50. data/spec/unit/hint_compiler_spec.rb +2 -2
  51. metadata +18 -7
  52. data/examples/rule_ast.rb +0 -25
  53. 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
- lookup_type("hash", config.input_processor)
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(type)
22
- schema_class.config.input = type
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
- klass = ::Class.new(schema_class, &block)
119
- @schema_class = klass
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.new
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
@@ -1,5 +1,5 @@
1
1
  module Dry
2
2
  module Validation
3
- VERSION = '0.8.0'.freeze
3
+ VERSION = '0.9.0'.freeze
4
4
  end
5
5
  end
@@ -4,7 +4,7 @@ pl:
4
4
  size?:
5
5
  arg:
6
6
  default: "wielkość musi być równa %{size}"
7
- range: "wielkość musi być między %{left} a %{right}"
7
+ range: "wielkość musi być między %{size_left} a %{size_right}"
8
8
  rules:
9
9
  email:
10
10
  filled?: "Proszę podać adres email"
@@ -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(num: ['num must be an integer'])
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(email: ['adres email nie jest poprawny'])
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(tags: ['must be empty'])
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(num: ['must not be one of: 1, 2, 3'])
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(array: ['must not include 2'])
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(num: ['must be one of: 1, 2, 3'])
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(num: ['must include 2'])
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(num: ['must be greater than 3'])
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(num: ['must be greater than or equal to 3'])
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(num: ['must be less than 3'])
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(num: ['must be less than or equal to 3'])
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(address: ['must be a hash'])
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(phone_numbers: ['must be an array'])
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(num: ['must be an integer'])
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(num: ['must be a float'])
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(num: ['must be a decimal'])
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(num: ['must be a date'])
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(num: ['must be a date time'])
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(num: ['must be a time'])
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(num: ['size cannot be greater than 3'])
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(num: ['size cannot be less than 3'])
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(num: ['cannot be defined'])
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(numbers: ['size must be 3'])
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(numbers: ['size must be within 3 - 4'])
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(num: ['length must be 3'])
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(num: ['length must be within 3 - 4'])
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(num: ['must be a string'])
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(num: ['must be boolean'])
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(str: ['is in invalid format'])
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(str: ['must be a number'])
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(str: ['must be odd'])
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(str: ['must be even'])
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(str: ['must be equal to Bar'])
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(str: ['must not be equal to Foo'])
443
+ expect(msg).to eql('must not be equal to Foo')
444
444
  end
445
445
  end
446
446
 
447
- describe ':type??' do
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(age: ['must be Integer'])
453
+ expect(msg).to eql('must be Integer')
454
454
  end
455
455
  end
456
456
  end