service_actor 3.7.0 → 3.8.1

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: 5654d6b33f83eaccbc860a7072e0f3f3fcf694e4e9ba5fdb4224ab6bd180f860
4
- data.tar.gz: 755220b315eccaf564f6eeb85c234a4da1deeb24ea3e71740f7bba3cb5029135
3
+ metadata.gz: ca3921af29250e8b2b68925ede79f0ad223d87bfa1be362e8e50cc178999658f
4
+ data.tar.gz: 86f770c478e718d0c1c20f0809ade458d1687cabeac24b35cfa39d35430ea35d
5
5
  SHA512:
6
- metadata.gz: 753d97cc97069242febc4212ca78c3f1149083dd928578e581c52073f556d40a6f275d08d28a28b97e1fd04841c05e78e3be3b122c2005cbd2775b7257f93b91
7
- data.tar.gz: 511d62768af23e9b84aa8ae6d268901f5227caf54956cd6261c1835d518823413c43dae16d278ecf63d45737df4373dbb755b5d1e779afd4c90d7bec78848bc4
6
+ metadata.gz: 29cfec59fba54ea9ea8ddd4a250588513ad3132d5ecef79f6d6f43a58c5fa6aded756bc34cd6e698f233e4d584072f7f7e7342e8e3ee71cb1027463334450c7b
7
+ data.tar.gz: 73b264aa00a02f23513da5b3b61b5a4a2b9a5afc72bb627d929f0a27c44a5cebf01b561d7e62c17fdb5293886f1bf65a6af0b69d0bd68674c2cad19eaef92ab3
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,57 @@
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
13
+
14
+ %i[
15
+ class
16
+ is_a?
17
+ kind_of?
18
+ send
19
+ public_send
20
+ tap
21
+ then
22
+ block_given?
23
+ object_id
24
+ instance_variables
25
+ ].each do |method_name|
26
+ define_method(method_name, ::Kernel.instance_method(method_name))
27
+ end
10
28
 
11
- new(data.to_h)
29
+ alias_method :yield_self, :then
30
+
31
+ def initialize(data = {})
32
+ @data = data.to_h
33
+ end
34
+
35
+ def to_h
36
+ data
12
37
  end
13
38
 
14
39
  def inspect
15
40
  "<#{self.class.name} #{to_h}>"
16
41
  end
17
42
 
43
+ alias_method :pretty_print, :inspect
44
+
18
45
  def fail!(failure_class = nil, result = {})
19
- if failure_class.nil? || failure_class.is_a?(Hash)
46
+ if failure_class.nil? || failure_class.is_a?(::Hash)
20
47
  result = failure_class.to_h
21
- failure_class = ServiceActor::Failure
48
+ failure_class = ::ServiceActor::Failure
22
49
  end
23
50
 
24
- merge!(result)
25
- merge!(failure?: true)
51
+ data.merge!(result)
52
+ data[:failure] = true
26
53
 
27
- raise failure_class, self
54
+ ::Kernel.raise failure_class, self
28
55
  end
29
56
 
30
57
  def success?
@@ -32,13 +59,15 @@ class ServiceActor::Result < OpenStruct
32
59
  end
33
60
 
34
61
  def failure?
35
- self[:failure?] || false
62
+ data[:failure] || false
63
+ end
64
+
65
+ def error
66
+ data[:error] || nil
36
67
  end
37
68
 
38
69
  def merge!(result)
39
- result.each_pair do |key, value|
40
- self[key] = value
41
- end
70
+ data.merge!(result)
42
71
 
43
72
  self
44
73
  end
@@ -48,34 +77,63 @@ class ServiceActor::Result < OpenStruct
48
77
  end
49
78
 
50
79
  def [](name)
51
- to_h[name]
80
+ data[name]
52
81
  end
53
82
 
54
- # Defined here to override the method on `Object`.
55
- def display
56
- to_h.fetch(:display)
83
+ def []=(key, value)
84
+ data[key] = value
57
85
  end
58
86
 
59
- private
87
+ def delete!(key)
88
+ data.delete(key)
89
+ end
90
+
91
+ def respond_to?(method_name, include_private = false)
92
+ self.class.instance_methods.include?(method_name) ||
93
+ respond_to_missing?(method_name, include_private)
94
+ end
95
+
96
+ def deconstruct_keys(keys)
97
+ deconstructed_keys = to_h.merge(success: success?, failure: failure?)
60
98
 
61
- def respond_to_missing?(method_name, include_private = false)
62
- method_name.to_s.end_with?("?") || super
99
+ keys ? deconstructed_keys.slice(*keys) : deconstructed_keys
63
100
  end
64
101
 
65
- def method_missing(symbol, *args)
66
- attribute = symbol.to_s.chomp("?")
102
+ private
103
+
104
+ attr_reader :data
105
+
106
+ def respond_to_missing?(method_name, _include_private = false)
107
+ return true if method_name.end_with?("=")
108
+ if method_name.end_with?("?") &&
109
+ data.key?(method_name.to_s.chomp("?").to_sym)
110
+ return true
111
+ end
67
112
 
68
- if symbol.to_s.end_with?("?") && respond_to?(attribute)
69
- define_singleton_method symbol do
70
- value = send(attribute.to_sym)
113
+ return true if data.key?(method_name)
71
114
 
72
- # Same as ActiveSupport’s #present?
73
- value.respond_to?(:empty?) ? !value.empty? : !!value
74
- end
115
+ false
116
+ end
75
117
 
76
- return send(symbol)
118
+ def method_missing(method_name, *args) # rubocop:disable Metrics/AbcSize
119
+ if method_name.end_with?("?") &&
120
+ data.key?(key = method_name.to_s.chomp("?").to_sym)
121
+ value = data[key]
122
+ value.respond_to?(:empty?) ? !value.empty? : !!value
123
+ elsif method_name.end_with?("=")
124
+ data[method_name.to_s.chomp("=").to_sym] = args.first
125
+ elsif data.key?(method_name)
126
+ data[method_name]
127
+ else
128
+ warn_on_undefined_method_invocation(method_name)
77
129
  end
130
+ end
78
131
 
79
- super symbol, *args
132
+ def warn_on_undefined_method_invocation(message)
133
+ ::Kernel.warn(
134
+ "DEPRECATED: Invoking undefined methods on `ServiceActor::Result` will " \
135
+ "lead to runtime errors in the next major release of Actor. " \
136
+ "Invoked method: `#{message}`",
137
+ )
80
138
  end
81
139
  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.1"
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.1
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-26 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: standard-rubocop-lts
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.10
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.10
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