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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5654d6b33f83eaccbc860a7072e0f3f3fcf694e4e9ba5fdb4224ab6bd180f860
4
- data.tar.gz: 755220b315eccaf564f6eeb85c234a4da1deeb24ea3e71740f7bba3cb5029135
3
+ metadata.gz: a852443e88e3b6957cbaf093330d08691ba8e862826ac3b745fa5510d1b0273c
4
+ data.tar.gz: 21a7648a3e3302600b1946285a76b0e80c70ba1550776dfd07057718397d5f66
5
5
  SHA512:
6
- metadata.gz: 753d97cc97069242febc4212ca78c3f1149083dd928578e581c52073f556d40a6f275d08d28a28b97e1fd04841c05e78e3be3b122c2005cbd2775b7257f93b91
7
- data.tar.gz: 511d62768af23e9b84aa8ae6d268901f5227caf54956cd6261c1835d518823413c43dae16d278ecf63d45737df4373dbb755b5d1e779afd4c90d7bec78848bc4
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 =~ /^aeiou/ ? 'an' : 'a' }
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
- "Expected: \"#{expected_type}\""
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
- def self.included(base)
11
- base.extend(ClassMethods)
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
- # For avoid method redefined warning messages.
30
- alias_method name, name if method_defined?(name)
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
@@ -3,16 +3,18 @@
3
3
  require "service_actor/support/loader"
4
4
 
5
5
  module ServiceActor::Base
6
- def self.included(base)
7
- # Essential mechanics
8
- base.include(ServiceActor::Core)
9
- base.include(ServiceActor::Configurable)
10
- base.include(ServiceActor::Attributable)
11
- base.include(ServiceActor::Playable)
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
- # Extra concerns
14
- base.include(ServiceActor::Checkable)
15
- base.include(ServiceActor::Defaultable)
16
- base.include(ServiceActor::Failable)
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
- def self.included(base)
5
- base.prepend(PrependedMethods)
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
- self.class.public_send("#{origin}s").each do |input_key, input_options|
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
@@ -1,6 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class ServiceActor::Checks::Base
4
+ class << self
5
+ def applicable_to_origin?(_origin)
6
+ true
7
+ end
8
+ end
9
+
4
10
  def initialize
5
11
  @argument_errors = []
6
12
  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
- "instead of #{value.inspect}"
25
+ "instead of #{value.inspect}"
26
26
  end
27
27
 
28
28
  private_constant :DEFAULT_MESSAGE
29
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)
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
- new(
35
- input_key: input_key,
36
- actor: actor,
37
- inclusion: conditions,
38
- value: result[input_key],
39
- ).check
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
- def self.check(check_name:, input_key:, actor:, conditions:, result:, **) # rubocop:disable Metrics/ParameterLists
37
- return unless check_name == :must
36
+ class << self
37
+ def check(check_name:, input_key:, actor:, conditions:, result:, **)
38
+ return unless check_name == :must
38
39
 
39
- new(
40
- input_key: input_key,
41
- actor: actor,
42
- nested_checks: conditions,
43
- value: result[input_key],
44
- ).check
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
- 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
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
- 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
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
- def self.included(base)
5
- base.extend(ClassMethods)
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
- attr_accessor :argument_error_class, :failure_class
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
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ServiceActor::Core
4
- def self.included(base)
5
- base.extend(ClassMethods)
4
+ class << self
5
+ def included(base)
6
+ base.extend(ClassMethods)
7
+ end
6
8
  end
7
9
 
8
10
  module ClassMethods
@@ -26,8 +26,10 @@
26
26
  # }
27
27
  # end
28
28
  module ServiceActor::Defaultable
29
- def self.included(base)
30
- base.prepend(PrependedMethods)
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
- def self.included(base)
11
- base.extend(ClassMethods)
12
- base.prepend(PrependedMethods)
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
- def self.included(base)
13
- base.extend(ClassMethods)
14
- base.prepend(PrependedMethods)
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, new, original)
43
- actor[new] = actor[original]
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
@@ -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 < OpenStruct
8
- def self.to_result(data)
9
- return data if data.is_a?(self)
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
- new(data.to_h)
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
- merge!(failure?: true)
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
- self[:failure?] || false
51
+ data[:failure] || false
36
52
  end
37
53
 
38
54
  def merge!(result)
39
- result.each_pair do |key, value|
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
- to_h[name]
65
+ data[name]
52
66
  end
53
67
 
54
- # Defined here to override the method on `Object`.
55
- def display
56
- to_h.fetch(:display)
68
+ def []=(key, value)
69
+ data[key] = value
57
70
  end
58
71
 
59
- private
72
+ def delete!(key)
73
+ data.delete(key)
74
+ end
60
75
 
61
- def respond_to_missing?(method_name, include_private = false)
62
- method_name.to_s.end_with?("?") || super
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 method_missing(symbol, *args)
66
- attribute = symbol.to_s.chomp("?")
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
- if symbol.to_s.end_with?("?") && respond_to?(attribute)
69
- define_singleton_method symbol do
70
- value = send(attribute.to_sym)
87
+ private
71
88
 
72
- # Same as ActiveSupport’s #present?
73
- value.respond_to?(:empty?) ? !value.empty? : !!value
74
- end
89
+ attr_reader :data
75
90
 
76
- return send(symbol)
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
- super symbol, *args
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ServiceActor
4
- VERSION = "3.7.0"
4
+ VERSION = "3.8.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.7.0
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: 2023-05-29 00:00:00.000000000 Z
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: '12.0'
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: 12.0.1
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: '12.0'
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: 12.0.1
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.4'
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