service_actor 3.5.0 → 3.6.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 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