dry-validation 0.9.2 → 0.9.3

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 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