axn 0.1.0.pre.alpha.2.7.1 → 0.1.0.pre.alpha.2.8
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/.rubocop.yml +11 -5
- data/CHANGELOG.md +10 -0
- data/Rakefile +12 -0
- data/docs/intro/about.md +2 -2
- data/docs/intro/overview.md +18 -0
- data/docs/recipes/rubocop-integration.md +352 -0
- data/docs/reference/action-result.md +1 -1
- data/docs/reference/class.md +110 -2
- data/docs/reference/configuration.md +5 -3
- data/docs/reference/instance.md +0 -52
- data/docs/usage/setup.md +4 -0
- data/docs/usage/steps.md +335 -0
- data/docs/usage/writing.md +67 -0
- data/lib/action/attachable/steps.rb +18 -17
- data/lib/action/attachable/subactions.rb +1 -1
- data/lib/action/context.rb +10 -14
- data/lib/action/core/context/facade.rb +11 -2
- data/lib/action/core/context/facade_inspector.rb +3 -2
- data/lib/action/core/context/internal.rb +3 -11
- data/lib/action/core/contract_validation.rb +1 -1
- data/lib/action/core/flow/callbacks.rb +22 -8
- data/lib/action/core/flow/exception_execution.rb +2 -5
- data/lib/action/core/flow/handlers/{base_handler.rb → base_descriptor.rb} +7 -4
- data/lib/action/core/flow/handlers/descriptors/callback_descriptor.rb +17 -0
- data/lib/action/core/flow/handlers/descriptors/message_descriptor.rb +53 -0
- data/lib/action/core/flow/handlers/matcher.rb +41 -2
- data/lib/action/core/flow/handlers/resolvers/base_resolver.rb +28 -0
- data/lib/action/core/flow/handlers/resolvers/callback_resolver.rb +29 -0
- data/lib/action/core/flow/handlers/resolvers/message_resolver.rb +59 -0
- data/lib/action/core/flow/handlers.rb +7 -4
- data/lib/action/core/flow/messages.rb +15 -41
- data/lib/action/core/nesting_tracking.rb +31 -0
- data/lib/action/core/timing.rb +1 -1
- data/lib/action/core.rb +20 -12
- data/lib/action/exceptions.rb +20 -2
- data/lib/action/result.rb +30 -32
- data/lib/axn/factory.rb +22 -23
- data/lib/axn/rubocop.rb +10 -0
- data/lib/axn/version.rb +1 -1
- data/lib/rubocop/cop/axn/README.md +237 -0
- data/lib/rubocop/cop/axn/unchecked_result.rb +327 -0
- metadata +14 -6
- data/lib/action/core/flow/handlers/callback_handler.rb +0 -21
- data/lib/action/core/flow/handlers/message_handler.rb +0 -27
- data/lib/action/core/hoist_errors.rb +0 -58
@@ -0,0 +1,327 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Axn
|
6
|
+
# This cop enforces that when calling Actions from within other Actions,
|
7
|
+
# you must either use `call!` (with the bang) or check `result.ok?`.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# class OuterAction
|
12
|
+
# include Action
|
13
|
+
# def call
|
14
|
+
# InnerAction.call(param: "value") # Missing result check
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# # good
|
19
|
+
# class OuterAction
|
20
|
+
# include Action
|
21
|
+
# def call
|
22
|
+
# result = InnerAction.call(param: "value")
|
23
|
+
# return result unless result.ok?
|
24
|
+
# # Process successful result...
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# # also good
|
29
|
+
# class OuterAction
|
30
|
+
# include Action
|
31
|
+
# def call
|
32
|
+
# InnerAction.call!(param: "value") # Using call! ensures exceptions bubble up
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# rubocop:disable Metrics/ClassLength
|
37
|
+
class UncheckedResult < RuboCop::Cop::Base
|
38
|
+
extend RuboCop::Cop::AutoCorrector
|
39
|
+
|
40
|
+
MSG = "Use `call!` or check `result.ok?` when calling Actions from within Actions"
|
41
|
+
|
42
|
+
# Configuration options
|
43
|
+
def check_nested?
|
44
|
+
cop_config["CheckNested"] != false
|
45
|
+
end
|
46
|
+
|
47
|
+
def check_non_nested?
|
48
|
+
cop_config["CheckNonNested"] != false
|
49
|
+
end
|
50
|
+
|
51
|
+
# Track whether we're inside an Action class and its call method
|
52
|
+
def_node_search :action_class?, <<~PATTERN
|
53
|
+
(class _ (const nil? :Action) ...)
|
54
|
+
PATTERN
|
55
|
+
|
56
|
+
def_node_search :includes_action?, <<~PATTERN
|
57
|
+
(send nil? :include (const nil? :Action))
|
58
|
+
PATTERN
|
59
|
+
|
60
|
+
def_node_search :call_method?, <<~PATTERN
|
61
|
+
(def :call ...)
|
62
|
+
PATTERN
|
63
|
+
|
64
|
+
def_node_search :action_call?, <<~PATTERN
|
65
|
+
(send (const _ _) :call ...)
|
66
|
+
PATTERN
|
67
|
+
|
68
|
+
def_node_search :bang_call?, <<~PATTERN
|
69
|
+
(send (const _ _) :call! ...)
|
70
|
+
PATTERN
|
71
|
+
|
72
|
+
def_node_search :result_assignment?, <<~PATTERN
|
73
|
+
(lvasgn _ (send (const _ _) :call ...))
|
74
|
+
PATTERN
|
75
|
+
|
76
|
+
def_node_search :result_ok_check?, <<~PATTERN
|
77
|
+
(send (send _ :result) :ok?)
|
78
|
+
PATTERN
|
79
|
+
|
80
|
+
def_node_search :result_failed_check?, <<~PATTERN
|
81
|
+
(send (send _ :result) :failed?)
|
82
|
+
PATTERN
|
83
|
+
|
84
|
+
def_node_search :result_error_check?, <<~PATTERN
|
85
|
+
(send (send _ :result) :error)
|
86
|
+
PATTERN
|
87
|
+
|
88
|
+
def_node_search :result_exception_check?, <<~PATTERN
|
89
|
+
(send (send _ :result) :exception)
|
90
|
+
PATTERN
|
91
|
+
|
92
|
+
def_node_search :return_with_result?, <<~PATTERN
|
93
|
+
(return (send _ :result))
|
94
|
+
PATTERN
|
95
|
+
|
96
|
+
def_node_search :expose_with_result?, <<~PATTERN
|
97
|
+
(send nil? :expose ...)
|
98
|
+
PATTERN
|
99
|
+
|
100
|
+
def_node_search :result_passed_to_method?, <<~PATTERN
|
101
|
+
(send nil? :result ...)
|
102
|
+
PATTERN
|
103
|
+
|
104
|
+
def on_send(node)
|
105
|
+
return unless action_call?(node)
|
106
|
+
return if bang_call?(node)
|
107
|
+
return unless inside_action_call_method?(node)
|
108
|
+
|
109
|
+
# Check if we should process this call based on configuration
|
110
|
+
is_inside_action = inside_action_context?(node)
|
111
|
+
return unless (is_inside_action && check_nested?) || (!is_inside_action && check_non_nested?)
|
112
|
+
|
113
|
+
return if result_properly_handled?(node)
|
114
|
+
|
115
|
+
add_offense(node, message: MSG)
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def inside_action_call_method?(node)
|
121
|
+
# Check if we're inside a call method of an Action class
|
122
|
+
current_node = node
|
123
|
+
while current_node.parent
|
124
|
+
current_node = current_node.parent
|
125
|
+
|
126
|
+
# Check if we're inside a def :call
|
127
|
+
next unless call_method?(current_node) && current_node.method_name == :call
|
128
|
+
|
129
|
+
# Now check if this class includes Action
|
130
|
+
class_node = find_enclosing_class(current_node)
|
131
|
+
return includes_action?(class_node) if class_node
|
132
|
+
end
|
133
|
+
false
|
134
|
+
rescue StandardError => _e
|
135
|
+
# If there's any error in the analysis, assume we're not in an Action call method
|
136
|
+
# This prevents the cop from crashing on complex or malformed code
|
137
|
+
false
|
138
|
+
end
|
139
|
+
|
140
|
+
def inside_action_context?(node)
|
141
|
+
# Check if this Action call is inside an Action class's call method
|
142
|
+
current_node = node
|
143
|
+
while current_node.parent
|
144
|
+
current_node = current_node.parent
|
145
|
+
|
146
|
+
# Check if we're inside a def :call
|
147
|
+
next unless call_method?(current_node) && current_node.method_name == :call
|
148
|
+
|
149
|
+
# Now check if this class includes Action
|
150
|
+
class_node = find_enclosing_class(current_node)
|
151
|
+
return true if class_node && includes_action?(class_node)
|
152
|
+
end
|
153
|
+
false
|
154
|
+
rescue StandardError => _e
|
155
|
+
false
|
156
|
+
end
|
157
|
+
|
158
|
+
def find_enclosing_class(node)
|
159
|
+
current_node = node
|
160
|
+
while current_node.parent
|
161
|
+
current_node = current_node.parent
|
162
|
+
return current_node if current_node.type == :class
|
163
|
+
end
|
164
|
+
nil
|
165
|
+
rescue StandardError => _e
|
166
|
+
# If there's any error in the analysis, return nil
|
167
|
+
nil
|
168
|
+
end
|
169
|
+
|
170
|
+
def result_properly_handled?(node)
|
171
|
+
# Check if the result is assigned to a variable
|
172
|
+
parent = node.parent
|
173
|
+
return false unless parent&.type == :lvasgn
|
174
|
+
|
175
|
+
result_var = parent.children[0]
|
176
|
+
|
177
|
+
# Look for proper result handling in the method
|
178
|
+
method_body = find_enclosing_method_body(node)
|
179
|
+
return false unless method_body
|
180
|
+
|
181
|
+
# Check if result.ok? is checked
|
182
|
+
return true if result_ok_check_in_method?(method_body, result_var)
|
183
|
+
|
184
|
+
# Check if result.failed? is checked
|
185
|
+
return true if result_failed_check_in_method?(method_body, result_var)
|
186
|
+
|
187
|
+
# Check if result.error is accessed
|
188
|
+
return true if result_error_check_in_method?(method_body, result_var)
|
189
|
+
|
190
|
+
# Check if result.exception is accessed
|
191
|
+
return true if result_exception_check_in_method?(method_body, result_var)
|
192
|
+
|
193
|
+
# Check if result is returned
|
194
|
+
return true if result_returned_in_method?(method_body, result_var)
|
195
|
+
|
196
|
+
# Check if result is used in expose
|
197
|
+
return true if result_used_in_expose?(method_body, result_var)
|
198
|
+
|
199
|
+
# Check if result is passed to another method
|
200
|
+
return true if result_passed_to_method?(method_body, result_var)
|
201
|
+
|
202
|
+
false
|
203
|
+
rescue StandardError => _e
|
204
|
+
# If there's any error in the analysis, assume the result is not properly handled
|
205
|
+
# This prevents the cop from crashing on complex or malformed code
|
206
|
+
false
|
207
|
+
end
|
208
|
+
|
209
|
+
def find_enclosing_method_body(node)
|
210
|
+
current_node = node
|
211
|
+
while current_node.parent
|
212
|
+
current_node = current_node.parent
|
213
|
+
if current_node.type == :def && current_node.method_name == :call
|
214
|
+
return current_node.children[2] # The method body
|
215
|
+
end
|
216
|
+
end
|
217
|
+
nil
|
218
|
+
end
|
219
|
+
|
220
|
+
def result_ok_check_in_method?(method_body, result_var)
|
221
|
+
method_body.each_descendant(:send) do |send_node|
|
222
|
+
next unless send_node.method_name == :ok?
|
223
|
+
|
224
|
+
# Check if this is any_variable.ok?
|
225
|
+
receiver = send_node.children[0]
|
226
|
+
return true if receiver&.type == :lvar && receiver.children[0] == result_var
|
227
|
+
end
|
228
|
+
false
|
229
|
+
end
|
230
|
+
|
231
|
+
def result_failed_check_in_method?(method_body, result_var)
|
232
|
+
method_body.each_descendant(:send) do |send_node|
|
233
|
+
next unless send_node.method_name == :failed?
|
234
|
+
|
235
|
+
receiver = send_node.children[0]
|
236
|
+
return true if receiver&.type == :lvar && receiver.children[0] == result_var
|
237
|
+
end
|
238
|
+
false
|
239
|
+
end
|
240
|
+
|
241
|
+
def result_error_check_in_method?(method_body, result_var)
|
242
|
+
method_body.each_descendant(:send) do |send_node|
|
243
|
+
next unless send_node.method_name == :error
|
244
|
+
|
245
|
+
receiver = send_node.children[0]
|
246
|
+
return true if receiver&.type == :lvar && receiver.children[0] == result_var
|
247
|
+
end
|
248
|
+
false
|
249
|
+
end
|
250
|
+
|
251
|
+
def result_exception_check_in_method?(method_body, result_var)
|
252
|
+
method_body.each_descendant(:send) do |send_node|
|
253
|
+
next unless send_node.method_name == :exception
|
254
|
+
|
255
|
+
receiver = send_node.children[0]
|
256
|
+
return true if receiver&.type == :lvar && receiver.children[0] == result_var
|
257
|
+
end
|
258
|
+
false
|
259
|
+
end
|
260
|
+
|
261
|
+
def result_returned_in_method?(method_body, result_var)
|
262
|
+
# Check for explicit return statements
|
263
|
+
method_body.each_descendant(:return) do |return_node|
|
264
|
+
return_value = return_node.children[0]
|
265
|
+
return true if return_value&.type == :lvar && return_value.children[0] == result_var
|
266
|
+
end
|
267
|
+
|
268
|
+
# Check for implicit return (last statement in method)
|
269
|
+
last_statement = if method_body.type == :begin
|
270
|
+
method_body.children.last
|
271
|
+
else
|
272
|
+
method_body
|
273
|
+
end
|
274
|
+
|
275
|
+
return true if last_statement&.type == :lvar && last_statement.children[0] == result_var
|
276
|
+
|
277
|
+
false
|
278
|
+
end
|
279
|
+
|
280
|
+
def result_used_in_expose?(method_body, result_var)
|
281
|
+
method_body.each_descendant(:send) do |send_node|
|
282
|
+
next unless send_node.method_name == :expose
|
283
|
+
|
284
|
+
# Check if any argument references the result variable
|
285
|
+
# send_node.children[0] is the receiver (nil for self)
|
286
|
+
# send_node.children[1] is the method name (:expose)
|
287
|
+
# send_node.children[2..] are the actual arguments
|
288
|
+
send_node.children[2..].each do |arg|
|
289
|
+
# Handle hash arguments in expose calls
|
290
|
+
if arg.type == :hash
|
291
|
+
arg.children.each do |pair|
|
292
|
+
next unless pair.type == :pair
|
293
|
+
|
294
|
+
value = pair.children[1]
|
295
|
+
return true if value&.type == :lvar && value.children[0] == result_var
|
296
|
+
end
|
297
|
+
elsif arg&.type == :lvar && arg.children[0] == result_var
|
298
|
+
return true
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
false
|
303
|
+
end
|
304
|
+
|
305
|
+
def result_passed_to_method?(method_body, result_var)
|
306
|
+
method_body.each_descendant(:send) do |send_node|
|
307
|
+
next if send_node.method_name == :result # Skip result method calls
|
308
|
+
next if send_node.method_name == :ok? # Skip result.ok? calls
|
309
|
+
next if send_node.method_name == :failed? # Skip result.failed? calls
|
310
|
+
next if send_node.method_name == :error # Skip result.error calls
|
311
|
+
next if send_node.method_name == :exception # Skip result.exception calls
|
312
|
+
|
313
|
+
# Check if result is passed as an argument
|
314
|
+
# send_node.children[0] is the receiver
|
315
|
+
# send_node.children[1] is the method name
|
316
|
+
# send_node.children[2..] are the actual arguments
|
317
|
+
send_node.children[2..].each do |arg|
|
318
|
+
return true if arg&.type == :lvar && arg.children[0] == result_var
|
319
|
+
end
|
320
|
+
end
|
321
|
+
false
|
322
|
+
end
|
323
|
+
# rubocop:enable Metrics/ClassLength
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: axn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.0.pre.alpha.2.
|
4
|
+
version: 0.1.0.pre.alpha.2.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kali Donovan
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-08-
|
11
|
+
date: 2025-08-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -61,6 +61,7 @@ files:
|
|
61
61
|
- docs/intro/about.md
|
62
62
|
- docs/intro/overview.md
|
63
63
|
- docs/recipes/memoization.md
|
64
|
+
- docs/recipes/rubocop-integration.md
|
64
65
|
- docs/recipes/testing.md
|
65
66
|
- docs/recipes/validating-user-input.md
|
66
67
|
- docs/reference/action-result.md
|
@@ -70,6 +71,7 @@ files:
|
|
70
71
|
- docs/strategies/index.md
|
71
72
|
- docs/strategies/transaction.md
|
72
73
|
- docs/usage/setup.md
|
74
|
+
- docs/usage/steps.md
|
73
75
|
- docs/usage/using.md
|
74
76
|
- docs/usage/writing.md
|
75
77
|
- lib/action/attachable.rb
|
@@ -90,16 +92,19 @@ files:
|
|
90
92
|
- lib/action/core/flow/callbacks.rb
|
91
93
|
- lib/action/core/flow/exception_execution.rb
|
92
94
|
- lib/action/core/flow/handlers.rb
|
93
|
-
- lib/action/core/flow/handlers/
|
94
|
-
- lib/action/core/flow/handlers/
|
95
|
+
- lib/action/core/flow/handlers/base_descriptor.rb
|
96
|
+
- lib/action/core/flow/handlers/descriptors/callback_descriptor.rb
|
97
|
+
- lib/action/core/flow/handlers/descriptors/message_descriptor.rb
|
95
98
|
- lib/action/core/flow/handlers/invoker.rb
|
96
99
|
- lib/action/core/flow/handlers/matcher.rb
|
97
|
-
- lib/action/core/flow/handlers/message_handler.rb
|
98
100
|
- lib/action/core/flow/handlers/registry.rb
|
101
|
+
- lib/action/core/flow/handlers/resolvers/base_resolver.rb
|
102
|
+
- lib/action/core/flow/handlers/resolvers/callback_resolver.rb
|
103
|
+
- lib/action/core/flow/handlers/resolvers/message_resolver.rb
|
99
104
|
- lib/action/core/flow/messages.rb
|
100
|
-
- lib/action/core/hoist_errors.rb
|
101
105
|
- lib/action/core/hooks.rb
|
102
106
|
- lib/action/core/logging.rb
|
107
|
+
- lib/action/core/nesting_tracking.rb
|
103
108
|
- lib/action/core/timing.rb
|
104
109
|
- lib/action/core/tracing.rb
|
105
110
|
- lib/action/core/use_strategy.rb
|
@@ -116,9 +121,12 @@ files:
|
|
116
121
|
- lib/action/strategies/transaction.rb
|
117
122
|
- lib/axn.rb
|
118
123
|
- lib/axn/factory.rb
|
124
|
+
- lib/axn/rubocop.rb
|
119
125
|
- lib/axn/testing/spec_helpers.rb
|
120
126
|
- lib/axn/util.rb
|
121
127
|
- lib/axn/version.rb
|
128
|
+
- lib/rubocop/cop/axn/README.md
|
129
|
+
- lib/rubocop/cop/axn/unchecked_result.rb
|
122
130
|
- package.json
|
123
131
|
- yarn.lock
|
124
132
|
homepage: https://github.com/teamshares/axn
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "action/core/flow/handlers/base_handler"
|
4
|
-
require "action/core/flow/handlers/invoker"
|
5
|
-
|
6
|
-
module Action
|
7
|
-
module Core
|
8
|
-
module Flow
|
9
|
-
module Handlers
|
10
|
-
class CallbackHandler < BaseHandler
|
11
|
-
def apply(action:, exception:)
|
12
|
-
return false unless matches?(action:, exception:)
|
13
|
-
|
14
|
-
Invoker.call(action:, handler:, exception:, operation: "executing handler")
|
15
|
-
true
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "action/core/flow/handlers/base_handler"
|
4
|
-
require "action/core/flow/handlers/invoker"
|
5
|
-
|
6
|
-
module Action
|
7
|
-
module Core
|
8
|
-
module Flow
|
9
|
-
module Handlers
|
10
|
-
class MessageHandler < BaseHandler
|
11
|
-
# Returns a string (truthy) when it applies and yields a non-blank message; otherwise nil
|
12
|
-
def apply(action:, exception:)
|
13
|
-
return nil unless matches?(action:, exception:)
|
14
|
-
|
15
|
-
value =
|
16
|
-
if handler.is_a?(Symbol) || handler.respond_to?(:call)
|
17
|
-
Invoker.call(action:, handler:, exception:, operation: "determining message callable")
|
18
|
-
else
|
19
|
-
handler
|
20
|
-
end
|
21
|
-
value.respond_to?(:presence) ? value.presence : value
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
@@ -1,58 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Action
|
4
|
-
module Core
|
5
|
-
module HoistErrors
|
6
|
-
def self.included(base)
|
7
|
-
base.class_eval do
|
8
|
-
include InstanceMethods
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
module InstanceMethods
|
13
|
-
private
|
14
|
-
|
15
|
-
MinimalFailedResult = Data.define(:error, :exception) do
|
16
|
-
def ok? = false
|
17
|
-
end
|
18
|
-
|
19
|
-
# This method is used to ensure that the result of a block is successful before proceeding.
|
20
|
-
#
|
21
|
-
# Assumes success unless the block raises an exception or returns a failed result.
|
22
|
-
# (i.e. if you wrap logic that is NOT an action call, it'll be successful unless it raises an exception)
|
23
|
-
def hoist_errors(prefix: nil)
|
24
|
-
raise ArgumentError, "#hoist_errors must be given a block to execute" unless block_given?
|
25
|
-
|
26
|
-
result = begin
|
27
|
-
yield
|
28
|
-
rescue StandardError => e
|
29
|
-
log "hoist_errors block transforming a #{e.class.name} exception: #{e.message}"
|
30
|
-
MinimalFailedResult.new(error: nil, exception: e)
|
31
|
-
end
|
32
|
-
|
33
|
-
# This ensures the last line of hoist_errors was an Action call
|
34
|
-
#
|
35
|
-
# CAUTION: if there are multiple calls per block, only the last one will be checked!
|
36
|
-
#
|
37
|
-
unless result.respond_to?(:ok?)
|
38
|
-
raise ArgumentError,
|
39
|
-
"#hoist_errors is expected to wrap an Action call, but it returned a #{result.class.name} instead"
|
40
|
-
end
|
41
|
-
|
42
|
-
return result if result.ok?
|
43
|
-
|
44
|
-
_handle_hoisted_errors(result, prefix:)
|
45
|
-
end
|
46
|
-
|
47
|
-
# Separate method to allow overriding in subclasses
|
48
|
-
def _handle_hoisted_errors(result, prefix: nil)
|
49
|
-
@__context.exception = result.exception if result.exception.present?
|
50
|
-
@__context.error_prefix = prefix if prefix.present?
|
51
|
-
|
52
|
-
error = result.exception.is_a?(Action::Failure) ? result.exception.message : result.error
|
53
|
-
fail! error
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|