service_actor 3.7.0 → 3.8.0
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 +2 -2
- data/lib/service_actor/arguments_validator.rb +18 -0
- data/lib/service_actor/attributable.rb +17 -7
- data/lib/service_actor/base.rb +12 -10
- data/lib/service_actor/checkable.rb +14 -13
- data/lib/service_actor/checks/base.rb +6 -0
- data/lib/service_actor/checks/inclusion_check.rb +13 -10
- data/lib/service_actor/checks/must_check.rb +11 -8
- data/lib/service_actor/checks/nil_check.rb +20 -17
- data/lib/service_actor/checks/type_check.rb +22 -18
- data/lib/service_actor/configurable.rb +17 -3
- data/lib/service_actor/core.rb +4 -2
- data/lib/service_actor/defaultable.rb +11 -3
- data/lib/service_actor/failable.rb +9 -3
- data/lib/service_actor/playable.rb +14 -15
- data/lib/service_actor/result.rb +75 -32
- data/lib/service_actor/version.rb +1 -1
- metadata +63 -27
- data/lib/service_actor/checks/default_check.rb +0 -90
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a852443e88e3b6957cbaf093330d08691ba8e862826ac3b745fa5510d1b0273c
|
4
|
+
data.tar.gz: 21a7648a3e3302600b1946285a76b0e80c70ba1550776dfd07057718397d5f66
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 692cc4e5240be00b9a184daf7e7ec921976649ec98c991e14b538e6be37e61462d06dadaa72704010f59c2f3c61ce5921a64871e5050e5fc0efb9a8a118c94d8
|
7
|
+
data.tar.gz: bc2a009e14baaa36d81179fd38f77ed22a972cc9486b0f5d511f4c49bef865d4e940fc990745a07922687340be4999f956f50bd4faae78c1567cb17d7902fd05
|
data/README.md
CHANGED
@@ -303,7 +303,7 @@ class BuildGreeting < Actor
|
|
303
303
|
input :adjective, default: "wonderful"
|
304
304
|
input :length_of_time, default: -> { ["day", "week", "month"].sample }
|
305
305
|
input :article,
|
306
|
-
default: -> context { context.adjective
|
306
|
+
default: -> context { context.adjective.match?(/^aeiou/) ? "an" : "a" }
|
307
307
|
|
308
308
|
output :greeting
|
309
309
|
|
@@ -475,7 +475,7 @@ end
|
|
475
475
|
is: [TrueClass, FalseClass],
|
476
476
|
message: (lambda do |input_key:, expected_type:, given_type:, **|
|
477
477
|
"Wrong type \"#{given_type}\" for \"#{input_key}\". " \
|
478
|
-
|
478
|
+
"Expected: \"#{expected_type}\""
|
479
479
|
end)
|
480
480
|
}
|
481
481
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ServiceActor::ArgumentsValidator
|
4
|
+
module_function
|
5
|
+
|
6
|
+
def validate_origin_name(name, origin:)
|
7
|
+
return unless ServiceActor::Result.instance_methods.include?(name.to_sym)
|
8
|
+
|
9
|
+
raise ArgumentError,
|
10
|
+
"#{origin} `#{name}` overrides `ServiceActor::Result` instance method"
|
11
|
+
end
|
12
|
+
|
13
|
+
def validate_error_class(value)
|
14
|
+
return if value.is_a?(Class) && value <= Exception
|
15
|
+
|
16
|
+
raise ArgumentError, "Expected #{value} to be a subclass of Exception"
|
17
|
+
end
|
18
|
+
end
|
@@ -7,8 +7,10 @@
|
|
7
7
|
# output :name
|
8
8
|
# end
|
9
9
|
module ServiceActor::Attributable
|
10
|
-
|
11
|
-
base
|
10
|
+
class << self
|
11
|
+
def included(base)
|
12
|
+
base.extend(ClassMethods)
|
13
|
+
end
|
12
14
|
end
|
13
15
|
|
14
16
|
module ClassMethods
|
@@ -20,14 +22,18 @@ module ServiceActor::Attributable
|
|
20
22
|
end
|
21
23
|
|
22
24
|
def input(name, **arguments)
|
25
|
+
ServiceActor::ArgumentsValidator.validate_origin_name(
|
26
|
+
name, origin: :input
|
27
|
+
)
|
28
|
+
|
23
29
|
inputs[name] = arguments
|
24
30
|
|
25
31
|
define_method(name) do
|
26
32
|
result[name]
|
27
33
|
end
|
28
34
|
|
29
|
-
#
|
30
|
-
alias_method
|
35
|
+
# To avoid method redefined warning messages.
|
36
|
+
alias_method(name, name) if method_defined?(name)
|
31
37
|
|
32
38
|
protected name
|
33
39
|
end
|
@@ -37,17 +43,21 @@ module ServiceActor::Attributable
|
|
37
43
|
end
|
38
44
|
|
39
45
|
def output(name, **arguments)
|
46
|
+
ServiceActor::ArgumentsValidator.validate_origin_name(
|
47
|
+
name, origin: :output
|
48
|
+
)
|
49
|
+
|
40
50
|
outputs[name] = arguments
|
41
51
|
|
42
52
|
define_method(name) do
|
43
53
|
result[name]
|
44
54
|
end
|
55
|
+
protected name
|
45
56
|
|
46
|
-
define_method("#{name}=") do |value|
|
57
|
+
define_method(:"#{name}=") do |value|
|
47
58
|
result[name] = value
|
48
59
|
end
|
49
|
-
|
50
|
-
protected name, "#{name}="
|
60
|
+
protected :"#{name}="
|
51
61
|
end
|
52
62
|
|
53
63
|
def outputs
|
data/lib/service_actor/base.rb
CHANGED
@@ -3,16 +3,18 @@
|
|
3
3
|
require "service_actor/support/loader"
|
4
4
|
|
5
5
|
module ServiceActor::Base
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
class << self
|
7
|
+
def included(base)
|
8
|
+
# Essential mechanics
|
9
|
+
base.include(ServiceActor::Core)
|
10
|
+
base.include(ServiceActor::Configurable)
|
11
|
+
base.include(ServiceActor::Attributable)
|
12
|
+
base.include(ServiceActor::Playable)
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
# Extra concerns
|
15
|
+
base.include(ServiceActor::Checkable)
|
16
|
+
base.include(ServiceActor::Defaultable)
|
17
|
+
base.include(ServiceActor::Failable)
|
18
|
+
end
|
17
19
|
end
|
18
20
|
end
|
@@ -1,11 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ServiceActor::Checkable
|
4
|
-
|
5
|
-
base
|
4
|
+
class << self
|
5
|
+
def included(base)
|
6
|
+
base.prepend(PrependedMethods)
|
7
|
+
end
|
6
8
|
end
|
7
9
|
|
8
10
|
module PrependedMethods
|
11
|
+
CHECK_CLASSES = [
|
12
|
+
ServiceActor::Checks::TypeCheck,
|
13
|
+
ServiceActor::Checks::MustCheck,
|
14
|
+
ServiceActor::Checks::InclusionCheck,
|
15
|
+
ServiceActor::Checks::NilCheck,
|
16
|
+
].freeze
|
17
|
+
private_constant :CHECK_CLASSES
|
18
|
+
|
9
19
|
def _call
|
10
20
|
self.service_actor_argument_errors = []
|
11
21
|
|
@@ -20,7 +30,8 @@ module ServiceActor::Checkable
|
|
20
30
|
|
21
31
|
# rubocop:disable Metrics/MethodLength
|
22
32
|
def service_actor_checks_for(origin)
|
23
|
-
|
33
|
+
check_classes = CHECK_CLASSES.select { _1.applicable_to_origin?(origin) }
|
34
|
+
self.class.public_send(:"#{origin}s").each do |input_key, input_options|
|
24
35
|
input_options.each do |check_name, check_conditions|
|
25
36
|
check_classes.each do |check_class|
|
26
37
|
argument_errors = check_class.check(
|
@@ -48,15 +59,5 @@ module ServiceActor::Checkable
|
|
48
59
|
raise self.class.argument_error_class,
|
49
60
|
service_actor_argument_errors.first
|
50
61
|
end
|
51
|
-
|
52
|
-
def check_classes
|
53
|
-
[
|
54
|
-
ServiceActor::Checks::TypeCheck,
|
55
|
-
ServiceActor::Checks::MustCheck,
|
56
|
-
ServiceActor::Checks::InclusionCheck,
|
57
|
-
ServiceActor::Checks::NilCheck,
|
58
|
-
ServiceActor::Checks::DefaultCheck
|
59
|
-
]
|
60
|
-
end
|
61
62
|
end
|
62
63
|
end
|
@@ -22,21 +22,23 @@ class ServiceActor::Checks::InclusionCheck < ServiceActor::Checks::Base
|
|
22
22
|
DEFAULT_MESSAGE = lambda do |input_key:, actor:, inclusion_in:, value:|
|
23
23
|
"The \"#{input_key}\" input must be included " \
|
24
24
|
"in #{inclusion_in.inspect} on \"#{actor}\" " \
|
25
|
-
|
25
|
+
"instead of #{value.inspect}"
|
26
26
|
end
|
27
27
|
|
28
28
|
private_constant :DEFAULT_MESSAGE
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
30
|
+
class << self
|
31
|
+
def check(check_name:, input_key:, actor:, conditions:, result:, **)
|
32
|
+
# DEPRECATED: `in` is deprecated in favor of `inclusion`.
|
33
|
+
return unless %i[inclusion in].include?(check_name)
|
33
34
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
35
|
+
new(
|
36
|
+
input_key: input_key,
|
37
|
+
actor: actor,
|
38
|
+
inclusion: conditions,
|
39
|
+
value: result[input_key],
|
40
|
+
).check
|
41
|
+
end
|
40
42
|
end
|
41
43
|
|
42
44
|
def initialize(input_key:, actor:, inclusion:, value:)
|
@@ -67,6 +69,7 @@ class ServiceActor::Checks::InclusionCheck < ServiceActor::Checks::Base
|
|
67
69
|
|
68
70
|
def define_inclusion_and_message
|
69
71
|
if @inclusion.is_a?(Hash)
|
72
|
+
@inclusion[:message] ||= DEFAULT_MESSAGE
|
70
73
|
@inclusion.values_at(:in, :message)
|
71
74
|
else
|
72
75
|
[@inclusion, DEFAULT_MESSAGE]
|
@@ -33,15 +33,17 @@ class ServiceActor::Checks::MustCheck < ServiceActor::Checks::Base
|
|
33
33
|
|
34
34
|
private_constant :DEFAULT_MESSAGE
|
35
35
|
|
36
|
-
|
37
|
-
|
36
|
+
class << self
|
37
|
+
def check(check_name:, input_key:, actor:, conditions:, result:, **)
|
38
|
+
return unless check_name == :must
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
40
|
+
new(
|
41
|
+
input_key: input_key,
|
42
|
+
actor: actor,
|
43
|
+
nested_checks: conditions,
|
44
|
+
value: result[input_key],
|
45
|
+
).check
|
46
|
+
end
|
45
47
|
end
|
46
48
|
|
47
49
|
def initialize(input_key:, actor:, nested_checks:, value:)
|
@@ -86,6 +88,7 @@ class ServiceActor::Checks::MustCheck < ServiceActor::Checks::Base
|
|
86
88
|
|
87
89
|
def define_check_and_message_from(nested_check_conditions)
|
88
90
|
if nested_check_conditions.is_a?(Hash)
|
91
|
+
nested_check_conditions[:message] ||= DEFAULT_MESSAGE
|
89
92
|
nested_check_conditions.values_at(:is, :message)
|
90
93
|
else
|
91
94
|
[nested_check_conditions, DEFAULT_MESSAGE]
|
@@ -35,23 +35,25 @@ class ServiceActor::Checks::NilCheck < ServiceActor::Checks::Base
|
|
35
35
|
|
36
36
|
private_constant :DEFAULT_MESSAGE
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
38
|
+
class << self
|
39
|
+
def check(
|
40
|
+
origin:,
|
41
|
+
input_key:,
|
42
|
+
input_options:,
|
43
|
+
actor:,
|
44
|
+
conditions:,
|
45
|
+
result:,
|
46
|
+
**
|
47
|
+
) # do
|
48
|
+
new(
|
49
|
+
origin: origin,
|
50
|
+
input_key: input_key,
|
51
|
+
input_options: input_options,
|
52
|
+
actor: actor,
|
53
|
+
allow_nil: conditions,
|
54
|
+
value: result[input_key],
|
55
|
+
).check
|
56
|
+
end
|
55
57
|
end
|
56
58
|
|
57
59
|
def initialize( # rubocop:disable Metrics/ParameterLists
|
@@ -92,6 +94,7 @@ class ServiceActor::Checks::NilCheck < ServiceActor::Checks::Base
|
|
92
94
|
|
93
95
|
def define_allow_nil_and_message_from(allow_nil)
|
94
96
|
if allow_nil.is_a?(Hash)
|
97
|
+
allow_nil[:message] ||= DEFAULT_MESSAGE
|
95
98
|
allow_nil.values_at(:is, :message)
|
96
99
|
else
|
97
100
|
[allow_nil, DEFAULT_MESSAGE]
|
@@ -34,24 +34,26 @@ class ServiceActor::Checks::TypeCheck < ServiceActor::Checks::Base
|
|
34
34
|
|
35
35
|
private_constant :DEFAULT_MESSAGE
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
37
|
+
class << self
|
38
|
+
def check(
|
39
|
+
check_name:,
|
40
|
+
origin:,
|
41
|
+
input_key:,
|
42
|
+
actor:,
|
43
|
+
conditions:,
|
44
|
+
result:,
|
45
|
+
**
|
46
|
+
) # do
|
47
|
+
return unless check_name == :type
|
48
|
+
|
49
|
+
new(
|
50
|
+
origin: origin,
|
51
|
+
input_key: input_key,
|
52
|
+
actor: actor,
|
53
|
+
type_definition: conditions,
|
54
|
+
given_type: result[input_key],
|
55
|
+
).check
|
56
|
+
end
|
55
57
|
end
|
56
58
|
|
57
59
|
def initialize(origin:, input_key:, actor:, type_definition:, given_type:)
|
@@ -86,6 +88,8 @@ class ServiceActor::Checks::TypeCheck < ServiceActor::Checks::Base
|
|
86
88
|
|
87
89
|
def define_types_and_message
|
88
90
|
if @type_definition.is_a?(Hash)
|
91
|
+
@type_definition[:message] ||= DEFAULT_MESSAGE
|
92
|
+
|
89
93
|
@type_definition, message =
|
90
94
|
@type_definition.values_at(:is, :message)
|
91
95
|
else
|
@@ -1,8 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ServiceActor::Configurable
|
4
|
-
|
5
|
-
base
|
4
|
+
class << self
|
5
|
+
def included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
6
8
|
end
|
7
9
|
|
8
10
|
module ClassMethods
|
@@ -15,6 +17,18 @@ module ServiceActor::Configurable
|
|
15
17
|
child.failure_class = failure_class || ServiceActor::Failure
|
16
18
|
end
|
17
19
|
|
18
|
-
|
20
|
+
def argument_error_class=(value)
|
21
|
+
ServiceActor::ArgumentsValidator.validate_error_class(value)
|
22
|
+
|
23
|
+
@argument_error_class = value
|
24
|
+
end
|
25
|
+
|
26
|
+
def failure_class=(value)
|
27
|
+
ServiceActor::ArgumentsValidator.validate_error_class(value)
|
28
|
+
|
29
|
+
@failure_class = value
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :argument_error_class, :failure_class
|
19
33
|
end
|
20
34
|
end
|
data/lib/service_actor/core.rb
CHANGED
@@ -26,8 +26,10 @@
|
|
26
26
|
# }
|
27
27
|
# end
|
28
28
|
module ServiceActor::Defaultable
|
29
|
-
|
30
|
-
base
|
29
|
+
class << self
|
30
|
+
def included(base)
|
31
|
+
base.prepend(PrependedMethods)
|
32
|
+
end
|
31
33
|
end
|
32
34
|
|
33
35
|
module PrependedMethods
|
@@ -43,13 +45,19 @@ module ServiceActor::Defaultable
|
|
43
45
|
|
44
46
|
default = input[:default]
|
45
47
|
|
46
|
-
if default.is_a?(Hash)
|
48
|
+
if default.is_a?(Hash) && default[:is]
|
47
49
|
default_for_advanced_mode_with(result, key, default)
|
48
50
|
else
|
49
51
|
default_for_normal_mode_with(result, key, default)
|
50
52
|
end
|
51
53
|
end
|
52
54
|
|
55
|
+
self.class.outputs.each do |key, options|
|
56
|
+
unless result.key?(key)
|
57
|
+
result.send(:"#{key}=", options[:default])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
53
61
|
super
|
54
62
|
end
|
55
63
|
|
@@ -7,9 +7,11 @@
|
|
7
7
|
# fail_on ServiceActor::ArgumentError
|
8
8
|
# end
|
9
9
|
module ServiceActor::Failable
|
10
|
-
|
11
|
-
base
|
12
|
-
|
10
|
+
class << self
|
11
|
+
def included(base)
|
12
|
+
base.extend(ClassMethods)
|
13
|
+
base.prepend(PrependedMethods)
|
14
|
+
end
|
13
15
|
end
|
14
16
|
|
15
17
|
module ClassMethods
|
@@ -20,6 +22,10 @@ module ServiceActor::Failable
|
|
20
22
|
end
|
21
23
|
|
22
24
|
def fail_on(*exceptions)
|
25
|
+
exceptions.each do |exception|
|
26
|
+
ServiceActor::ArgumentsValidator.validate_error_class(exception)
|
27
|
+
end
|
28
|
+
|
23
29
|
fail_ons.push(*exceptions)
|
24
30
|
end
|
25
31
|
|
@@ -9,9 +9,11 @@
|
|
9
9
|
# SendWelcomeEmail
|
10
10
|
# end
|
11
11
|
module ServiceActor::Playable
|
12
|
-
|
13
|
-
base
|
14
|
-
|
12
|
+
class << self
|
13
|
+
def included(base)
|
14
|
+
base.extend(ClassMethods)
|
15
|
+
base.prepend(PrependedMethods)
|
16
|
+
end
|
15
17
|
end
|
16
18
|
|
17
19
|
module ClassMethods
|
@@ -20,6 +22,12 @@ module ServiceActor::Playable
|
|
20
22
|
end
|
21
23
|
|
22
24
|
def alias_input(**options)
|
25
|
+
options.each_key do |new|
|
26
|
+
ServiceActor::ArgumentsValidator.validate_origin_name(
|
27
|
+
new, origin: :alias
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
23
31
|
lambda do |actor|
|
24
32
|
options.each do |new, original|
|
25
33
|
define_alias_input(actor, new, original)
|
@@ -39,17 +47,8 @@ module ServiceActor::Playable
|
|
39
47
|
|
40
48
|
private
|
41
49
|
|
42
|
-
def define_alias_input(actor,
|
43
|
-
actor[
|
44
|
-
actor.instance_exec do
|
45
|
-
[original, new].each do |method|
|
46
|
-
singleton_class.send(:undef_method, "#{method}=")
|
47
|
-
define_singleton_method("#{method}=") do |v|
|
48
|
-
actor[original] = v
|
49
|
-
actor[new] = v
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
50
|
+
def define_alias_input(actor, new_input, original_input)
|
51
|
+
actor[new_input] = actor.delete!(original_input)
|
53
52
|
end
|
54
53
|
end
|
55
54
|
|
@@ -113,7 +112,7 @@ module ServiceActor::Playable
|
|
113
112
|
return unless actor.is_a?(Class)
|
114
113
|
return unless actor.ancestors.map(&:name).include?("Interactor")
|
115
114
|
|
116
|
-
result.merge!(actor.call(result).to_h)
|
115
|
+
result.merge!(actor.call(result.to_h).to_h)
|
117
116
|
end
|
118
117
|
end
|
119
118
|
end
|
data/lib/service_actor/result.rb
CHANGED
@@ -1,30 +1,46 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "ostruct"
|
4
|
-
|
5
3
|
# Represents the context of an actor, holding the data from both its inputs
|
6
4
|
# and outputs.
|
7
|
-
class ServiceActor::Result <
|
8
|
-
|
9
|
-
|
5
|
+
class ServiceActor::Result < BasicObject
|
6
|
+
class << self
|
7
|
+
def to_result(data)
|
8
|
+
return data if data.is_a?(self)
|
9
|
+
|
10
|
+
new(data.to_h)
|
11
|
+
end
|
12
|
+
end
|
10
13
|
|
11
|
-
|
14
|
+
%i[class is_a? kind_of? send tap then block_given?].each do |method_name|
|
15
|
+
define_method(method_name, ::Kernel.instance_method(method_name))
|
16
|
+
end
|
17
|
+
|
18
|
+
alias_method :yield_self, :then
|
19
|
+
|
20
|
+
def initialize(data = {})
|
21
|
+
@data = data.to_h
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_h
|
25
|
+
data
|
12
26
|
end
|
13
27
|
|
14
28
|
def inspect
|
15
29
|
"<#{self.class.name} #{to_h}>"
|
16
30
|
end
|
17
31
|
|
32
|
+
alias_method :pretty_print, :inspect
|
33
|
+
|
18
34
|
def fail!(failure_class = nil, result = {})
|
19
|
-
if failure_class.nil? || failure_class.is_a?(Hash)
|
35
|
+
if failure_class.nil? || failure_class.is_a?(::Hash)
|
20
36
|
result = failure_class.to_h
|
21
|
-
failure_class = ServiceActor::Failure
|
37
|
+
failure_class = ::ServiceActor::Failure
|
22
38
|
end
|
23
39
|
|
24
|
-
merge!(result)
|
25
|
-
|
40
|
+
data.merge!(result)
|
41
|
+
data[:failure] = true
|
26
42
|
|
27
|
-
raise failure_class, self
|
43
|
+
::Kernel.raise failure_class, self
|
28
44
|
end
|
29
45
|
|
30
46
|
def success?
|
@@ -32,13 +48,11 @@ class ServiceActor::Result < OpenStruct
|
|
32
48
|
end
|
33
49
|
|
34
50
|
def failure?
|
35
|
-
|
51
|
+
data[:failure] || false
|
36
52
|
end
|
37
53
|
|
38
54
|
def merge!(result)
|
39
|
-
result
|
40
|
-
self[key] = value
|
41
|
-
end
|
55
|
+
data.merge!(result)
|
42
56
|
|
43
57
|
self
|
44
58
|
end
|
@@ -48,34 +62,63 @@ class ServiceActor::Result < OpenStruct
|
|
48
62
|
end
|
49
63
|
|
50
64
|
def [](name)
|
51
|
-
|
65
|
+
data[name]
|
52
66
|
end
|
53
67
|
|
54
|
-
|
55
|
-
|
56
|
-
to_h.fetch(:display)
|
68
|
+
def []=(key, value)
|
69
|
+
data[key] = value
|
57
70
|
end
|
58
71
|
|
59
|
-
|
72
|
+
def delete!(key)
|
73
|
+
data.delete(key)
|
74
|
+
end
|
60
75
|
|
61
|
-
def
|
62
|
-
|
76
|
+
def respond_to?(method_name, include_private = false)
|
77
|
+
self.class.instance_methods.include?(method_name) ||
|
78
|
+
respond_to_missing?(method_name, include_private)
|
63
79
|
end
|
64
80
|
|
65
|
-
def
|
66
|
-
|
81
|
+
def deconstruct_keys(keys)
|
82
|
+
deconstructed_keys = to_h.merge(success: success?, failure: failure?)
|
83
|
+
|
84
|
+
keys ? deconstructed_keys.slice(*keys) : deconstructed_keys
|
85
|
+
end
|
67
86
|
|
68
|
-
|
69
|
-
define_singleton_method symbol do
|
70
|
-
value = send(attribute.to_sym)
|
87
|
+
private
|
71
88
|
|
72
|
-
|
73
|
-
value.respond_to?(:empty?) ? !value.empty? : !!value
|
74
|
-
end
|
89
|
+
attr_reader :data
|
75
90
|
|
76
|
-
|
91
|
+
def respond_to_missing?(method_name, _include_private = false)
|
92
|
+
return true if method_name.end_with?("=")
|
93
|
+
if method_name.end_with?("?") &&
|
94
|
+
data.key?(method_name.to_s.chomp("?").to_sym)
|
95
|
+
return true
|
77
96
|
end
|
78
97
|
|
79
|
-
|
98
|
+
return true if data.key?(method_name)
|
99
|
+
|
100
|
+
false
|
101
|
+
end
|
102
|
+
|
103
|
+
def method_missing(method_name, *args) # rubocop:disable Metrics/AbcSize
|
104
|
+
if method_name.end_with?("?") &&
|
105
|
+
data.key?(key = method_name.to_s.chomp("?").to_sym)
|
106
|
+
value = data[key]
|
107
|
+
value.respond_to?(:empty?) ? !value.empty? : !!value
|
108
|
+
elsif method_name.end_with?("=")
|
109
|
+
data[method_name.to_s.chomp("=").to_sym] = args.first
|
110
|
+
elsif data.key?(method_name)
|
111
|
+
data[method_name]
|
112
|
+
else
|
113
|
+
warn_on_undefined_method_invocation(method_name)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def warn_on_undefined_method_invocation(message)
|
118
|
+
::Kernel.warn(
|
119
|
+
"DEPRECATED: Invoking undefined methods on `ServiceActor::Result` will " \
|
120
|
+
"lead to runtime errors in the next major release of Actor. " \
|
121
|
+
"Invoked method: `#{message}`",
|
122
|
+
)
|
80
123
|
end
|
81
124
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: service_actor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sunny Ripert
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-03-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: zeitwerk
|
@@ -16,146 +16,182 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
19
|
+
version: '1.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
26
|
+
version: '1.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rspec
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
33
|
+
version: '3.0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
40
|
+
version: '3.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
47
|
+
version: '13.0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
54
|
+
version: '13.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: pry
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
61
|
+
version: '0.12'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
68
|
+
version: '0.12'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rubocop-lts
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
75
|
+
version: '18.2'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '18.2'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec-block_is_expected
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
76
87
|
- - ">="
|
77
88
|
- !ruby/object:Gem::Version
|
78
|
-
version:
|
89
|
+
version: '1.0'
|
79
90
|
type: :development
|
80
91
|
prerelease: false
|
81
92
|
version_requirements: !ruby/object:Gem::Requirement
|
82
93
|
requirements:
|
83
|
-
- - "
|
94
|
+
- - ">="
|
84
95
|
- !ruby/object:Gem::Version
|
85
|
-
version: '
|
96
|
+
version: '1.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop-gradual
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
86
101
|
- - ">="
|
87
102
|
- !ruby/object:Gem::Version
|
88
|
-
version:
|
103
|
+
version: '0.3'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.3'
|
89
111
|
- !ruby/object:Gem::Dependency
|
90
112
|
name: rubocop-rspec
|
91
113
|
requirement: !ruby/object:Gem::Requirement
|
92
114
|
requirements:
|
93
115
|
- - ">="
|
94
116
|
- !ruby/object:Gem::Version
|
95
|
-
version: '0'
|
117
|
+
version: '2.0'
|
96
118
|
type: :development
|
97
119
|
prerelease: false
|
98
120
|
version_requirements: !ruby/object:Gem::Requirement
|
99
121
|
requirements:
|
100
122
|
- - ">="
|
101
123
|
- !ruby/object:Gem::Version
|
102
|
-
version: '0'
|
124
|
+
version: '2.0'
|
103
125
|
- !ruby/object:Gem::Dependency
|
104
126
|
name: rubocop-performance
|
105
127
|
requirement: !ruby/object:Gem::Requirement
|
106
128
|
requirements:
|
107
129
|
- - ">="
|
108
130
|
- !ruby/object:Gem::Version
|
109
|
-
version: '0'
|
131
|
+
version: '1.0'
|
110
132
|
type: :development
|
111
133
|
prerelease: false
|
112
134
|
version_requirements: !ruby/object:Gem::Requirement
|
113
135
|
requirements:
|
114
136
|
- - ">="
|
115
137
|
- !ruby/object:Gem::Version
|
116
|
-
version: '0'
|
138
|
+
version: '1.0'
|
117
139
|
- !ruby/object:Gem::Dependency
|
118
140
|
name: rubocop-rake
|
119
141
|
requirement: !ruby/object:Gem::Requirement
|
120
142
|
requirements:
|
121
143
|
- - ">="
|
122
144
|
- !ruby/object:Gem::Version
|
123
|
-
version: '0'
|
145
|
+
version: '0.1'
|
124
146
|
type: :development
|
125
147
|
prerelease: false
|
126
148
|
version_requirements: !ruby/object:Gem::Requirement
|
127
149
|
requirements:
|
128
150
|
- - ">="
|
129
151
|
- !ruby/object:Gem::Version
|
130
|
-
version: '0'
|
152
|
+
version: '0.1'
|
131
153
|
- !ruby/object:Gem::Dependency
|
132
154
|
name: code-scanning-rubocop
|
133
155
|
requirement: !ruby/object:Gem::Requirement
|
134
156
|
requirements:
|
135
157
|
- - ">="
|
136
158
|
- !ruby/object:Gem::Version
|
137
|
-
version: '0'
|
159
|
+
version: '0.6'
|
138
160
|
type: :development
|
139
161
|
prerelease: false
|
140
162
|
version_requirements: !ruby/object:Gem::Requirement
|
141
163
|
requirements:
|
142
164
|
- - ">="
|
143
165
|
- !ruby/object:Gem::Version
|
144
|
-
version: '0'
|
166
|
+
version: '0.6'
|
145
167
|
- !ruby/object:Gem::Dependency
|
146
168
|
name: interactor
|
147
169
|
requirement: !ruby/object:Gem::Requirement
|
148
170
|
requirements:
|
149
171
|
- - ">="
|
150
172
|
- !ruby/object:Gem::Version
|
151
|
-
version: '0'
|
173
|
+
version: '3.0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '3.0'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: simplecov
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - ">="
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '0.0'
|
152
188
|
type: :development
|
153
189
|
prerelease: false
|
154
190
|
version_requirements: !ruby/object:Gem::Requirement
|
155
191
|
requirements:
|
156
192
|
- - ">="
|
157
193
|
- !ruby/object:Gem::Version
|
158
|
-
version: '0'
|
194
|
+
version: '0.0'
|
159
195
|
description: Service objects for your application logic
|
160
196
|
email:
|
161
197
|
- sunny@sunfox.org
|
@@ -169,11 +205,11 @@ files:
|
|
169
205
|
- README.md
|
170
206
|
- lib/service_actor.rb
|
171
207
|
- lib/service_actor/argument_error.rb
|
208
|
+
- lib/service_actor/arguments_validator.rb
|
172
209
|
- lib/service_actor/attributable.rb
|
173
210
|
- lib/service_actor/base.rb
|
174
211
|
- lib/service_actor/checkable.rb
|
175
212
|
- lib/service_actor/checks/base.rb
|
176
|
-
- lib/service_actor/checks/default_check.rb
|
177
213
|
- lib/service_actor/checks/inclusion_check.rb
|
178
214
|
- lib/service_actor/checks/must_check.rb
|
179
215
|
- lib/service_actor/checks/nil_check.rb
|
@@ -204,7 +240,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
204
240
|
requirements:
|
205
241
|
- - ">="
|
206
242
|
- !ruby/object:Gem::Version
|
207
|
-
version: '2.
|
243
|
+
version: '2.7'
|
208
244
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
209
245
|
requirements:
|
210
246
|
- - ">="
|
@@ -1,90 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Adds the `default:` option to inputs. Accepts regular values and lambdas.
|
4
|
-
# If no default is set and the value has not been given, raises an error.
|
5
|
-
#
|
6
|
-
# Example:
|
7
|
-
#
|
8
|
-
# class MultiplyThing < Actor
|
9
|
-
# input :counter, default: 1
|
10
|
-
# input :multiplier, default: -> { rand(1..10) }
|
11
|
-
# end
|
12
|
-
#
|
13
|
-
# class MultiplyThing < Actor
|
14
|
-
# input :counter,
|
15
|
-
# default: {
|
16
|
-
# is: 1,
|
17
|
-
# message: "Counter is required"
|
18
|
-
# }
|
19
|
-
#
|
20
|
-
# input :multiplier,
|
21
|
-
# default: {
|
22
|
-
# is: -> { rand(1..10) },
|
23
|
-
# message: (lambda do |input_key:, actor:|
|
24
|
-
# "Input \"#{input_key}\" is required"
|
25
|
-
# end)
|
26
|
-
# }
|
27
|
-
# end
|
28
|
-
class ServiceActor::Checks::DefaultCheck < ServiceActor::Checks::Base
|
29
|
-
def self.check(result:, input_key:, input_options:, actor:, **)
|
30
|
-
new(
|
31
|
-
result: result,
|
32
|
-
input_key: input_key,
|
33
|
-
input_options: input_options,
|
34
|
-
actor: actor,
|
35
|
-
).check
|
36
|
-
end
|
37
|
-
|
38
|
-
def initialize(result:, input_key:, input_options:, actor:)
|
39
|
-
super()
|
40
|
-
|
41
|
-
@result = result
|
42
|
-
@input_key = input_key
|
43
|
-
@input_options = input_options
|
44
|
-
@actor = actor
|
45
|
-
end
|
46
|
-
|
47
|
-
def check # rubocop:disable Metrics/MethodLength
|
48
|
-
return if @result.key?(@input_key)
|
49
|
-
|
50
|
-
unless @input_options.key?(:default)
|
51
|
-
return add_argument_error(
|
52
|
-
"The \"#{@input_key}\" input on \"#{@actor}\" is missing",
|
53
|
-
)
|
54
|
-
end
|
55
|
-
|
56
|
-
default = @input_options[:default]
|
57
|
-
|
58
|
-
if default.is_a?(Hash)
|
59
|
-
default_for_advanced_mode_with(default)
|
60
|
-
else
|
61
|
-
default_for_normal_mode_with(default)
|
62
|
-
end
|
63
|
-
|
64
|
-
nil
|
65
|
-
end
|
66
|
-
|
67
|
-
private
|
68
|
-
|
69
|
-
def default_for_normal_mode_with(default)
|
70
|
-
default = default.call if default.is_a?(Proc)
|
71
|
-
@result[@input_key] = default
|
72
|
-
end
|
73
|
-
|
74
|
-
def default_for_advanced_mode_with(content)
|
75
|
-
default, message = content.values_at(:is, :message)
|
76
|
-
|
77
|
-
unless default
|
78
|
-
return add_argument_error(
|
79
|
-
message,
|
80
|
-
input_key: @input_key,
|
81
|
-
actor: self.class,
|
82
|
-
)
|
83
|
-
end
|
84
|
-
|
85
|
-
default = default.call if default.is_a?(Proc)
|
86
|
-
@result[@input_key] = default
|
87
|
-
|
88
|
-
message.call(@input_key, self.class)
|
89
|
-
end
|
90
|
-
end
|