service_actor 3.3.0 → 3.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +145 -105
- data/lib/service_actor/argument_error.rb +2 -4
- data/lib/service_actor/attributable.rb +39 -41
- data/lib/service_actor/base.rb +15 -33
- data/lib/service_actor/collectionable.rb +58 -22
- data/lib/service_actor/conditionable.rb +63 -28
- data/lib/service_actor/core.rb +52 -45
- data/lib/service_actor/defaultable.rb +65 -26
- data/lib/service_actor/error.rb +2 -4
- data/lib/service_actor/failable.rb +27 -29
- data/lib/service_actor/failure.rb +8 -10
- data/lib/service_actor/nil_checkable.rb +73 -31
- data/lib/service_actor/playable.rb +62 -64
- data/lib/service_actor/raisable.rb +20 -0
- data/lib/service_actor/result.rb +50 -52
- data/lib/service_actor/support/loader.rb +16 -0
- data/lib/service_actor/type_checkable.rb +75 -35
- data/lib/service_actor/version.rb +1 -1
- metadata +32 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ccd0af6d1704b2efc805ce3bfa5cdfd1e74797c9b0a015511daf351e692ab6e8
|
4
|
+
data.tar.gz: 6fa64250a89f505bfc0b9b2e300faf5c3ae222740317263fe4947a383e1b64c8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a3041c28efd75c40e793e299432b743274803b7a237a22525a05de3b916b17f899c6de85a0eeeb98594de47f8d742d595c7582ecb505700a3ec9712f842d1c87
|
7
|
+
data.tar.gz: 2f6e0ece1cc4964311a25035dba7011844b1d3992d7ec14f9a36a497b6b62f361ba5826ee364e1871411dd3ce5fff144354a104c846f98f0bcfe971cd8b53030
|
data/README.md
CHANGED
@@ -15,17 +15,17 @@ and controllers thin.
|
|
15
15
|
- [Inputs](#inputs)
|
16
16
|
- [Outputs](#outputs)
|
17
17
|
- [Defaults](#defaults)
|
18
|
-
- [Conditions](#conditions)
|
19
18
|
- [Allow nil](#allow-nil)
|
19
|
+
- [Conditions](#conditions)
|
20
20
|
- [Types](#types)
|
21
21
|
- [Fail](#fail)
|
22
|
+
- [Custom input errors](#custom-input-errors)
|
22
23
|
- [Play actors in a sequence](#play-actors-in-a-sequence)
|
23
24
|
- [Rollback](#rollback)
|
24
25
|
- [Inline actors](#inline-actors)
|
25
26
|
- [Play conditions](#play-conditions)
|
26
27
|
- [Testing](#testing)
|
27
|
-
- [
|
28
|
-
- [Influences](#influences)
|
28
|
+
- [FAQ](#faq)
|
29
29
|
- [Thanks](#thanks)
|
30
30
|
- [Contributing](#contributing)
|
31
31
|
- [License](#contributing)
|
@@ -120,22 +120,12 @@ The result you get from calling an actor will include the outputs you set:
|
|
120
120
|
```rb
|
121
121
|
actor = BuildGreeting.call
|
122
122
|
actor.greeting # => "Have a wonderful day!"
|
123
|
-
|
124
|
-
|
125
|
-
For every output there is also a boolean method ending with `?` to test its
|
126
|
-
presence:
|
127
|
-
|
128
|
-
```rb
|
129
|
-
if actor.greeting?
|
130
|
-
puts "Greetings is truthy"
|
131
|
-
else
|
132
|
-
puts "Greetings is falsey"
|
133
|
-
end
|
123
|
+
actor.greeting? # => true
|
134
124
|
```
|
135
125
|
|
136
126
|
### Defaults
|
137
127
|
|
138
|
-
Inputs can be
|
128
|
+
Inputs can be optional by providing a default:
|
139
129
|
|
140
130
|
```rb
|
141
131
|
class BuildGreeting < Actor
|
@@ -149,29 +139,30 @@ class BuildGreeting < Actor
|
|
149
139
|
self.greeting = "Have a #{adjective} #{length_of_time} #{name}!"
|
150
140
|
end
|
151
141
|
end
|
152
|
-
```
|
153
|
-
|
154
|
-
This lets you call the actor without specifying those keys:
|
155
142
|
|
156
|
-
```rb
|
157
143
|
actor = BuildGreeting.call(name: "Jim")
|
158
144
|
actor.greeting # => "Have a wonderful week Jim!"
|
159
145
|
```
|
160
146
|
|
161
|
-
|
147
|
+
### Allow nil
|
148
|
+
|
149
|
+
By default inputs accept `nil` values. To raise an error instead:
|
162
150
|
|
163
151
|
```rb
|
164
|
-
|
165
|
-
|
152
|
+
class UpdateUser < Actor
|
153
|
+
input :user, allow_nil: false
|
154
|
+
|
155
|
+
# …
|
156
|
+
end
|
166
157
|
```
|
167
158
|
|
168
159
|
### Conditions
|
169
160
|
|
170
|
-
You can ensure an input is included in a collection by using `
|
161
|
+
You can ensure an input is included in a collection by using `inclusion`:
|
171
162
|
|
172
163
|
```rb
|
173
164
|
class Pay < Actor
|
174
|
-
input :currency,
|
165
|
+
input :currency, inclusion: %w[EUR USD]
|
175
166
|
|
176
167
|
# …
|
177
168
|
end
|
@@ -180,7 +171,7 @@ end
|
|
180
171
|
This raises an argument error if the input does not match one of the given
|
181
172
|
values.
|
182
173
|
|
183
|
-
|
174
|
+
Declare custom conditions with the name of your choice by using `must`:
|
184
175
|
|
185
176
|
```rb
|
186
177
|
class UpdateAdminUser < Actor
|
@@ -193,19 +184,8 @@ class UpdateAdminUser < Actor
|
|
193
184
|
end
|
194
185
|
```
|
195
186
|
|
196
|
-
This
|
197
|
-
|
198
|
-
### Allow nil
|
199
|
-
|
200
|
-
By default inputs accept `nil` values. To raise an error instead:
|
201
|
-
|
202
|
-
```rb
|
203
|
-
class UpdateUser < Actor
|
204
|
-
input :user, allow_nil: false
|
205
|
-
|
206
|
-
# …
|
207
|
-
end
|
208
|
-
```
|
187
|
+
This will raise an argument error if any of the given lambdas returns a falsey
|
188
|
+
value.
|
209
189
|
|
210
190
|
### Types
|
211
191
|
|
@@ -234,7 +214,7 @@ When using a type condition, `allow_nil` defaults to `false`.
|
|
234
214
|
To stop the execution and mark an actor as having failed, use `fail!`:
|
235
215
|
|
236
216
|
```rb
|
237
|
-
class UpdateUser
|
217
|
+
class UpdateUser < Actor
|
238
218
|
input :user
|
239
219
|
input :attributes
|
240
220
|
|
@@ -248,7 +228,8 @@ class UpdateUser
|
|
248
228
|
end
|
249
229
|
```
|
250
230
|
|
251
|
-
This will raise an error in your
|
231
|
+
This will raise an error in your application with the given data added to the
|
232
|
+
result.
|
252
233
|
|
253
234
|
To test for the success of your actor instead of raising an exception, use
|
254
235
|
`.result` instead of `.call`. You can then call `success?` or `failure?` on
|
@@ -270,8 +251,118 @@ class UsersController < ApplicationController
|
|
270
251
|
end
|
271
252
|
```
|
272
253
|
|
273
|
-
|
274
|
-
|
254
|
+
### Custom input errors
|
255
|
+
|
256
|
+
Use a `Hash` with `is:` and `message:` keys to prepare custom
|
257
|
+
error messages on inputs. For example:
|
258
|
+
|
259
|
+
```rb
|
260
|
+
class UpdateAdminUser < Actor
|
261
|
+
input :user,
|
262
|
+
must: {
|
263
|
+
be_an_admin: {
|
264
|
+
is: -> user { user.admin? },
|
265
|
+
message: "The user is not an administrator"
|
266
|
+
}
|
267
|
+
}
|
268
|
+
|
269
|
+
# ...
|
270
|
+
end
|
271
|
+
```
|
272
|
+
|
273
|
+
You can also use incoming arguments when shaping your error text:
|
274
|
+
|
275
|
+
```rb
|
276
|
+
class UpdateUser < Actor
|
277
|
+
input :user,
|
278
|
+
allow_nil: {
|
279
|
+
is: false,
|
280
|
+
message: (lambda do |input_key:, **|
|
281
|
+
"The value \"#{input_key}\" cannot be empty"
|
282
|
+
end)
|
283
|
+
}
|
284
|
+
|
285
|
+
# ...
|
286
|
+
end
|
287
|
+
```
|
288
|
+
|
289
|
+
<details>
|
290
|
+
<summary>See examples of custom messages on all input arguments</summary>
|
291
|
+
|
292
|
+
#### Inclusion
|
293
|
+
|
294
|
+
```ruby
|
295
|
+
class Pay < Actor
|
296
|
+
input :provider,
|
297
|
+
inclusion: {
|
298
|
+
in: ["MANGOPAY", "PayPal", "Stripe"],
|
299
|
+
message: (lambda do |value:, **|
|
300
|
+
"Payment system \"#{value}\" is not supported"
|
301
|
+
end)
|
302
|
+
}
|
303
|
+
end
|
304
|
+
```
|
305
|
+
|
306
|
+
#### Must
|
307
|
+
|
308
|
+
```ruby
|
309
|
+
class Pay < Actor
|
310
|
+
input :provider,
|
311
|
+
must: {
|
312
|
+
exist: {
|
313
|
+
is: -> provider { PROVIDERS.include?(provider) },
|
314
|
+
message: (lambda do |value:, **|
|
315
|
+
"The specified provider \"#{value}\" was not found."
|
316
|
+
end)
|
317
|
+
}
|
318
|
+
}
|
319
|
+
end
|
320
|
+
```
|
321
|
+
|
322
|
+
#### Default
|
323
|
+
|
324
|
+
```ruby
|
325
|
+
class MultiplyThing < Actor
|
326
|
+
input :multiplier,
|
327
|
+
default: {
|
328
|
+
is: -> { rand(1..10) },
|
329
|
+
message: (lambda do |input_key:, **|
|
330
|
+
"Input \"#{input_key}\" is required"
|
331
|
+
end)
|
332
|
+
}
|
333
|
+
end
|
334
|
+
```
|
335
|
+
|
336
|
+
#### Type
|
337
|
+
|
338
|
+
```ruby
|
339
|
+
class ReduceOrderAmount < Actor
|
340
|
+
input :bonus_applied,
|
341
|
+
type: {
|
342
|
+
is: [TrueClass, FalseClass],
|
343
|
+
message: (lambda do |input_key:, expected_type:, given_type:, **|
|
344
|
+
"Wrong type \"#{given_type}\" for \"#{input_key}\". " \
|
345
|
+
"Expected: \"#{expected_type}\""
|
346
|
+
end)
|
347
|
+
}
|
348
|
+
end
|
349
|
+
```
|
350
|
+
|
351
|
+
#### Allow nil
|
352
|
+
|
353
|
+
```ruby
|
354
|
+
class CreateUser < Actor
|
355
|
+
input :name,
|
356
|
+
allow_nil: {
|
357
|
+
is: false,
|
358
|
+
message: (lambda do |input_key:, **|
|
359
|
+
"The value \"#{input_key}\" cannot be empty"
|
360
|
+
end)
|
361
|
+
}
|
362
|
+
end
|
363
|
+
```
|
364
|
+
|
365
|
+
</details>
|
275
366
|
|
276
367
|
## Play actors in a sequence
|
277
368
|
|
@@ -287,13 +378,13 @@ class PlaceOrder < Actor
|
|
287
378
|
end
|
288
379
|
```
|
289
380
|
|
290
|
-
|
291
|
-
|
292
|
-
|
381
|
+
Calling this actor will now call every actor along the way. Inputs and outputs
|
382
|
+
will go from one actor to the next, all sharing the same result set until it is
|
383
|
+
finally returned.
|
293
384
|
|
294
385
|
### Rollback
|
295
386
|
|
296
|
-
When using `play`,
|
387
|
+
When using `play`, if an actor calls `fail!`, the following actors will not be
|
297
388
|
called.
|
298
389
|
|
299
390
|
Instead, all the actors that succeeded will have their `rollback` method called
|
@@ -384,22 +475,6 @@ class PlaceOrder < Actor
|
|
384
475
|
end
|
385
476
|
```
|
386
477
|
|
387
|
-
### Fail on argument error
|
388
|
-
|
389
|
-
By default, errors on inputs will raise an error, even when using `.result`
|
390
|
-
instead of `.call`. If instead you want to mark the actor as failed, you can
|
391
|
-
catch the exception to treat it as an actor failure:
|
392
|
-
|
393
|
-
```rb
|
394
|
-
class PlaceOrder < Actor
|
395
|
-
fail_on ServiceActor::ArgumentError
|
396
|
-
|
397
|
-
input :currency, in: ["EUR", "USD"]
|
398
|
-
|
399
|
-
# …
|
400
|
-
end
|
401
|
-
```
|
402
|
-
|
403
478
|
## Testing
|
404
479
|
|
405
480
|
In your application, add automated testing to your actors as you would do to any
|
@@ -408,57 +483,22 @@ other part of your applications.
|
|
408
483
|
You will find that cutting your business logic into single purpose actors will
|
409
484
|
make it easier for you to test your application.
|
410
485
|
|
411
|
-
##
|
412
|
-
|
413
|
-
If you application already uses a class called “Actor”, you can build your own
|
414
|
-
by changing the gem’s require statement:
|
415
|
-
|
416
|
-
```rb
|
417
|
-
gem "service_actor", require: "service_actor/base"
|
418
|
-
```
|
419
|
-
|
420
|
-
And building your own class to inherit from:
|
421
|
-
|
422
|
-
```rb
|
423
|
-
class ApplicationActor
|
424
|
-
include ServiceActor::Base
|
425
|
-
end
|
426
|
-
```
|
486
|
+
## FAQ
|
427
487
|
|
428
|
-
|
429
|
-
|
430
|
-
This gem is heavily influenced by
|
431
|
-
[Interactor](https://github.com/collectiveidea/interactor) ♥.
|
432
|
-
Some key differences make Actor unique:
|
433
|
-
|
434
|
-
- Does not [hide errors when an actor fails inside another
|
435
|
-
actor](https://github.com/collectiveidea/interactor/issues/170).
|
436
|
-
- Requires you to document arguments with `input` and `output`.
|
437
|
-
- Defaults to raising errors on failures: actor uses `call` and `result`
|
438
|
-
instead of `call!` and `call`. This way, the _default_ is to raise an error
|
439
|
-
and failures are not hidden away because you forgot to use `!`.
|
440
|
-
- Allows defaults, type checking, requirements and conditions on inputs.
|
441
|
-
- Delegates methods on the context: `foo` vs `context.foo`, `self.foo =` vs
|
442
|
-
`context.foo = `, `fail!` vs `context.fail!`.
|
443
|
-
- Shorter setup syntax: inherit from `< Actor` vs having to `include Interactor`
|
444
|
-
and `include Interactor::Organizer`.
|
445
|
-
- Organizers allow lambdas, instance methods, being called multiple times,
|
446
|
-
and having conditions.
|
447
|
-
- Allows early success with conditions inside organizers.
|
448
|
-
- No `before`, `after` and `around` hooks, prefer using `play` with lambdas or
|
449
|
-
overriding `call`.
|
450
|
-
|
451
|
-
Actor supports mixing actors & interactors when using `play` for a smooth
|
452
|
-
migration.
|
488
|
+
Howtos and frequently asked questions can be found on the
|
489
|
+
[wiki](https://github.com/sunny/actor/wiki).
|
453
490
|
|
454
491
|
## Thanks
|
455
492
|
|
456
|
-
|
457
|
-
|
493
|
+
This gem is influenced by (and compatible with)
|
494
|
+
[Interactor](https://github.com/sunny/actor/wiki/Interactor).
|
458
495
|
|
459
496
|
Thank you to the wonderful
|
460
497
|
[contributors](https://github.com/sunny/actor/graphs/contributors).
|
461
498
|
|
499
|
+
Thank you to @nicoolas25, @AnneSottise & @williampollet for the early thoughts
|
500
|
+
and feedback on this gem.
|
501
|
+
|
462
502
|
Photo by [Lloyd Dirks](https://unsplash.com/photos/4SLz_RCk6kQ).
|
463
503
|
|
464
504
|
## Contributing
|
@@ -1,6 +1,4 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
class ArgumentError < Error; end
|
6
|
-
end
|
3
|
+
# Raised when an input or output does not match the given conditions.
|
4
|
+
class ServiceActor::ArgumentError < ServiceActor::Error; end
|
@@ -1,59 +1,57 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
end
|
14
|
-
|
15
|
-
module ClassMethods
|
16
|
-
def inherited(child)
|
17
|
-
super
|
18
|
-
|
19
|
-
child.inputs.merge!(inputs)
|
20
|
-
child.outputs.merge!(outputs)
|
21
|
-
end
|
3
|
+
# DSL to document the accepted attributes.
|
4
|
+
#
|
5
|
+
# class CreateUser < Actor
|
6
|
+
# input :name
|
7
|
+
# output :name
|
8
|
+
# end
|
9
|
+
module ServiceActor::Attributable
|
10
|
+
def self.included(base)
|
11
|
+
base.extend(ClassMethods)
|
12
|
+
end
|
22
13
|
|
23
|
-
|
24
|
-
|
14
|
+
module ClassMethods
|
15
|
+
def inherited(child)
|
16
|
+
super
|
25
17
|
|
26
|
-
|
27
|
-
|
28
|
-
|
18
|
+
child.inputs.merge!(inputs)
|
19
|
+
child.outputs.merge!(outputs)
|
20
|
+
end
|
29
21
|
|
30
|
-
|
31
|
-
|
22
|
+
def input(name, **arguments)
|
23
|
+
inputs[name] = arguments
|
32
24
|
|
33
|
-
|
25
|
+
define_method(name) do
|
26
|
+
result[name]
|
34
27
|
end
|
35
28
|
|
36
|
-
|
37
|
-
|
38
|
-
end
|
29
|
+
# For avoid method redefined warning messages.
|
30
|
+
alias_method name, name if method_defined?(name)
|
39
31
|
|
40
|
-
|
41
|
-
|
32
|
+
protected name
|
33
|
+
end
|
42
34
|
|
43
|
-
|
44
|
-
|
45
|
-
|
35
|
+
def inputs
|
36
|
+
@inputs ||= {}
|
37
|
+
end
|
46
38
|
|
47
|
-
|
48
|
-
|
49
|
-
end
|
39
|
+
def output(name, **arguments)
|
40
|
+
outputs[name] = arguments
|
50
41
|
|
51
|
-
|
42
|
+
define_method(name) do
|
43
|
+
result[name]
|
52
44
|
end
|
53
45
|
|
54
|
-
|
55
|
-
|
46
|
+
define_method("#{name}=") do |value|
|
47
|
+
result[name] = value
|
56
48
|
end
|
49
|
+
|
50
|
+
protected name, "#{name}="
|
51
|
+
end
|
52
|
+
|
53
|
+
def outputs
|
54
|
+
@outputs ||= {}
|
57
55
|
end
|
58
56
|
end
|
59
57
|
end
|
data/lib/service_actor/base.rb
CHANGED
@@ -1,39 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
require "service_actor/error"
|
5
|
-
require "service_actor/failure"
|
6
|
-
require "service_actor/argument_error"
|
3
|
+
require "service_actor/support/loader"
|
7
4
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
5
|
+
module ServiceActor::Base
|
6
|
+
def self.included(base)
|
7
|
+
# Essential mechanics
|
8
|
+
base.include(ServiceActor::Core)
|
9
|
+
base.include(ServiceActor::Raisable)
|
10
|
+
base.include(ServiceActor::Attributable)
|
11
|
+
base.include(ServiceActor::Playable)
|
13
12
|
|
14
|
-
#
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
module ServiceActor
|
23
|
-
module Base
|
24
|
-
def self.included(base)
|
25
|
-
# Core
|
26
|
-
base.include(Core)
|
27
|
-
base.include(Attributable)
|
28
|
-
base.include(Playable)
|
29
|
-
|
30
|
-
# Concerns
|
31
|
-
base.include(TypeCheckable)
|
32
|
-
base.include(NilCheckable)
|
33
|
-
base.include(Conditionable)
|
34
|
-
base.include(Collectionable)
|
35
|
-
base.include(Defaultable)
|
36
|
-
base.include(Failable)
|
37
|
-
end
|
13
|
+
# Extra concerns
|
14
|
+
base.include(ServiceActor::TypeCheckable)
|
15
|
+
base.include(ServiceActor::NilCheckable)
|
16
|
+
base.include(ServiceActor::Conditionable)
|
17
|
+
base.include(ServiceActor::Collectionable)
|
18
|
+
base.include(ServiceActor::Defaultable)
|
19
|
+
base.include(ServiceActor::Failable)
|
38
20
|
end
|
39
21
|
end
|
@@ -1,32 +1,68 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
3
|
+
# Add checks to your inputs, by specifying what values are authorized under the
|
4
|
+
# "in" key.
|
5
|
+
#
|
6
|
+
# Example:
|
7
|
+
#
|
8
|
+
# class Pay < Actor
|
9
|
+
# input :provider, inclusion: ["MANGOPAY", "PayPal", "Stripe"]
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# class Pay < Actor
|
13
|
+
# input :provider,
|
14
|
+
# inclusion: {
|
15
|
+
# in: ["MANGOPAY", "PayPal", "Stripe"],
|
16
|
+
# message: (lambda do |input_key:, actor:, inclusion_in:, value:|
|
17
|
+
# "Payment system \"#{value}\" is not supported"
|
18
|
+
# end)
|
19
|
+
# }
|
20
|
+
# end
|
21
|
+
module ServiceActor::Collectionable
|
22
|
+
def self.included(base)
|
23
|
+
base.prepend(PrependedMethods)
|
24
|
+
end
|
25
|
+
|
26
|
+
module PrependedMethods
|
27
|
+
DEFAULT_MESSAGE = lambda do |input_key:, actor:, inclusion_in:, value:|
|
28
|
+
"The \"#{input_key}\" input must be included " \
|
29
|
+
"in #{inclusion_in.inspect} on \"#{actor}\" " \
|
30
|
+
"instead of #{value.inspect}"
|
15
31
|
end
|
16
32
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
33
|
+
private_constant :DEFAULT_MESSAGE
|
34
|
+
|
35
|
+
def _call # rubocop:disable Metrics/MethodLength
|
36
|
+
self.class.inputs.each do |key, options|
|
37
|
+
value = result[key]
|
38
|
+
|
39
|
+
# DEPRECATED: `in` is deprecated in favor of `inclusion`.
|
40
|
+
inclusion = options[:inclusion] || options[:in]
|
41
|
+
|
42
|
+
inclusion_in, message = define_inclusion_from(inclusion)
|
21
43
|
|
22
|
-
|
44
|
+
next if inclusion_in.nil?
|
45
|
+
next if inclusion_in.include?(value)
|
46
|
+
|
47
|
+
raise_error_with(
|
48
|
+
message,
|
49
|
+
input_key: key,
|
50
|
+
actor: self.class,
|
51
|
+
inclusion_in: inclusion_in,
|
52
|
+
value: value,
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
super
|
57
|
+
end
|
23
58
|
|
24
|
-
|
25
|
-
"Input #{key} must be included in #{options[:in].inspect} " \
|
26
|
-
"but instead was #{result[key].inspect}"
|
27
|
-
end
|
59
|
+
private
|
28
60
|
|
29
|
-
|
61
|
+
def define_inclusion_from(inclusion)
|
62
|
+
if inclusion.is_a?(Hash)
|
63
|
+
inclusion.values_at(:in, :message)
|
64
|
+
else
|
65
|
+
[inclusion, DEFAULT_MESSAGE]
|
30
66
|
end
|
31
67
|
end
|
32
68
|
end
|