service_actor 3.9.3 → 4.0.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 +4 -4
- data/README.md +31 -18
- data/lib/service_actor/arguments_validator.rb +9 -0
- data/lib/service_actor/attributable.rb +8 -0
- data/lib/service_actor/checkable.rb +1 -2
- data/lib/service_actor/checks/base.rb +0 -6
- data/lib/service_actor/checks/type_check.rb +2 -2
- data/lib/service_actor/defaultable.rb +9 -44
- data/lib/service_actor/failure.rb +1 -1
- data/lib/service_actor/playable.rb +9 -1
- data/lib/service_actor/result.rb +19 -7
- data/lib/service_actor/version.rb +1 -1
- metadata +32 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 86aa1f5411838fa24961e50eff4138d414c86aa99c7bdc5907953f7643406431
|
4
|
+
data.tar.gz: 1e0c82c0ba0e3b3c8278fa7d286454f891266972d34144b2ee3aa18a2bddd20d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ed8bfce6061647dee123bfdecc2a89f92cb7b4fc09664bef3671d722e59605787acfe3ceb9822b785a4a0ed651b315a9c27a38366516cbeadef9403d92c49709
|
7
|
+
data.tar.gz: 9dec2bb8b9d5815c1c8d39108432003575f2f5a9a9431c93c69228c2c13f776eea60794696328e2d9ebc6e13849e2fa3e6db3e6e3ac337c03a39d29b24c578ea
|
data/README.md
CHANGED
@@ -314,20 +314,20 @@ end
|
|
314
314
|
|
315
315
|
### Defaults
|
316
316
|
|
317
|
-
Inputs can be optional by providing a `default` value
|
317
|
+
Inputs can be optional by providing a `default` value in a lambda.
|
318
318
|
|
319
319
|
```rb
|
320
320
|
class BuildGreeting < Actor
|
321
321
|
input :name
|
322
|
-
input :adjective, default: "wonderful"
|
322
|
+
input :adjective, default: -> { "wonderful" }
|
323
323
|
input :length_of_time, default: -> { ["day", "week", "month"].sample }
|
324
324
|
input :article,
|
325
|
-
default: ->
|
325
|
+
default: -> actor { actor.adjective.match?(/^[aeiou]/) ? "an" : "a" }
|
326
326
|
|
327
327
|
output :greeting
|
328
328
|
|
329
329
|
def call
|
330
|
-
self.greeting = "Have #{article} #{length_of_time}, #{name}!"
|
330
|
+
self.greeting = "Have #{article} #{adjective} #{length_of_time}, #{name}!"
|
331
331
|
end
|
332
332
|
end
|
333
333
|
|
@@ -338,6 +338,33 @@ actor = BuildGreeting.call(name: "Siobhan", adjective: "elegant")
|
|
338
338
|
actor.greeting # => "Have an elegant week, Siobhan!"
|
339
339
|
```
|
340
340
|
|
341
|
+
While lambdas are the preferred way to specify defaults, you can also provide
|
342
|
+
a default value without using lambdas by using an immutable object.
|
343
|
+
|
344
|
+
```rb
|
345
|
+
# frozen_string_literal: true
|
346
|
+
|
347
|
+
class ExampleActor < Actor
|
348
|
+
input :options, default: {
|
349
|
+
names: {male: "Iaroslav", female: "Anna"}.freeze,
|
350
|
+
country_codes: %w[gb ru].freeze
|
351
|
+
}.freeze
|
352
|
+
end
|
353
|
+
```
|
354
|
+
|
355
|
+
Note that default values might be mutated if the values returned by the lambda
|
356
|
+
are references to mutable objects, e.g.
|
357
|
+
|
358
|
+
```rb
|
359
|
+
class ExampleActor < Actor
|
360
|
+
input :options, default: -> { Registry::DEFAULT_OPTIONS } # `Registry::DEFAULT_OPTIONS` is not frozen
|
361
|
+
|
362
|
+
def call
|
363
|
+
options[:names] = nil
|
364
|
+
end
|
365
|
+
end
|
366
|
+
```
|
367
|
+
|
341
368
|
### Allow nil
|
342
369
|
|
343
370
|
By default inputs accept `nil` values. To raise an error instead:
|
@@ -471,20 +498,6 @@ end
|
|
471
498
|
end
|
472
499
|
```
|
473
500
|
|
474
|
-
#### Default
|
475
|
-
|
476
|
-
```ruby
|
477
|
-
class MultiplyThing < Actor
|
478
|
-
input :multiplier,
|
479
|
-
default: {
|
480
|
-
is: -> { rand(1..10) },
|
481
|
-
message: (lambda do |input_key:, **|
|
482
|
-
"Input \"#{input_key}\" is required"
|
483
|
-
end)
|
484
|
-
}
|
485
|
-
end
|
486
|
-
```
|
487
|
-
|
488
501
|
#### Type
|
489
502
|
|
490
503
|
```ruby
|
@@ -16,4 +16,13 @@ module ServiceActor::ArgumentsValidator
|
|
16
16
|
|
17
17
|
raise ArgumentError, "Expected #{value} to be a subclass of Exception"
|
18
18
|
end
|
19
|
+
|
20
|
+
def validate_default_value(value, origin_type:, origin_name:, actor:)
|
21
|
+
return if value.is_a?(Proc) || !defined?(Ractor.shareable?) || Ractor.shareable?(value)
|
22
|
+
|
23
|
+
::Kernel.warn(
|
24
|
+
"DEPRECATED: Actor `#{actor}` has #{origin_type} `#{origin_name}` with default " \
|
25
|
+
"which is not a Proc or an immutable object.",
|
26
|
+
)
|
27
|
+
end
|
19
28
|
end
|
@@ -26,6 +26,10 @@ module ServiceActor::Attributable
|
|
26
26
|
name, origin: :input
|
27
27
|
)
|
28
28
|
|
29
|
+
ServiceActor::ArgumentsValidator.validate_default_value(
|
30
|
+
arguments[:default], actor: self, origin_type: :input, origin_name: name
|
31
|
+
)
|
32
|
+
|
29
33
|
inputs[name] = arguments
|
30
34
|
|
31
35
|
define_method(name) do
|
@@ -47,6 +51,10 @@ module ServiceActor::Attributable
|
|
47
51
|
name, origin: :output
|
48
52
|
)
|
49
53
|
|
54
|
+
ServiceActor::ArgumentsValidator.validate_default_value(
|
55
|
+
arguments[:default], actor: self, origin_type: :output, origin_name: name
|
56
|
+
)
|
57
|
+
|
50
58
|
outputs[name] = arguments
|
51
59
|
|
52
60
|
define_method(name) do
|
@@ -31,10 +31,9 @@ module ServiceActor::Checkable
|
|
31
31
|
|
32
32
|
# rubocop:disable Metrics/MethodLength
|
33
33
|
def service_actor_checks_for(origin)
|
34
|
-
check_classes = CHECK_CLASSES.select { _1.applicable_to_origin?(origin) }
|
35
34
|
self.class.public_send(:"#{origin}s").each do |input_key, input_options|
|
36
35
|
input_options.each do |check_name, check_conditions|
|
37
|
-
|
36
|
+
CHECK_CLASSES.each do |check_class|
|
38
37
|
argument_errors = check_class.check(
|
39
38
|
check_name: check_name,
|
40
39
|
origin: origin,
|
@@ -72,14 +72,14 @@ class ServiceActor::Checks::TypeCheck < ServiceActor::Checks::Base
|
|
72
72
|
|
73
73
|
types, message = define_types_and_message
|
74
74
|
|
75
|
-
return if types.any? { |type| @given_type
|
75
|
+
return if types.any? { |type| type === @given_type }
|
76
76
|
|
77
77
|
add_argument_error(
|
78
78
|
message,
|
79
79
|
origin: @origin,
|
80
80
|
input_key: @input_key,
|
81
81
|
actor: @actor,
|
82
|
-
expected_type: types.join(", "),
|
82
|
+
expected_type: types.map(&:name).join(", "),
|
83
83
|
given_type: @given_type.class,
|
84
84
|
)
|
85
85
|
end
|
@@ -9,22 +9,6 @@
|
|
9
9
|
# input :counter, default: 1
|
10
10
|
# input :multiplier, default: -> { rand(1..10) }
|
11
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
12
|
module ServiceActor::Defaultable
|
29
13
|
class << self
|
30
14
|
def included(base)
|
@@ -43,19 +27,13 @@ module ServiceActor::Defaultable
|
|
43
27
|
)
|
44
28
|
end
|
45
29
|
|
46
|
-
|
47
|
-
|
48
|
-
if default.is_a?(Hash) && default[:is]
|
49
|
-
default_for_advanced_mode_with(result, key, default)
|
50
|
-
else
|
51
|
-
default_for_normal_mode_with(result, key, default)
|
52
|
-
end
|
30
|
+
apply_default_for_origin(key, input)
|
53
31
|
end
|
54
32
|
|
55
|
-
self.class.outputs.each do |key,
|
56
|
-
|
57
|
-
|
58
|
-
|
33
|
+
self.class.outputs.each do |key, output|
|
34
|
+
next if result.key?(key)
|
35
|
+
|
36
|
+
apply_default_for_origin(key, output)
|
59
37
|
end
|
60
38
|
|
61
39
|
super
|
@@ -63,26 +41,13 @@ module ServiceActor::Defaultable
|
|
63
41
|
|
64
42
|
private
|
65
43
|
|
66
|
-
def
|
67
|
-
|
68
|
-
end
|
69
|
-
|
70
|
-
def default_for_advanced_mode_with(result, key, content)
|
71
|
-
default, message = content.values_at(:is, :message)
|
44
|
+
def apply_default_for_origin(origin_name, origin_options)
|
45
|
+
default = origin_options[:default]
|
72
46
|
|
73
|
-
|
74
|
-
raise_error_with(message, input_key: key, actor: self.class)
|
75
|
-
end
|
76
|
-
|
77
|
-
result[key] = reify_default(result, default)
|
78
|
-
|
79
|
-
message.call(key, self.class)
|
47
|
+
result[origin_name] = reify_default(result, default)
|
80
48
|
end
|
81
49
|
|
82
|
-
|
83
|
-
def raise_error_with(message, **arguments)
|
84
|
-
message = message.call(**arguments) if message.is_a?(Proc)
|
85
|
-
|
50
|
+
def raise_error_with(message)
|
86
51
|
raise self.class.argument_error_class, message
|
87
52
|
end
|
88
53
|
|
@@ -90,7 +90,15 @@ module ServiceActor::Playable
|
|
90
90
|
end
|
91
91
|
|
92
92
|
def play_actor(actor)
|
93
|
-
|
93
|
+
actor_includes_kernel = begin
|
94
|
+
actor.respond_to?(:is_a?)
|
95
|
+
rescue NoMethodError
|
96
|
+
false
|
97
|
+
end
|
98
|
+
|
99
|
+
if !actor_includes_kernel
|
100
|
+
actor.call(result)
|
101
|
+
elsif actor.is_a?(Symbol)
|
94
102
|
send(actor)
|
95
103
|
elsif actor.is_a?(Class) && actor.ancestors.include?(ServiceActor::Core)
|
96
104
|
play_service_actor(actor)
|
data/lib/service_actor/result.rb
CHANGED
@@ -14,9 +14,12 @@ class ServiceActor::Result < BasicObject
|
|
14
14
|
%i[
|
15
15
|
block_given?
|
16
16
|
class
|
17
|
+
hash
|
18
|
+
instance_of?
|
17
19
|
instance_variables
|
18
20
|
is_a?
|
19
21
|
kind_of?
|
22
|
+
methods
|
20
23
|
nil?
|
21
24
|
object_id
|
22
25
|
private_methods
|
@@ -24,7 +27,6 @@ class ServiceActor::Result < BasicObject
|
|
24
27
|
send
|
25
28
|
tap
|
26
29
|
then
|
27
|
-
methods
|
28
30
|
].each do |method_name|
|
29
31
|
define_method(method_name, ::Kernel.instance_method(method_name))
|
30
32
|
end
|
@@ -33,7 +35,7 @@ class ServiceActor::Result < BasicObject
|
|
33
35
|
alias_method :blank?, :nil?
|
34
36
|
|
35
37
|
def initialize(data = {})
|
36
|
-
@data = data.to_h
|
38
|
+
@data = data.to_h.transform_keys(&:to_sym)
|
37
39
|
end
|
38
40
|
|
39
41
|
def to_h
|
@@ -44,7 +46,11 @@ class ServiceActor::Result < BasicObject
|
|
44
46
|
"<#{self.class.name} #{to_h}>"
|
45
47
|
end
|
46
48
|
|
47
|
-
|
49
|
+
def pretty_print(pp)
|
50
|
+
pp.text "#<#{self.class.name} "
|
51
|
+
pp.pp to_h
|
52
|
+
pp.text ">"
|
53
|
+
end
|
48
54
|
|
49
55
|
def fail!(failure_class = nil, result = {})
|
50
56
|
if failure_class.nil? || failure_class.is_a?(::Hash)
|
@@ -71,24 +77,32 @@ class ServiceActor::Result < BasicObject
|
|
71
77
|
end
|
72
78
|
|
73
79
|
def merge!(result)
|
74
|
-
data.merge!(result)
|
80
|
+
data.merge!(result.transform_keys(&:to_sym))
|
75
81
|
|
76
82
|
self
|
77
83
|
end
|
78
84
|
|
79
85
|
def key?(name)
|
86
|
+
name = name.to_sym
|
87
|
+
|
80
88
|
to_h.key?(name)
|
81
89
|
end
|
82
90
|
|
83
91
|
def [](name)
|
92
|
+
name = name.to_sym
|
93
|
+
|
84
94
|
data[name]
|
85
95
|
end
|
86
96
|
|
87
97
|
def []=(key, value)
|
98
|
+
key = key.to_sym
|
99
|
+
|
88
100
|
data[key] = value
|
89
101
|
end
|
90
102
|
|
91
103
|
def delete!(key)
|
104
|
+
key = key.to_sym
|
105
|
+
|
92
106
|
data.delete(key)
|
93
107
|
end
|
94
108
|
|
@@ -110,9 +124,7 @@ class ServiceActor::Result < BasicObject
|
|
110
124
|
# Key `_default_output` is an internal datum used by actor class
|
111
125
|
# method `.valuable`. Don't expose it with the rest of the result.
|
112
126
|
def filter_default_output(h)
|
113
|
-
|
114
|
-
# update once support for 2.7 is dropped
|
115
|
-
h.filter { |k| k != :_default_output }
|
127
|
+
h.except(:_default_output)
|
116
128
|
end
|
117
129
|
|
118
130
|
def respond_to_missing?(method_name, _include_private = false)
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: service_actor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sunny Ripert
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-02-24 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: zeitwerk
|
@@ -150,6 +149,20 @@ dependencies:
|
|
150
149
|
- - ">="
|
151
150
|
- !ruby/object:Gem::Version
|
152
151
|
version: '0.1'
|
152
|
+
- !ruby/object:Gem::Dependency
|
153
|
+
name: rubocop-thread_safety
|
154
|
+
requirement: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - ">="
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '0.1'
|
159
|
+
type: :development
|
160
|
+
prerelease: false
|
161
|
+
version_requirements: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - ">="
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0.1'
|
153
166
|
- !ruby/object:Gem::Dependency
|
154
167
|
name: code-scanning-rubocop
|
155
168
|
requirement: !ruby/object:Gem::Requirement
|
@@ -192,6 +205,20 @@ dependencies:
|
|
192
205
|
- - ">="
|
193
206
|
- !ruby/object:Gem::Version
|
194
207
|
version: '0.0'
|
208
|
+
- !ruby/object:Gem::Dependency
|
209
|
+
name: ostruct
|
210
|
+
requirement: !ruby/object:Gem::Requirement
|
211
|
+
requirements:
|
212
|
+
- - ">="
|
213
|
+
- !ruby/object:Gem::Version
|
214
|
+
version: '0.0'
|
215
|
+
type: :development
|
216
|
+
prerelease: false
|
217
|
+
version_requirements: !ruby/object:Gem::Requirement
|
218
|
+
requirements:
|
219
|
+
- - ">="
|
220
|
+
- !ruby/object:Gem::Version
|
221
|
+
version: '0.0'
|
195
222
|
description: Service objects for your application logic
|
196
223
|
email:
|
197
224
|
- sunny@sunfox.org
|
@@ -233,7 +260,6 @@ metadata:
|
|
233
260
|
source_code_uri: https://github.com/sunny/actor
|
234
261
|
changelog_uri: https://github.com/sunny/actor/blob/main/CHANGELOG.md
|
235
262
|
rubygems_mfa_required: 'true'
|
236
|
-
post_install_message:
|
237
263
|
rdoc_options: []
|
238
264
|
require_paths:
|
239
265
|
- lib
|
@@ -241,15 +267,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
241
267
|
requirements:
|
242
268
|
- - ">="
|
243
269
|
- !ruby/object:Gem::Version
|
244
|
-
version: '
|
270
|
+
version: '3.0'
|
245
271
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
246
272
|
requirements:
|
247
273
|
- - ">="
|
248
274
|
- !ruby/object:Gem::Version
|
249
275
|
version: '0'
|
250
276
|
requirements: []
|
251
|
-
rubygems_version: 3.
|
252
|
-
signing_key:
|
277
|
+
rubygems_version: 3.6.5
|
253
278
|
specification_version: 4
|
254
279
|
summary: Service objects for your application logic
|
255
280
|
test_files: []
|