dry-validation 0.9.2 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 671e0b5c9aeccb3851d4d654e82be0d2cc0dc23c
4
- data.tar.gz: d61862295923a7a6e4cf1dacf733606bf648c9b8
3
+ metadata.gz: d65d8862d070db6d2641c69e7077b4d2b9a97ee9
4
+ data.tar.gz: 25ce604442b2886454a39e13180b52dfc498598c
5
5
  SHA512:
6
- metadata.gz: 0d9e4a6203a141ac29630a0a38dd4c29363dd7acd094f4dba55321729d6b036275111bcaae09e1764a5df4ac41c2dfe70c26b73606307c910e6c7eb56981c165
7
- data.tar.gz: 4439a78095b19e8e1464395eccb49bd17394fb06f9d245e7bc7576fe8c9586067f90c3b46132db56ea967f0f87abcd16425963276fc4841c9b302e7ce8a6d340
6
+ metadata.gz: 1ff29e904cf720763e48e4e329a85e81695e9e2a17b093f1581ce46b295f4abf1116b216abb7f8ca9703b0ccd86473591dfa38d41d92b8da621c9abc729a3447
7
+ data.tar.gz: 97169242904d4bd1c05efc41ef54021b10a304b2bd92ff074632a37053e81ccaab415d228c8391fb916acd73768029cf5adc065c7c29d7eb6e0e47870d1c4830
data/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ # v0.9.3 2016-07-22
2
+
3
+ ### Added
4
+
5
+ * Support for range arg in error messages for `excluded_from?` and `included_in?` (mrbongiolo)
6
+ * `Result#message_set` returns raw message set object (solnic)
7
+
8
+ ### Fixed
9
+
10
+ * Error messages for high-level rules in nested schemas are nested correctly (solnic)
11
+ * Dumping error messages works with high-level rules relying on the same value (solnic)
12
+
13
+ ### Changed
14
+
15
+ * `#messages` is no longer memoized (solnic)
16
+
17
+ [Compare v0.9.2...v0.9.3](https://github.com/dryrb/dry-validation/compare/v0.9.2...v0.9.3)
18
+
1
19
  # v0.9.2 2016-07-13
2
20
 
3
21
  ### Fixed
data/Gemfile CHANGED
@@ -10,6 +10,11 @@ end
10
10
  group :tools do
11
11
  gem 'byebug', platform: :mri
12
12
  gem 'pry'
13
+
14
+ unless ENV['TRAVIS']
15
+ gem 'mutant', github: 'mbj/mutant'
16
+ gem 'mutant-rspec', github: 'mbj/mutant'
17
+ end
13
18
  end
14
19
 
15
20
  group :benchmarks do
data/config/errors.yml CHANGED
@@ -6,7 +6,10 @@ en:
6
6
 
7
7
  excludes?: "must not include %{value}"
8
8
 
9
- excluded_from?: "must not be one of: %{list}"
9
+ excluded_from?:
10
+ arg:
11
+ default: "must not be one of: %{list}"
12
+ range: "must not be one of: %{list_left} - %{list_right}"
10
13
  exclusion?: "must not be one of: %{list}"
11
14
 
12
15
  eql?: "must be equal to %{left}"
@@ -29,7 +32,10 @@ en:
29
32
 
30
33
  hash?: "must be a hash"
31
34
 
32
- included_in?: "must be one of: %{list}"
35
+ included_in?:
36
+ arg:
37
+ default: "must be one of: %{list}"
38
+ range: "must be one of: %{list_left} - %{list_right}"
33
39
  inclusion?: "must be one of: %{list}"
34
40
 
35
41
  includes?: "must include %{value}"
@@ -23,7 +23,7 @@ module Dry
23
23
 
24
24
  path.compact!
25
25
 
26
- template = messages[rule, default_lookup_options]
26
+ template = messages[rule.is_a?(Array) ? rule.last : rule, default_lookup_options]
27
27
 
28
28
  if template
29
29
  predicate, args, tokens = visit(error, opts.merge(path: path, message: false))
@@ -60,13 +60,18 @@ module Dry
60
60
  opts[:path] << path.last
61
61
  visit(other, opts)
62
62
  else
63
- visit(other, opts.merge(path: [path]))
63
+ visit(other, opts.merge(path: [path], schema: true))
64
64
  end
65
65
  end
66
66
 
67
67
  def visit_check(node, opts = EMPTY_HASH)
68
- path, other = node
69
- visit(other, opts.merge(path: Array(path)))
68
+ name, other = node
69
+
70
+ if opts[:schema]
71
+ visit(other, opts)
72
+ else
73
+ visit(other, opts.merge(path: Array(name)))
74
+ end
70
75
  end
71
76
 
72
77
  def lookup_options(opts, arg_vals = [])
@@ -18,36 +18,57 @@ module Dry
18
18
  initialize_placeholders!
19
19
  end
20
20
 
21
+ def dump
22
+ root? ? to_a : to_h
23
+ end
24
+
21
25
  def empty?
22
26
  messages.empty?
23
27
  end
24
28
 
29
+ def root?
30
+ !empty? && messages.all?(&:root?)
31
+ end
32
+
25
33
  def each(&block)
26
34
  return to_enum unless block
27
35
  messages.each(&block)
28
36
  end
29
37
 
30
38
  def with_hints!(hints)
31
- @hints = hints.group_by(&:index_path)
32
- freeze
39
+ @hints.update(hints.group_by(&:index_path))
40
+ self
33
41
  end
34
42
 
35
43
  def to_h
36
- reduce(placeholders) do |hash, msg|
37
- if msg.root?
38
- (hash[nil] ||= []) << msg.to_s
39
- else
40
- node = msg.path.reduce(hash) { |a, e| a[e] }
41
- node << msg
42
- node.concat(Array(hints[msg.index_path]))
43
- node.uniq!(&:signature)
44
+ if root?
45
+ { nil => map(&:to_s) }
46
+ else
47
+ group_by(&:path).reduce(placeholders) do |hash, (path, msgs)|
48
+ node = path.reduce(hash) { |a, e| a[e] }
49
+
50
+ msgs.each do |msg|
51
+ node << msg
52
+ msg_hints = hints[msg.index_path]
53
+
54
+ if msg_hints
55
+ node.concat(msg_hints)
56
+ node.uniq!(&:signature)
57
+ end
58
+ end
59
+
44
60
  node.map!(&:to_s)
61
+
62
+ hash
45
63
  end
46
- hash
47
64
  end
48
65
  end
49
66
  alias_method :to_hash, :to_h
50
67
 
68
+ def to_a
69
+ to_h.values.flatten
70
+ end
71
+
51
72
  private
52
73
 
53
74
  def initialize_placeholders!
@@ -1,3 +1,5 @@
1
+ require 'dry/validation/constants'
2
+
1
3
  module Dry
2
4
  module Validation
3
5
  class Result
@@ -12,13 +14,12 @@ module Dry
12
14
  alias_method :to_hash, :output
13
15
  alias_method :to_h, :output # for MRI 2.0, remove it when drop support
14
16
 
15
- EMPTY_MESSAGES = {}.freeze
16
-
17
17
  def initialize(output, errors, error_compiler, hint_compiler)
18
18
  @output = output
19
19
  @errors = errors
20
20
  @error_compiler = error_compiler
21
21
  @hint_compiler = hint_compiler
22
+ @messages = EMPTY_HASH if success?
22
23
  end
23
24
 
24
25
  def each(&block)
@@ -37,22 +38,14 @@ module Dry
37
38
  !success?
38
39
  end
39
40
 
40
- def messages(options = {})
41
- @messages ||=
42
- begin
43
- return EMPTY_MESSAGES if success?
44
- hints = hint_compiler.with(options).call
45
- msg_set = error_compiler.with(options).(error_ast).with_hints!(hints)
46
-
47
- as_hash = options.fetch(:as_hash, true)
41
+ def messages(options = EMPTY_HASH)
42
+ message_set(options).dump
43
+ end
48
44
 
49
- if as_hash
50
- hash = msg_set.to_h
51
- hash.key?(nil) ? hash.values.flatten : hash
52
- else
53
- msg_set
54
- end
55
- end
45
+ def message_set(options = EMPTY_HASH)
46
+ error_compiler
47
+ .with(options).(error_ast)
48
+ .with_hints!(hint_compiler.with(options).())
56
49
  end
57
50
 
58
51
  def to_ast
@@ -62,7 +55,7 @@ module Dry
62
55
  private
63
56
 
64
57
  def error_ast
65
- errors.map { |error| error.to_ast }
58
+ @error_ast ||= errors.map { |error| error.to_ast }
66
59
  end
67
60
  end
68
61
  end
@@ -48,7 +48,7 @@ module Dry
48
48
  target = dsl.schema_class
49
49
 
50
50
  if config.input
51
- config.input_rule = dsl.__send__(:infer_predicates, Array(target.config.input))
51
+ config.input_rule = dsl.infer_predicates(Array(target.config.input))
52
52
  end
53
53
 
54
54
  rules = target.config.rules + (options.fetch(:rules, []) + dsl.rules)
@@ -171,8 +171,6 @@ module Dry
171
171
  self.class.public_methods.include?(name)
172
172
  end
173
173
 
174
- private
175
-
176
174
  def infer_predicates(predicates, infer_on = self)
177
175
  predicates.map { |predicate|
178
176
  name, *args = ::Kernel.Array(predicate).first
@@ -187,6 +185,8 @@ module Dry
187
185
  }.reduce(:and)
188
186
  end
189
187
 
188
+ private
189
+
190
190
  def method_missing(meth, *args, &block)
191
191
  return schema_class.instance_method(meth) if dyn_arg?(meth)
192
192
 
@@ -1,5 +1,5 @@
1
1
  module Dry
2
2
  module Validation
3
- VERSION = '0.9.2'.freeze
3
+ VERSION = '0.9.3'.freeze
4
4
  end
5
5
  end
@@ -0,0 +1,59 @@
1
+ RSpec.describe Dry::Validation::Result do
2
+ subject(:result) { schema.(input) }
3
+
4
+ let(:schema) { Dry::Validation.Schema { required(:name).filled(:str?) } }
5
+
6
+ context 'with valid input' do
7
+ let(:input) { { name: 'Jane' } }
8
+
9
+ it 'is successful' do
10
+ expect(result).to be_successful
11
+ end
12
+
13
+ it 'is not a failure' do
14
+ expect(result).to_not be_failure
15
+ end
16
+
17
+ it 'coerces to validated hash' do
18
+ expect(Hash(result)).to eql(name: 'Jane')
19
+ end
20
+
21
+ describe '#messages' do
22
+ it 'returns an empty hash' do
23
+ expect(result.messages).to be_empty
24
+ end
25
+ end
26
+ end
27
+
28
+ context 'with invalid input' do
29
+ let(:input) { { name: '' } }
30
+
31
+ it 'is not successful' do
32
+ expect(result).to_not be_successful
33
+ end
34
+
35
+ it 'is failure' do
36
+ expect(result).to be_failure
37
+ end
38
+
39
+ it 'coerces to validated hash' do
40
+ expect(Hash(result)).to eql(name: '')
41
+ end
42
+
43
+ describe '#messages' do
44
+ it 'returns a hash with error messages' do
45
+ expect(result.messages).to eql(name: ['must be filled'])
46
+ end
47
+
48
+ it 'with full: true returns full messages' do
49
+ expect(result.messages(full: true)).to eql(name: ['name must be filled'])
50
+ end
51
+ end
52
+
53
+ describe '#message_set' do
54
+ it 'returns message set' do
55
+ expect(result.message_set.to_h).to eql(name: ['must be filled'])
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,74 @@
1
+ RSpec.describe 'Macros / rule' do
2
+ shared_context 'password confirmation high-level rule' do
3
+ subject(:schema) { schema_class.new }
4
+
5
+ let(:schema_class) do
6
+ Dry::Validation.Schema(build: false) do
7
+ required(:user).schema do
8
+ required(:password).filled
9
+ required(:password_confirmation).filled
10
+
11
+ rule(password_confirmation: %i[password_confirmation password]) do |pc, p|
12
+ pc.eql?(p)
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ it 'passes when input is valid' do
19
+ expect(schema.(user: { password: 'foo', password_confirmation: 'foo' })).to be_successful
20
+ end
21
+
22
+ it 'fails when the rule failed' do
23
+ expect(schema.(user: { password: 'foo', password_confirmation: 'bar' }).messages).to eql(
24
+ user: { password_confirmation: [error_message] }
25
+ )
26
+ end
27
+ end
28
+
29
+ context 'with the default message' do
30
+ let(:error_message) { 'must be equal to foo' }
31
+
32
+ include_context 'password confirmation high-level rule'
33
+ end
34
+
35
+ context 'with a custom message' do
36
+ let(:error_message) { 'does not match' }
37
+
38
+ before do
39
+ schema_class.class_eval do
40
+ def self.messages
41
+ default_messages.merge(en: { errors: { password_confirmation: 'does not match' } })
42
+ end
43
+ end
44
+ end
45
+
46
+ include_context 'password confirmation high-level rule'
47
+ end
48
+
49
+ context 'with two rules relying on the same value' do
50
+ subject(:schema) do
51
+ Dry::Validation.Schema do
52
+ required(:x).filled(:int?)
53
+
54
+ rule(a: [:x]) do |x|
55
+ x.gt?(3)
56
+ end
57
+
58
+ rule(b: [:x]) do |x|
59
+ x.gt?(5)
60
+ end
61
+ end
62
+ end
63
+
64
+ it 'passes when input is valid' do
65
+ expect(schema.(x: 6)).to be_successful
66
+ end
67
+
68
+ it 'fails when rules failed' do
69
+ expect(schema.(x: 2).messages).to eql(
70
+ x: ['must be greater than 3', 'must be greater than 5']
71
+ )
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,459 @@
1
+ RSpec.describe 'Predicates: Excluded From' do
2
+
3
+ context "Array" do
4
+
5
+ context 'with required' do
6
+ subject(:schema) do
7
+ Dry::Validation.Schema do
8
+ required(:foo) { excluded_from?([1, 3, 5]) }
9
+ end
10
+ end
11
+
12
+ context 'with valid input' do
13
+ let(:input) { { foo: 2 } }
14
+
15
+ it 'is successful' do
16
+ expect(result).to be_successful
17
+ end
18
+ end
19
+
20
+ context 'with missing input' do
21
+ let(:input) { {} }
22
+
23
+ it 'is not successful' do
24
+ expect(result).to be_failing ['is missing', 'must not be one of: 1, 3, 5']
25
+ end
26
+ end
27
+
28
+ context 'with nil input' do
29
+ let(:input) { { foo: nil } }
30
+
31
+ it 'is successful' do
32
+ expect(result).to be_successful
33
+ end
34
+ end
35
+
36
+ context 'with blank input' do
37
+ let(:input) { { foo: '' } }
38
+
39
+ it 'is successful' do
40
+ expect(result).to be_successful
41
+ end
42
+ end
43
+
44
+ context 'with invalid type' do
45
+ let(:input) { { foo: { a: 1 } } }
46
+
47
+ it 'is successful' do
48
+ expect(result).to be_successful
49
+ end
50
+ end
51
+
52
+ context 'with invalid input' do
53
+ let(:input) { { foo: 5 } }
54
+
55
+ it 'is not successful' do
56
+ expect(result).to be_failing ['must not be one of: 1, 3, 5']
57
+ end
58
+ end
59
+ end
60
+
61
+ context 'with optional' do
62
+ subject(:schema) do
63
+ Dry::Validation.Schema do
64
+ optional(:foo) { excluded_from?([1, 3, 5]) }
65
+ end
66
+ end
67
+
68
+ context 'with valid input' do
69
+ let(:input) { { foo: 2 } }
70
+
71
+ it 'is successful' do
72
+ expect(result).to be_successful
73
+ end
74
+ end
75
+
76
+ context 'with missing input' do
77
+ let(:input) { {} }
78
+
79
+ it 'is successful' do
80
+ expect(result).to be_successful
81
+ end
82
+ end
83
+
84
+ context 'with nil input' do
85
+ let(:input) { { foo: nil } }
86
+
87
+ it 'is successful' do
88
+ expect(result).to be_successful
89
+ end
90
+ end
91
+
92
+ context 'with blank input' do
93
+ let(:input) { { foo: '' } }
94
+
95
+ it 'is successful' do
96
+ expect(result).to be_successful
97
+ end
98
+ end
99
+
100
+ context 'with invalid type' do
101
+ let(:input) { { foo: { a: 1 } } }
102
+
103
+ it 'is successful' do
104
+ expect(result).to be_successful
105
+ end
106
+ end
107
+
108
+ context 'with invalid input' do
109
+ let(:input) { { foo: 5 } }
110
+
111
+ it 'is not successful' do
112
+ expect(result).to be_failing ['must not be one of: 1, 3, 5']
113
+ end
114
+ end
115
+ end
116
+
117
+ context 'as macro' do
118
+ context 'with required' do
119
+ context 'with value' do
120
+ subject(:schema) do
121
+ Dry::Validation.Schema do
122
+ required(:foo).value(excluded_from?: [1, 3, 5])
123
+ end
124
+ end
125
+
126
+ context 'with valid input' do
127
+ let(:input) { { foo: 2 } }
128
+
129
+ it 'is successful' do
130
+ expect(result).to be_successful
131
+ end
132
+ end
133
+
134
+ context 'with missing input' do
135
+ let(:input) { {} }
136
+
137
+ it 'is not successful' do
138
+ expect(result).to be_failing ['is missing', 'must not be one of: 1, 3, 5']
139
+ end
140
+ end
141
+
142
+ context 'with nil input' do
143
+ let(:input) { { foo: nil } }
144
+
145
+ it 'is successful' do
146
+ expect(result).to be_successful
147
+ end
148
+ end
149
+
150
+ context 'with blank input' do
151
+ let(:input) { { foo: '' } }
152
+
153
+ it 'is successful' do
154
+ expect(result).to be_successful
155
+ end
156
+ end
157
+
158
+ context 'with invalid type' do
159
+ let(:input) { { foo: { a: 1 } } }
160
+
161
+ it 'is successful' do
162
+ expect(result).to be_successful
163
+ end
164
+ end
165
+
166
+ context 'with invalid input' do
167
+ let(:input) { { foo: 5 } }
168
+
169
+ it 'is not successful' do
170
+ expect(result).to be_failing ['must not be one of: 1, 3, 5']
171
+ end
172
+ end
173
+ end
174
+
175
+ context 'with filled' do
176
+ subject(:schema) do
177
+ Dry::Validation.Schema do
178
+ required(:foo).filled(excluded_from?: [1, 3, 5])
179
+ end
180
+ end
181
+
182
+ context 'with valid input' do
183
+ let(:input) { { foo: 2 } }
184
+
185
+ it 'is successful' do
186
+ expect(result).to be_successful
187
+ end
188
+ end
189
+
190
+ context 'with missing input' do
191
+ let(:input) { {} }
192
+
193
+ it 'is not successful' do
194
+ expect(result).to be_failing ['is missing', 'must not be one of: 1, 3, 5']
195
+ end
196
+ end
197
+
198
+ context 'with nil input' do
199
+ let(:input) { { foo: nil } }
200
+
201
+ it 'is not successful' do
202
+ expect(result).to be_failing ['must be filled', 'must not be one of: 1, 3, 5']
203
+ end
204
+ end
205
+
206
+ context 'with blank input' do
207
+ let(:input) { { foo: '' } }
208
+
209
+ it 'is not successful' do
210
+ expect(result).to be_failing ['must be filled', 'must not be one of: 1, 3, 5']
211
+ end
212
+ end
213
+
214
+ context 'with invalid type' do
215
+ let(:input) { { foo: { a: 1 } } }
216
+
217
+ it 'is successful' do
218
+ expect(result).to be_successful
219
+ end
220
+ end
221
+
222
+ context 'with invalid input' do
223
+ let(:input) { { foo: 5 } }
224
+
225
+ it 'is not successful' do
226
+ expect(result).to be_failing ['must not be one of: 1, 3, 5']
227
+ end
228
+ end
229
+ end
230
+
231
+ context 'with maybe' do
232
+ subject(:schema) do
233
+ Dry::Validation.Schema do
234
+ required(:foo).maybe(excluded_from?: [1, 3, 5])
235
+ end
236
+ end
237
+
238
+ context 'with valid input' do
239
+ let(:input) { { foo: 2 } }
240
+
241
+ it 'is successful' do
242
+ expect(result).to be_successful
243
+ end
244
+ end
245
+
246
+ context 'with missing input' do
247
+ let(:input) { {} }
248
+
249
+ it 'is not successful' do
250
+ expect(result).to be_failing ['is missing', 'must not be one of: 1, 3, 5']
251
+ end
252
+ end
253
+
254
+ context 'with nil input' do
255
+ let(:input) { { foo: nil } }
256
+
257
+ it 'is successful' do
258
+ expect(result).to be_successful
259
+ end
260
+ end
261
+
262
+ context 'with blank input' do
263
+ let(:input) { { foo: '' } }
264
+
265
+ it 'is successful' do
266
+ expect(result).to be_successful
267
+ end
268
+ end
269
+
270
+ context 'with invalid type' do
271
+ let(:input) { { foo: { a: 1 } } }
272
+
273
+ it 'is successful' do
274
+ expect(result).to be_successful
275
+ end
276
+ end
277
+
278
+ context 'with invalid input' do
279
+ let(:input) { { foo: 5 } }
280
+
281
+ it 'is not successful' do
282
+ expect(result).to be_failing ['must not be one of: 1, 3, 5']
283
+ end
284
+ end
285
+ end
286
+ end
287
+
288
+ context 'with optional' do
289
+ context 'with value' do
290
+ subject(:schema) do
291
+ Dry::Validation.Schema do
292
+ optional(:foo).value(excluded_from?: [1, 3, 5])
293
+ end
294
+ end
295
+
296
+ context 'with valid input' do
297
+ let(:input) { { foo: 2 } }
298
+
299
+ it 'is successful' do
300
+ expect(result).to be_successful
301
+ end
302
+ end
303
+
304
+ context 'with missing input' do
305
+ let(:input) { {} }
306
+
307
+ it 'is successful' do
308
+ expect(result).to be_successful
309
+ end
310
+ end
311
+
312
+ context 'with nil input' do
313
+ let(:input) { { foo: nil } }
314
+
315
+ it 'is successful' do
316
+ expect(result).to be_successful
317
+ end
318
+ end
319
+
320
+ context 'with blank input' do
321
+ let(:input) { { foo: '' } }
322
+
323
+ it 'is successful' do
324
+ expect(result).to be_successful
325
+ end
326
+ end
327
+
328
+ context 'with invalid type' do
329
+ let(:input) { { foo: { a: 1 } } }
330
+
331
+ it 'is successful' do
332
+ expect(result).to be_successful
333
+ end
334
+ end
335
+
336
+ context 'with invalid input' do
337
+ let(:input) { { foo: 5 } }
338
+
339
+ it 'is not successful' do
340
+ expect(result).to be_failing ['must not be one of: 1, 3, 5']
341
+ end
342
+ end
343
+ end
344
+
345
+ context 'with filled' do
346
+ subject(:schema) do
347
+ Dry::Validation.Schema do
348
+ optional(:foo).filled(excluded_from?: [1, 3, 5])
349
+ end
350
+ end
351
+
352
+ context 'with valid input' do
353
+ let(:input) { { foo: 2 } }
354
+
355
+ it 'is successful' do
356
+ expect(result).to be_successful
357
+ end
358
+ end
359
+
360
+ context 'with missing input' do
361
+ let(:input) { {} }
362
+
363
+ it 'is successful' do
364
+ expect(result).to be_successful
365
+ end
366
+ end
367
+
368
+ context 'with nil input' do
369
+ let(:input) { { foo: nil } }
370
+
371
+ it 'is not successful' do
372
+ expect(result).to be_failing ['must be filled', 'must not be one of: 1, 3, 5']
373
+ end
374
+ end
375
+
376
+ context 'with blank input' do
377
+ let(:input) { { foo: '' } }
378
+
379
+ it 'is not successful' do
380
+ expect(result).to be_failing ['must be filled', 'must not be one of: 1, 3, 5']
381
+ end
382
+ end
383
+
384
+ context 'with invalid type' do
385
+ let(:input) { { foo: { a: 1 } } }
386
+
387
+ it 'is successful' do
388
+ expect(result).to be_successful
389
+ end
390
+ end
391
+
392
+ context 'with invalid input' do
393
+ let(:input) { { foo: 5 } }
394
+
395
+ it 'is not successful' do
396
+ expect(result).to be_failing ['must not be one of: 1, 3, 5']
397
+ end
398
+ end
399
+ end
400
+
401
+ context 'with maybe' do
402
+ subject(:schema) do
403
+ Dry::Validation.Schema do
404
+ optional(:foo).maybe(excluded_from?: [1, 3, 5])
405
+ end
406
+ end
407
+
408
+ context 'with valid input' do
409
+ let(:input) { { foo: 2 } }
410
+
411
+ it 'is successful' do
412
+ expect(result).to be_successful
413
+ end
414
+ end
415
+
416
+ context 'with missing input' do
417
+ let(:input) { {} }
418
+
419
+ it 'is successful' do
420
+ expect(result).to be_successful
421
+ end
422
+ end
423
+
424
+ context 'with nil input' do
425
+ let(:input) { { foo: nil } }
426
+
427
+ it 'is successful' do
428
+ expect(result).to be_successful
429
+ end
430
+ end
431
+
432
+ context 'with blank input' do
433
+ let(:input) { { foo: '' } }
434
+
435
+ it 'is successful' do
436
+ expect(result).to be_successful
437
+ end
438
+ end
439
+
440
+ context 'with invalid type' do
441
+ let(:input) { { foo: { a: 1 } } }
442
+
443
+ it 'is successful' do
444
+ expect(result).to be_successful
445
+ end
446
+ end
447
+
448
+ context 'with invalid input' do
449
+ let(:input) { { foo: 5 } }
450
+
451
+ it 'is not successful' do
452
+ expect(result).to be_failing ['must not be one of: 1, 3, 5']
453
+ end
454
+ end
455
+ end
456
+ end
457
+ end
458
+ end
459
+ end