service_actor 3.6.1 → 3.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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