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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 76d96df8529ca670b0380e22ae56b4a2b7f32af6bd50e8b311fd6cef056b5f88
4
- data.tar.gz: 7bfea37c9b431c615cccd6e60e00500372aabe3e7b0c8bb4061a2df129a99bc4
3
+ metadata.gz: cd30f97e96402732d11711e040cd4a65ab96962a60338a5358bcae13c9578b06
4
+ data.tar.gz: 42694afe490d30e236c3a42bde46547759fd5adf7d8a52556dc7838a45be937e
5
5
  SHA512:
6
- metadata.gz: 03bae9c5a662507d5170eaa133ac344469fef28c956d49f9ef0d2d619146a365221bb8705e43224bb54c3328262e5fca8adc718760cc536564563058f4eb35e9
7
- data.tar.gz: 7e477e327a244f1fa3fe9e90fbff8f18e1d53f208bac2ea62ec7701c9c82abddc1a8d712a20719f31a6e8c866de697aa8a2ac05e3615f5f62c81f16604ee995a
6
+ metadata.gz: 6d5d01ce6b421df89f2074753c84ee84c9d166219316ee92c69a2d35e186f28faad4482e020c1144cac05caf1ca0ee2e56ee567dd1793f8bbf33c5b87dea31df
7
+ data.tar.gz: '038d365e4f8cff50b7fbc71ba757da5095d62487cd1e043338f386e9c04dd49fd1fe99081d84f7c4ac94f30b3bc35774ea6460825ae98c6380aa17760f8f133d'
@@ -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::Raisable)
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::TypeCheckable)
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
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module ServiceActor::Raisable
3
+ module ServiceActor::Configurable
4
4
  def self.included(base)
5
5
  base.extend(ClassMethods)
6
6
  end
@@ -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
- new(result)._call
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ServiceActor
4
- VERSION = "3.5.0"
4
+ VERSION = "3.6.0"
5
5
  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.5.0
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-20 00:00:00.000000000 Z
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/collectionable.rb
175
- - lib/service_actor/conditionable.rb
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