service_actor 3.3.0 → 3.4.1
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/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: 17a682280c4577e879e35e0210e31c7e22067d8d8c9e11c806d8eb6e95f515ac
|
4
|
+
data.tar.gz: 24af188ea46c8650e5375b7c9bba438fc0a6d5f51d05ce7159e959671346d24e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 49e059ed1cef11316fa6f7c75c9e1baa7ede7de3545ccce499bf0d2b7a2199509e3c51c9515eca0f3e34a6f29e745ed694a691b13d16316e9102ade7c2f58c65
|
7
|
+
data.tar.gz: '092ec403cb6f721eb1575a181ff426b6835fd4364ffa7378d3c533afa701c9bc0a7bfa4e46abf6c97c9c6a8a7b0b5e9c6e222275386d1837bd345fdcfc1adca1'
|
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
|