service_actor 3.6.1 → 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.
@@ -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,21 +45,26 @@ 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
 
56
64
  private
57
65
 
58
66
  def default_for_normal_mode_with(result, key, default)
59
- default = default.call if default.is_a?(Proc)
60
- result[key] = default
67
+ result[key] = reify_default(result, default)
61
68
  end
62
69
 
63
70
  def default_for_advanced_mode_with(result, key, content)
@@ -67,8 +74,7 @@ module ServiceActor::Defaultable
67
74
  raise_error_with(message, input_key: key, actor: self.class)
68
75
  end
69
76
 
70
- default = default.call if default.is_a?(Proc)
71
- result[key] = default
77
+ result[key] = reify_default(result, default)
72
78
 
73
79
  message.call(key, self.class)
74
80
  end
@@ -79,5 +85,11 @@ module ServiceActor::Defaultable
79
85
 
80
86
  raise self.class.argument_error_class, message
81
87
  end
88
+
89
+ def reify_default(result, default)
90
+ return default unless default.is_a?(Proc)
91
+
92
+ default.arity.zero? ? default.call : default.call(result)
93
+ end
82
94
  end
83
95
  end
@@ -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,24 +47,15 @@ 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
 
56
55
  module PrependedMethods
57
56
  def call
58
57
  self.class.play_actors.each do |options|
59
- next if options[:if] && !options[:if].call(result)
58
+ next unless callable_actor?(options)
60
59
 
61
60
  options[:actors].each { |actor| play_actor(actor) }
62
61
  end
@@ -77,6 +76,13 @@ module ServiceActor::Playable
77
76
 
78
77
  private
79
78
 
79
+ def callable_actor?(options)
80
+ return false if options[:if] && !options[:if].call(result)
81
+ return false if options[:unless]&.call(result)
82
+
83
+ true
84
+ end
85
+
80
86
  def play_actor(actor)
81
87
  play_service_actor(actor) ||
82
88
  play_method(actor) ||
@@ -106,7 +112,7 @@ module ServiceActor::Playable
106
112
  return unless actor.is_a?(Class)
107
113
  return unless actor.ancestors.map(&:name).include?("Interactor")
108
114
 
109
- result.merge!(actor.call(result).to_h)
115
+ result.merge!(actor.call(result.to_h).to_h)
110
116
  end
111
117
  end
112
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.6.1"
4
+ VERSION = "3.8.0"
5
5
  end