service_actor 3.5.0 → 3.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/service_actor/base.rb +2 -5
- data/lib/service_actor/checkable.rb +62 -0
- data/lib/service_actor/checks/base.rb +17 -0
- data/lib/service_actor/checks/default_check.rb +90 -0
- data/lib/service_actor/checks/inclusion_check.rb +75 -0
- data/lib/service_actor/checks/must_check.rb +83 -0
- data/lib/service_actor/checks/nil_check.rb +110 -0
- data/lib/service_actor/checks/type_check.rb +101 -0
- data/lib/service_actor/{raisable.rb → configurable.rb} +1 -1
- data/lib/service_actor/core.rb +4 -10
- data/lib/service_actor/defaultable.rb +7 -0
- data/lib/service_actor/version.rb +1 -1
- metadata +10 -7
- data/lib/service_actor/collectionable.rb +0 -69
- data/lib/service_actor/conditionable.rb +0 -75
- data/lib/service_actor/nil_checkable.rb +0 -88
- data/lib/service_actor/type_checkable.rb +0 -91
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cd30f97e96402732d11711e040cd4a65ab96962a60338a5358bcae13c9578b06
|
4
|
+
data.tar.gz: 42694afe490d30e236c3a42bde46547759fd5adf7d8a52556dc7838a45be937e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6d5d01ce6b421df89f2074753c84ee84c9d166219316ee92c69a2d35e186f28faad4482e020c1144cac05caf1ca0ee2e56ee567dd1793f8bbf33c5b87dea31df
|
7
|
+
data.tar.gz: '038d365e4f8cff50b7fbc71ba757da5095d62487cd1e043338f386e9c04dd49fd1fe99081d84f7c4ac94f30b3bc35774ea6460825ae98c6380aa17760f8f133d'
|
data/lib/service_actor/base.rb
CHANGED
@@ -6,15 +6,12 @@ module ServiceActor::Base
|
|
6
6
|
def self.included(base)
|
7
7
|
# Essential mechanics
|
8
8
|
base.include(ServiceActor::Core)
|
9
|
-
base.include(ServiceActor::
|
9
|
+
base.include(ServiceActor::Configurable)
|
10
10
|
base.include(ServiceActor::Attributable)
|
11
11
|
base.include(ServiceActor::Playable)
|
12
12
|
|
13
13
|
# Extra concerns
|
14
|
-
base.include(ServiceActor::
|
15
|
-
base.include(ServiceActor::NilCheckable)
|
16
|
-
base.include(ServiceActor::Conditionable)
|
17
|
-
base.include(ServiceActor::Collectionable)
|
14
|
+
base.include(ServiceActor::Checkable)
|
18
15
|
base.include(ServiceActor::Defaultable)
|
19
16
|
base.include(ServiceActor::Failable)
|
20
17
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ServiceActor::Checkable
|
4
|
+
def self.included(base)
|
5
|
+
base.prepend(PrependedMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module PrependedMethods
|
9
|
+
def _call
|
10
|
+
self.service_actor_argument_errors = []
|
11
|
+
|
12
|
+
service_actor_checks_for(:input)
|
13
|
+
super
|
14
|
+
service_actor_checks_for(:output)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_accessor :service_actor_argument_errors
|
20
|
+
|
21
|
+
# rubocop:disable Metrics/MethodLength
|
22
|
+
def service_actor_checks_for(origin)
|
23
|
+
self.class.public_send("#{origin}s").each do |input_key, input_options|
|
24
|
+
input_options.each do |check_name, check_conditions|
|
25
|
+
check_classes.each do |check_class|
|
26
|
+
argument_errors = check_class.check(
|
27
|
+
check_name: check_name,
|
28
|
+
origin: origin,
|
29
|
+
input_key: input_key,
|
30
|
+
actor: self.class,
|
31
|
+
conditions: check_conditions,
|
32
|
+
result: result,
|
33
|
+
input_options: input_options,
|
34
|
+
)
|
35
|
+
|
36
|
+
service_actor_argument_errors.push(*argument_errors)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
raise_actor_argument_errors
|
41
|
+
end
|
42
|
+
end
|
43
|
+
# rubocop:enable Metrics/MethodLength
|
44
|
+
|
45
|
+
def raise_actor_argument_errors
|
46
|
+
return if service_actor_argument_errors.empty?
|
47
|
+
|
48
|
+
raise self.class.argument_error_class,
|
49
|
+
service_actor_argument_errors.first
|
50
|
+
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
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ServiceActor::Checks::Base
|
4
|
+
def initialize
|
5
|
+
@argument_errors = []
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_reader :argument_errors
|
9
|
+
|
10
|
+
protected
|
11
|
+
|
12
|
+
def add_argument_error(message, **arguments)
|
13
|
+
message = message.call(**arguments) if message.is_a?(Proc)
|
14
|
+
|
15
|
+
argument_errors.push(message)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,90 @@
|
|
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
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
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
|
+
class ServiceActor::Checks::InclusionCheck < ServiceActor::Checks::Base
|
22
|
+
DEFAULT_MESSAGE = lambda do |input_key:, actor:, inclusion_in:, value:|
|
23
|
+
"The \"#{input_key}\" input must be included " \
|
24
|
+
"in #{inclusion_in.inspect} on \"#{actor}\" " \
|
25
|
+
"instead of #{value.inspect}"
|
26
|
+
end
|
27
|
+
|
28
|
+
private_constant :DEFAULT_MESSAGE
|
29
|
+
|
30
|
+
def self.check(check_name:, input_key:, actor:, conditions:, result:, **) # rubocop:disable Metrics/ParameterLists
|
31
|
+
# DEPRECATED: `in` is deprecated in favor of `inclusion`.
|
32
|
+
return unless %i[inclusion in].include?(check_name)
|
33
|
+
|
34
|
+
new(
|
35
|
+
input_key: input_key,
|
36
|
+
actor: actor,
|
37
|
+
inclusion: conditions,
|
38
|
+
value: result[input_key],
|
39
|
+
).check
|
40
|
+
end
|
41
|
+
|
42
|
+
def initialize(input_key:, actor:, inclusion:, value:)
|
43
|
+
super()
|
44
|
+
|
45
|
+
@input_key = input_key
|
46
|
+
@actor = actor
|
47
|
+
@inclusion = inclusion
|
48
|
+
@value = value
|
49
|
+
end
|
50
|
+
|
51
|
+
def check
|
52
|
+
inclusion_in, message = define_inclusion_and_message
|
53
|
+
|
54
|
+
return if inclusion_in.nil?
|
55
|
+
return if inclusion_in.include?(@value)
|
56
|
+
|
57
|
+
add_argument_error(
|
58
|
+
message,
|
59
|
+
input_key: @input_key,
|
60
|
+
actor: @actor,
|
61
|
+
inclusion_in: inclusion_in,
|
62
|
+
value: @value,
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def define_inclusion_and_message
|
69
|
+
if @inclusion.is_a?(Hash)
|
70
|
+
@inclusion.values_at(:in, :message)
|
71
|
+
else
|
72
|
+
[@inclusion, DEFAULT_MESSAGE]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Add checks to your inputs, by calling lambdas with the name of you choice
|
4
|
+
# under the "must" key.
|
5
|
+
#
|
6
|
+
# Will raise an error if any check returns a truthy value.
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
#
|
10
|
+
# class Pay < Actor
|
11
|
+
# input :provider,
|
12
|
+
# must: {
|
13
|
+
# exist: -> provider { PROVIDERS.include?(provider) },
|
14
|
+
# }
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# class Pay < Actor
|
18
|
+
# input :provider,
|
19
|
+
# must: {
|
20
|
+
# exist: {
|
21
|
+
# is: -> provider { PROVIDERS.include?(provider) },
|
22
|
+
# message: (lambda do |input_key:, check_name:, actor:, value:|
|
23
|
+
# "The specified provider \"#{value}\" was not found."
|
24
|
+
# end)
|
25
|
+
# }
|
26
|
+
# }
|
27
|
+
# end
|
28
|
+
class ServiceActor::Checks::MustCheck < ServiceActor::Checks::Base
|
29
|
+
DEFAULT_MESSAGE = lambda do |input_key:, actor:, check_name:, value:|
|
30
|
+
"The \"#{input_key}\" input on \"#{actor}\" must \"#{check_name}\" " \
|
31
|
+
"but was #{value.inspect}"
|
32
|
+
end
|
33
|
+
|
34
|
+
private_constant :DEFAULT_MESSAGE
|
35
|
+
|
36
|
+
def self.check(check_name:, input_key:, actor:, conditions:, result:, **) # rubocop:disable Metrics/ParameterLists
|
37
|
+
return unless check_name == :must
|
38
|
+
|
39
|
+
new(
|
40
|
+
input_key: input_key,
|
41
|
+
actor: actor,
|
42
|
+
nested_checks: conditions,
|
43
|
+
value: result[input_key],
|
44
|
+
).check
|
45
|
+
end
|
46
|
+
|
47
|
+
def initialize(input_key:, actor:, nested_checks:, value:)
|
48
|
+
super()
|
49
|
+
|
50
|
+
@input_key = input_key
|
51
|
+
@actor = actor
|
52
|
+
@nested_checks = nested_checks
|
53
|
+
@value = value
|
54
|
+
end
|
55
|
+
|
56
|
+
def check
|
57
|
+
@nested_checks.each do |nested_check_name, nested_check_conditions|
|
58
|
+
check, message = define_check_and_message_from(nested_check_conditions)
|
59
|
+
|
60
|
+
next if check.call(@value)
|
61
|
+
|
62
|
+
add_argument_error(
|
63
|
+
message,
|
64
|
+
input_key: @input_key,
|
65
|
+
actor: @actor,
|
66
|
+
check_name: nested_check_name,
|
67
|
+
value: @value,
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
@argument_errors
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def define_check_and_message_from(nested_check_conditions)
|
77
|
+
if nested_check_conditions.is_a?(Hash)
|
78
|
+
nested_check_conditions.values_at(:is, :message)
|
79
|
+
else
|
80
|
+
[nested_check_conditions, DEFAULT_MESSAGE]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Ensure your inputs and outputs are not nil by adding `allow_nil: false`.
|
4
|
+
#
|
5
|
+
# Example:
|
6
|
+
#
|
7
|
+
# class CreateUser < Actor
|
8
|
+
# input :name, allow_nil: false
|
9
|
+
# output :user, allow_nil: false
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# class CreateUser < Actor
|
13
|
+
# input :name,
|
14
|
+
# allow_nil: {
|
15
|
+
# is: false,
|
16
|
+
# message: (lambda do |origin:, input_key:, actor:|
|
17
|
+
# "The value \"#{input_key}\" cannot be empty"
|
18
|
+
# end)
|
19
|
+
# }
|
20
|
+
#
|
21
|
+
# input :phone, allow_nil: { is: false, message: "Phone must be present" }
|
22
|
+
#
|
23
|
+
# output :user,
|
24
|
+
# allow_nil: {
|
25
|
+
# is: false,
|
26
|
+
# message: (lambda do |origin:, input_key:, actor:|
|
27
|
+
# "The value \"#{input_key}\" cannot be empty"
|
28
|
+
# end)
|
29
|
+
# }
|
30
|
+
# end
|
31
|
+
class ServiceActor::Checks::NilCheck < ServiceActor::Checks::Base
|
32
|
+
DEFAULT_MESSAGE = lambda do |origin:, input_key:, actor:|
|
33
|
+
"The \"#{input_key}\" #{origin} on \"#{actor}\" does not allow nil values"
|
34
|
+
end
|
35
|
+
|
36
|
+
private_constant :DEFAULT_MESSAGE
|
37
|
+
|
38
|
+
def self.check( # rubocop:disable Metrics/ParameterLists
|
39
|
+
origin:,
|
40
|
+
input_key:,
|
41
|
+
input_options:,
|
42
|
+
actor:,
|
43
|
+
conditions:,
|
44
|
+
result:,
|
45
|
+
**
|
46
|
+
) # do
|
47
|
+
new(
|
48
|
+
origin: origin,
|
49
|
+
input_key: input_key,
|
50
|
+
input_options: input_options,
|
51
|
+
actor: actor,
|
52
|
+
allow_nil: conditions,
|
53
|
+
value: result[input_key],
|
54
|
+
).check
|
55
|
+
end
|
56
|
+
|
57
|
+
def initialize( # rubocop:disable Metrics/ParameterLists
|
58
|
+
origin:,
|
59
|
+
input_key:,
|
60
|
+
input_options:,
|
61
|
+
actor:,
|
62
|
+
allow_nil:,
|
63
|
+
value:
|
64
|
+
) # do
|
65
|
+
super()
|
66
|
+
|
67
|
+
@origin = origin
|
68
|
+
@input_key = input_key
|
69
|
+
@input_options = input_options
|
70
|
+
@actor = actor
|
71
|
+
@allow_nil = allow_nil
|
72
|
+
@value = value
|
73
|
+
end
|
74
|
+
|
75
|
+
def check
|
76
|
+
return unless @value.nil?
|
77
|
+
|
78
|
+
allow_nil, message =
|
79
|
+
define_allow_nil_and_message_from(@input_options[:allow_nil])
|
80
|
+
|
81
|
+
return if allow_nil?(allow_nil)
|
82
|
+
|
83
|
+
add_argument_error(
|
84
|
+
message,
|
85
|
+
origin: @origin,
|
86
|
+
input_key: @input_key,
|
87
|
+
actor: @actor,
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def define_allow_nil_and_message_from(allow_nil)
|
94
|
+
if allow_nil.is_a?(Hash)
|
95
|
+
allow_nil.values_at(:is, :message)
|
96
|
+
else
|
97
|
+
[allow_nil, DEFAULT_MESSAGE]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def allow_nil?(tmp_allow_nil)
|
102
|
+
return tmp_allow_nil unless tmp_allow_nil.nil?
|
103
|
+
|
104
|
+
if @input_options.key?(:default) && @input_options[:default].nil?
|
105
|
+
return true
|
106
|
+
end
|
107
|
+
|
108
|
+
!@input_options[:type]
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Adds `type:` checking to inputs and outputs. Accepts class names or classes
|
4
|
+
# that should match an ancestor. Also accepts arrays.
|
5
|
+
#
|
6
|
+
# Example:
|
7
|
+
#
|
8
|
+
# class ReduceOrderAmount < Actor
|
9
|
+
# input :order, type: "Order"
|
10
|
+
# input :amount, type: [Integer, Float]
|
11
|
+
# input :bonus_applied, type: [TrueClass, FalseClass]
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# class ReduceOrderAmount < Actor
|
15
|
+
# input :order, type: { is: Order, message: "Order is required" }
|
16
|
+
# input :amount, type: { is: Integer, message: "Incorrect amount" }
|
17
|
+
#
|
18
|
+
# input :bonus_applied,
|
19
|
+
# type: {
|
20
|
+
# is: [TrueClass, FalseClass],
|
21
|
+
# message: (lambda do |origin:, input_key:, actor:, expected_type:, given_type:| # rubocop:disable Layout/LineLength
|
22
|
+
# "Wrong type \"#{given_type}\" for \"#{input_key}\". " \
|
23
|
+
# "Expected: \"#{expected_type}\""
|
24
|
+
# end)
|
25
|
+
# }
|
26
|
+
# end
|
27
|
+
class ServiceActor::Checks::TypeCheck < ServiceActor::Checks::Base
|
28
|
+
DEFAULT_MESSAGE = lambda do
|
29
|
+
|origin:, input_key:, actor:, expected_type:, given_type:|
|
30
|
+
|
31
|
+
"The \"#{input_key}\" #{origin} on \"#{actor}\" must be of type " \
|
32
|
+
"\"#{expected_type}\" but was \"#{given_type}\""
|
33
|
+
end
|
34
|
+
|
35
|
+
private_constant :DEFAULT_MESSAGE
|
36
|
+
|
37
|
+
def self.check( # rubocop:disable Metrics/ParameterLists
|
38
|
+
check_name:,
|
39
|
+
origin:,
|
40
|
+
input_key:,
|
41
|
+
actor:,
|
42
|
+
conditions:,
|
43
|
+
result:,
|
44
|
+
**
|
45
|
+
) # do
|
46
|
+
return unless check_name == :type
|
47
|
+
|
48
|
+
new(
|
49
|
+
origin: origin,
|
50
|
+
input_key: input_key,
|
51
|
+
actor: actor,
|
52
|
+
type_definition: conditions,
|
53
|
+
given_type: result[input_key],
|
54
|
+
).check
|
55
|
+
end
|
56
|
+
|
57
|
+
def initialize(origin:, input_key:, actor:, type_definition:, given_type:)
|
58
|
+
super()
|
59
|
+
|
60
|
+
@origin = origin
|
61
|
+
@input_key = input_key
|
62
|
+
@actor = actor
|
63
|
+
@type_definition = type_definition
|
64
|
+
@given_type = given_type
|
65
|
+
end
|
66
|
+
|
67
|
+
def check
|
68
|
+
return if @type_definition.nil?
|
69
|
+
return if @given_type.nil?
|
70
|
+
|
71
|
+
types, message = define_types_and_message
|
72
|
+
|
73
|
+
return if types.any? { |type| @given_type.is_a?(type) }
|
74
|
+
|
75
|
+
add_argument_error(
|
76
|
+
message,
|
77
|
+
origin: @origin,
|
78
|
+
input_key: @input_key,
|
79
|
+
actor: @actor,
|
80
|
+
expected_type: types.join(", "),
|
81
|
+
given_type: @given_type.class,
|
82
|
+
)
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def define_types_and_message
|
88
|
+
if @type_definition.is_a?(Hash)
|
89
|
+
@type_definition, message =
|
90
|
+
@type_definition.values_at(:is, :message)
|
91
|
+
else
|
92
|
+
message = DEFAULT_MESSAGE
|
93
|
+
end
|
94
|
+
|
95
|
+
types = Array(@type_definition).map do |name|
|
96
|
+
name.is_a?(String) ? Object.const_get(name) : name
|
97
|
+
end
|
98
|
+
|
99
|
+
[types, message]
|
100
|
+
end
|
101
|
+
end
|
data/lib/service_actor/core.rb
CHANGED
@@ -11,7 +11,10 @@ module ServiceActor::Core
|
|
11
11
|
# CreateUser.call(name: "Joe")
|
12
12
|
def call(result = nil, **arguments)
|
13
13
|
result = ServiceActor::Result.to_result(result).merge!(arguments)
|
14
|
-
|
14
|
+
|
15
|
+
instance = new(result)
|
16
|
+
instance._call
|
17
|
+
|
15
18
|
result
|
16
19
|
end
|
17
20
|
|
@@ -54,13 +57,4 @@ module ServiceActor::Core
|
|
54
57
|
def fail!(**arguments)
|
55
58
|
result.fail!(self.class.failure_class, **arguments)
|
56
59
|
end
|
57
|
-
|
58
|
-
private
|
59
|
-
|
60
|
-
# Raises an error depending on the mode
|
61
|
-
def raise_error_with(message, **arguments)
|
62
|
-
message = message.call(**arguments) if message.is_a?(Proc)
|
63
|
-
|
64
|
-
raise self.class.argument_error_class, message
|
65
|
-
end
|
66
60
|
end
|
@@ -72,5 +72,12 @@ module ServiceActor::Defaultable
|
|
72
72
|
|
73
73
|
message.call(key, self.class)
|
74
74
|
end
|
75
|
+
|
76
|
+
# Raises an error depending on the mode
|
77
|
+
def raise_error_with(message, **arguments)
|
78
|
+
message = message.call(**arguments) if message.is_a?(Proc)
|
79
|
+
|
80
|
+
raise self.class.argument_error_class, message
|
81
|
+
end
|
75
82
|
end
|
76
83
|
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.6.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: 2022-12-
|
11
|
+
date: 2022-12-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: zeitwerk
|
@@ -171,19 +171,22 @@ files:
|
|
171
171
|
- lib/service_actor/argument_error.rb
|
172
172
|
- lib/service_actor/attributable.rb
|
173
173
|
- lib/service_actor/base.rb
|
174
|
-
- lib/service_actor/
|
175
|
-
- lib/service_actor/
|
174
|
+
- lib/service_actor/checkable.rb
|
175
|
+
- lib/service_actor/checks/base.rb
|
176
|
+
- lib/service_actor/checks/default_check.rb
|
177
|
+
- lib/service_actor/checks/inclusion_check.rb
|
178
|
+
- lib/service_actor/checks/must_check.rb
|
179
|
+
- lib/service_actor/checks/nil_check.rb
|
180
|
+
- lib/service_actor/checks/type_check.rb
|
181
|
+
- lib/service_actor/configurable.rb
|
176
182
|
- lib/service_actor/core.rb
|
177
183
|
- lib/service_actor/defaultable.rb
|
178
184
|
- lib/service_actor/error.rb
|
179
185
|
- lib/service_actor/failable.rb
|
180
186
|
- lib/service_actor/failure.rb
|
181
|
-
- lib/service_actor/nil_checkable.rb
|
182
187
|
- lib/service_actor/playable.rb
|
183
|
-
- lib/service_actor/raisable.rb
|
184
188
|
- lib/service_actor/result.rb
|
185
189
|
- lib/service_actor/support/loader.rb
|
186
|
-
- lib/service_actor/type_checkable.rb
|
187
190
|
- lib/service_actor/version.rb
|
188
191
|
homepage: https://github.com/sunny/actor
|
189
192
|
licenses:
|
@@ -1,69 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
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}"
|
31
|
-
end
|
32
|
-
|
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)
|
43
|
-
|
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
|
58
|
-
|
59
|
-
private
|
60
|
-
|
61
|
-
def define_inclusion_from(inclusion)
|
62
|
-
if inclusion.is_a?(Hash)
|
63
|
-
inclusion.values_at(:in, :message)
|
64
|
-
else
|
65
|
-
[inclusion, DEFAULT_MESSAGE]
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
@@ -1,75 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Add checks to your inputs, by calling lambdas with the name of you choice
|
4
|
-
# under the "must" key.
|
5
|
-
#
|
6
|
-
# Will raise an error if any check returns a truthy value.
|
7
|
-
#
|
8
|
-
# Example:
|
9
|
-
#
|
10
|
-
# class Pay < Actor
|
11
|
-
# input :provider,
|
12
|
-
# must: {
|
13
|
-
# exist: -> provider { PROVIDERS.include?(provider) },
|
14
|
-
# }
|
15
|
-
# end
|
16
|
-
#
|
17
|
-
# class Pay < Actor
|
18
|
-
# input :provider,
|
19
|
-
# must: {
|
20
|
-
# exist: {
|
21
|
-
# is: -> provider { PROVIDERS.include?(provider) },
|
22
|
-
# message: (lambda do |input_key:, check_name:, actor:, value:|
|
23
|
-
# "The specified provider \"#{value}\" was not found."
|
24
|
-
# end)
|
25
|
-
# }
|
26
|
-
# }
|
27
|
-
# end
|
28
|
-
module ServiceActor::Conditionable
|
29
|
-
def self.included(base)
|
30
|
-
base.prepend(PrependedMethods)
|
31
|
-
end
|
32
|
-
|
33
|
-
module PrependedMethods
|
34
|
-
DEFAULT_MESSAGE = lambda do |input_key:, check_name:, actor:, value:|
|
35
|
-
"The \"#{input_key}\" input on \"#{actor}\" must \"#{check_name}\" " \
|
36
|
-
"but was #{value.inspect}"
|
37
|
-
end
|
38
|
-
|
39
|
-
private_constant :DEFAULT_MESSAGE
|
40
|
-
|
41
|
-
def _call # rubocop:disable Metrics/MethodLength
|
42
|
-
self.class.inputs.each do |key, options|
|
43
|
-
next unless options[:must]
|
44
|
-
|
45
|
-
options[:must].each do |check_name, check|
|
46
|
-
value = result[key]
|
47
|
-
|
48
|
-
check, message = define_check_from(check)
|
49
|
-
|
50
|
-
next if check.call(value)
|
51
|
-
|
52
|
-
raise_error_with(
|
53
|
-
message,
|
54
|
-
input_key: key,
|
55
|
-
check_name: check_name,
|
56
|
-
actor: self.class,
|
57
|
-
value: value,
|
58
|
-
)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
super
|
63
|
-
end
|
64
|
-
|
65
|
-
private
|
66
|
-
|
67
|
-
def define_check_from(check)
|
68
|
-
if check.is_a?(Hash)
|
69
|
-
check.values_at(:is, :message)
|
70
|
-
else
|
71
|
-
[check, DEFAULT_MESSAGE]
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
@@ -1,88 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Ensure your inputs and outputs are not nil by adding `allow_nil: false`.
|
4
|
-
#
|
5
|
-
# Example:
|
6
|
-
#
|
7
|
-
# class CreateUser < Actor
|
8
|
-
# input :name, allow_nil: false
|
9
|
-
# output :user, allow_nil: false
|
10
|
-
# end
|
11
|
-
#
|
12
|
-
# class CreateUser < Actor
|
13
|
-
# input :name,
|
14
|
-
# allow_nil: {
|
15
|
-
# is: false,
|
16
|
-
# message: (lambda do |origin:, input_key:, actor:|
|
17
|
-
# "The value \"#{input_key}\" cannot be empty"
|
18
|
-
# end)
|
19
|
-
# }
|
20
|
-
#
|
21
|
-
# input :phone, allow_nil: { is: false, message: "Phone must be present" }
|
22
|
-
#
|
23
|
-
# output :user,
|
24
|
-
# allow_nil: {
|
25
|
-
# is: false,
|
26
|
-
# message: (lambda do |origin:, input_key:, actor:|
|
27
|
-
# "The value \"#{input_key}\" cannot be empty"
|
28
|
-
# end)
|
29
|
-
# }
|
30
|
-
# end
|
31
|
-
module ServiceActor::NilCheckable
|
32
|
-
def self.included(base)
|
33
|
-
base.prepend(PrependedMethods)
|
34
|
-
end
|
35
|
-
|
36
|
-
module PrependedMethods
|
37
|
-
DEFAULT_MESSAGE = lambda do |origin:, input_key:, actor:|
|
38
|
-
"The \"#{input_key}\" #{origin} on \"#{actor}\" does not allow " \
|
39
|
-
"nil values"
|
40
|
-
end
|
41
|
-
|
42
|
-
private_constant :DEFAULT_MESSAGE
|
43
|
-
|
44
|
-
def _call
|
45
|
-
check_context_for_nil(self.class.inputs, origin: "input")
|
46
|
-
|
47
|
-
super
|
48
|
-
|
49
|
-
check_context_for_nil(self.class.outputs, origin: "output")
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
53
|
-
|
54
|
-
def check_context_for_nil(definitions, origin:)
|
55
|
-
definitions.each do |key, options|
|
56
|
-
value = result[key]
|
57
|
-
|
58
|
-
next unless value.nil?
|
59
|
-
|
60
|
-
allow_nil, message = define_allow_nil_from(options[:allow_nil])
|
61
|
-
|
62
|
-
next if allow_nil?(allow_nil, options)
|
63
|
-
|
64
|
-
raise_error_with(
|
65
|
-
message,
|
66
|
-
origin: origin,
|
67
|
-
input_key: key,
|
68
|
-
actor: self.class,
|
69
|
-
)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def define_allow_nil_from(allow_nil)
|
74
|
-
if allow_nil.is_a?(Hash)
|
75
|
-
allow_nil.values_at(:is, :message)
|
76
|
-
else
|
77
|
-
[allow_nil, DEFAULT_MESSAGE]
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def allow_nil?(allow_nil, options)
|
82
|
-
return allow_nil unless allow_nil.nil?
|
83
|
-
return true if options.key?(:default) && options[:default].nil?
|
84
|
-
|
85
|
-
!options[:type]
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
@@ -1,91 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Adds `type:` checking to inputs and outputs. Accepts class names or classes
|
4
|
-
# that should match an ancestor. Also accepts arrays.
|
5
|
-
#
|
6
|
-
# Example:
|
7
|
-
#
|
8
|
-
# class ReduceOrderAmount < Actor
|
9
|
-
# input :order, type: "Order"
|
10
|
-
# input :amount, type: [Integer, Float]
|
11
|
-
# input :bonus_applied, type: [TrueClass, FalseClass]
|
12
|
-
# end
|
13
|
-
#
|
14
|
-
# class ReduceOrderAmount < Actor
|
15
|
-
# input :order, type: { is: Order, message: "Order is required" }
|
16
|
-
# input :amount, type: { is: Integer, message: "Incorrect amount" }
|
17
|
-
#
|
18
|
-
# input :bonus_applied,
|
19
|
-
# type: {
|
20
|
-
# is: [TrueClass, FalseClass],
|
21
|
-
# message: (lambda do |origin:, input_key:, actor:, expected_type:, given_type:| # rubocop:disable Layout/LineLength
|
22
|
-
# "Wrong type \"#{given_type}\" for \"#{input_key}\". " \
|
23
|
-
# "Expected: \"#{expected_type}\""
|
24
|
-
# end)
|
25
|
-
# }
|
26
|
-
# end
|
27
|
-
module ServiceActor::TypeCheckable
|
28
|
-
def self.included(base)
|
29
|
-
base.prepend(PrependedMethods)
|
30
|
-
end
|
31
|
-
|
32
|
-
module PrependedMethods
|
33
|
-
DEFAULT_MESSAGE = lambda do
|
34
|
-
|origin:, input_key:, actor:, expected_type:, given_type:|
|
35
|
-
|
36
|
-
"The \"#{input_key}\" #{origin} on \"#{actor}\" must be of type " \
|
37
|
-
"\"#{expected_type}\" but was \"#{given_type}\""
|
38
|
-
end
|
39
|
-
|
40
|
-
private_constant :DEFAULT_MESSAGE
|
41
|
-
|
42
|
-
def _call
|
43
|
-
check_type_definitions(self.class.inputs, origin: "input")
|
44
|
-
|
45
|
-
super
|
46
|
-
|
47
|
-
check_type_definitions(self.class.outputs, origin: "output")
|
48
|
-
end
|
49
|
-
|
50
|
-
private
|
51
|
-
|
52
|
-
def check_type_definitions(definitions, origin:) # rubocop:disable Metrics/MethodLength
|
53
|
-
definitions.each do |key, options|
|
54
|
-
type_definition = options[:type] || next
|
55
|
-
value = result[key] || next
|
56
|
-
|
57
|
-
types, message = define_types_with(type_definition)
|
58
|
-
|
59
|
-
next if types.any? { |type| value.is_a?(type) }
|
60
|
-
|
61
|
-
raise_error_with(
|
62
|
-
message,
|
63
|
-
origin: origin,
|
64
|
-
input_key: key,
|
65
|
-
actor: self.class,
|
66
|
-
expected_type: types.join(", "),
|
67
|
-
given_type: value.class,
|
68
|
-
)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def define_types_with(type_definition)
|
73
|
-
if type_definition.is_a?(Hash)
|
74
|
-
type_definition, message =
|
75
|
-
type_definition.values_at(:is, :message)
|
76
|
-
else
|
77
|
-
message = DEFAULT_MESSAGE
|
78
|
-
end
|
79
|
-
|
80
|
-
types = types_for_definition(type_definition)
|
81
|
-
|
82
|
-
[types, message]
|
83
|
-
end
|
84
|
-
|
85
|
-
def types_for_definition(type_definition)
|
86
|
-
Array(type_definition).map do |name|
|
87
|
-
name.is_a?(String) ? Object.const_get(name) : name
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|