cmdx 1.21.0 → 2.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/CHANGELOG.md +118 -1
- data/README.md +37 -24
- data/lib/cmdx/.DS_Store +0 -0
- data/lib/cmdx/callbacks.rb +179 -0
- data/lib/cmdx/chain.rb +78 -175
- data/lib/cmdx/coercions/array.rb +19 -33
- data/lib/cmdx/coercions/big_decimal.rb +12 -29
- data/lib/cmdx/coercions/boolean.rb +25 -45
- data/lib/cmdx/coercions/coerce.rb +32 -0
- data/lib/cmdx/coercions/complex.rb +12 -27
- data/lib/cmdx/coercions/date.rb +29 -33
- data/lib/cmdx/coercions/date_time.rb +29 -33
- data/lib/cmdx/coercions/float.rb +8 -29
- data/lib/cmdx/coercions/hash.rb +17 -43
- data/lib/cmdx/coercions/integer.rb +8 -32
- data/lib/cmdx/coercions/rational.rb +12 -33
- data/lib/cmdx/coercions/string.rb +6 -24
- data/lib/cmdx/coercions/symbol.rb +12 -26
- data/lib/cmdx/coercions/time.rb +31 -35
- data/lib/cmdx/coercions.rb +174 -0
- data/lib/cmdx/configuration.rb +45 -237
- data/lib/cmdx/context.rb +264 -243
- data/lib/cmdx/deprecation.rb +67 -0
- data/lib/cmdx/deprecators/error.rb +22 -0
- data/lib/cmdx/deprecators/log.rb +22 -0
- data/lib/cmdx/deprecators/warn.rb +21 -0
- data/lib/cmdx/deprecators.rb +101 -0
- data/lib/cmdx/errors.rb +145 -79
- data/lib/cmdx/executors/fiber.rb +42 -0
- data/lib/cmdx/executors/thread.rb +36 -0
- data/lib/cmdx/executors.rb +95 -0
- data/lib/cmdx/fault.rb +85 -78
- data/lib/cmdx/i18n_proxy.rb +104 -0
- data/lib/cmdx/input.rb +294 -0
- data/lib/cmdx/inputs.rb +218 -0
- data/lib/cmdx/log_formatters/json.rb +9 -20
- data/lib/cmdx/log_formatters/key_value.rb +10 -21
- data/lib/cmdx/log_formatters/line.rb +7 -19
- data/lib/cmdx/log_formatters/logstash.rb +8 -21
- data/lib/cmdx/log_formatters/raw.rb +8 -20
- data/lib/cmdx/logger_proxy.rb +30 -0
- data/lib/cmdx/mergers/deep_merge.rb +23 -0
- data/lib/cmdx/mergers/last_write_wins.rb +23 -0
- data/lib/cmdx/mergers/no_merge.rb +20 -0
- data/lib/cmdx/mergers.rb +95 -0
- data/lib/cmdx/middlewares.rb +128 -0
- data/lib/cmdx/output.rb +115 -0
- data/lib/cmdx/outputs.rb +66 -0
- data/lib/cmdx/pipeline.rb +144 -131
- data/lib/cmdx/railtie.rb +10 -36
- data/lib/cmdx/result.rb +247 -524
- data/lib/cmdx/retriers/bounded_random.rb +24 -0
- data/lib/cmdx/retriers/decorrelated_jitter.rb +28 -0
- data/lib/cmdx/retriers/exponential.rb +23 -0
- data/lib/cmdx/retriers/fibonacci.rb +39 -0
- data/lib/cmdx/retriers/full_random.rb +23 -0
- data/lib/cmdx/retriers/half_random.rb +24 -0
- data/lib/cmdx/retriers/linear.rb +23 -0
- data/lib/cmdx/retriers.rb +106 -0
- data/lib/cmdx/retry.rb +117 -138
- data/lib/cmdx/runtime.rb +251 -0
- data/lib/cmdx/settings.rb +68 -200
- data/lib/cmdx/signal.rb +165 -0
- data/lib/cmdx/task.rb +443 -343
- data/lib/cmdx/telemetry.rb +108 -0
- data/lib/cmdx/util.rb +73 -0
- data/lib/cmdx/validators/absence.rb +10 -39
- data/lib/cmdx/validators/exclusion.rb +33 -52
- data/lib/cmdx/validators/format.rb +19 -49
- data/lib/cmdx/validators/inclusion.rb +33 -54
- data/lib/cmdx/validators/length.rb +125 -127
- data/lib/cmdx/validators/numeric.rb +123 -123
- data/lib/cmdx/validators/presence.rb +10 -39
- data/lib/cmdx/validators/validate.rb +31 -0
- data/lib/cmdx/validators.rb +161 -0
- data/lib/cmdx/version.rb +2 -4
- data/lib/cmdx/workflow.rb +71 -96
- data/lib/cmdx.rb +111 -42
- data/lib/generators/cmdx/install_generator.rb +7 -17
- data/lib/generators/cmdx/task_generator.rb +12 -29
- data/lib/generators/cmdx/templates/install.rb +120 -48
- data/lib/generators/cmdx/templates/task.rb.tt +1 -1
- data/lib/generators/cmdx/templates/workflow.rb.tt +1 -2
- data/lib/generators/cmdx/workflow_generator.rb +12 -29
- data/lib/locales/en.yml +8 -7
- data/mkdocs.yml +25 -23
- metadata +39 -138
- data/lib/cmdx/attribute.rb +0 -440
- data/lib/cmdx/attribute_registry.rb +0 -185
- data/lib/cmdx/attribute_value.rb +0 -252
- data/lib/cmdx/callback_registry.rb +0 -169
- data/lib/cmdx/coercion_registry.rb +0 -138
- data/lib/cmdx/deprecator.rb +0 -77
- data/lib/cmdx/exception.rb +0 -46
- data/lib/cmdx/executor.rb +0 -378
- data/lib/cmdx/identifier.rb +0 -30
- data/lib/cmdx/locale.rb +0 -78
- data/lib/cmdx/middleware_registry.rb +0 -148
- data/lib/cmdx/middlewares/correlate.rb +0 -140
- data/lib/cmdx/middlewares/runtime.rb +0 -77
- data/lib/cmdx/middlewares/timeout.rb +0 -78
- data/lib/cmdx/parallelizer.rb +0 -100
- data/lib/cmdx/utils/call.rb +0 -53
- data/lib/cmdx/utils/condition.rb +0 -71
- data/lib/cmdx/utils/format.rb +0 -82
- data/lib/cmdx/utils/normalize.rb +0 -52
- data/lib/cmdx/utils/wrap.rb +0 -38
- data/lib/cmdx/validator_registry.rb +0 -143
- data/lib/generators/cmdx/locale_generator.rb +0 -39
- data/lib/locales/af.yml +0 -55
- data/lib/locales/ar.yml +0 -55
- data/lib/locales/az.yml +0 -55
- data/lib/locales/be.yml +0 -55
- data/lib/locales/bg.yml +0 -55
- data/lib/locales/bn.yml +0 -55
- data/lib/locales/bs.yml +0 -55
- data/lib/locales/ca.yml +0 -55
- data/lib/locales/cnr.yml +0 -55
- data/lib/locales/cs.yml +0 -55
- data/lib/locales/cy.yml +0 -55
- data/lib/locales/da.yml +0 -55
- data/lib/locales/de.yml +0 -55
- data/lib/locales/dz.yml +0 -55
- data/lib/locales/el.yml +0 -55
- data/lib/locales/eo.yml +0 -55
- data/lib/locales/es.yml +0 -55
- data/lib/locales/et.yml +0 -55
- data/lib/locales/eu.yml +0 -55
- data/lib/locales/fa.yml +0 -55
- data/lib/locales/fi.yml +0 -55
- data/lib/locales/fr.yml +0 -55
- data/lib/locales/fy.yml +0 -55
- data/lib/locales/gd.yml +0 -55
- data/lib/locales/gl.yml +0 -55
- data/lib/locales/he.yml +0 -55
- data/lib/locales/hi.yml +0 -55
- data/lib/locales/hr.yml +0 -55
- data/lib/locales/hu.yml +0 -55
- data/lib/locales/hy.yml +0 -55
- data/lib/locales/id.yml +0 -55
- data/lib/locales/is.yml +0 -55
- data/lib/locales/it.yml +0 -55
- data/lib/locales/ja.yml +0 -55
- data/lib/locales/ka.yml +0 -55
- data/lib/locales/kk.yml +0 -55
- data/lib/locales/km.yml +0 -55
- data/lib/locales/kn.yml +0 -55
- data/lib/locales/ko.yml +0 -55
- data/lib/locales/lb.yml +0 -55
- data/lib/locales/lo.yml +0 -55
- data/lib/locales/lt.yml +0 -55
- data/lib/locales/lv.yml +0 -55
- data/lib/locales/mg.yml +0 -55
- data/lib/locales/mk.yml +0 -55
- data/lib/locales/ml.yml +0 -55
- data/lib/locales/mn.yml +0 -55
- data/lib/locales/mr-IN.yml +0 -55
- data/lib/locales/ms.yml +0 -55
- data/lib/locales/nb.yml +0 -55
- data/lib/locales/ne.yml +0 -55
- data/lib/locales/nl.yml +0 -55
- data/lib/locales/nn.yml +0 -55
- data/lib/locales/oc.yml +0 -55
- data/lib/locales/or.yml +0 -55
- data/lib/locales/pa.yml +0 -55
- data/lib/locales/pl.yml +0 -55
- data/lib/locales/pt.yml +0 -55
- data/lib/locales/rm.yml +0 -55
- data/lib/locales/ro.yml +0 -55
- data/lib/locales/ru.yml +0 -55
- data/lib/locales/sc.yml +0 -55
- data/lib/locales/sk.yml +0 -55
- data/lib/locales/sl.yml +0 -55
- data/lib/locales/sq.yml +0 -55
- data/lib/locales/sr.yml +0 -55
- data/lib/locales/st.yml +0 -55
- data/lib/locales/sv.yml +0 -55
- data/lib/locales/sw.yml +0 -55
- data/lib/locales/ta.yml +0 -55
- data/lib/locales/te.yml +0 -55
- data/lib/locales/th.yml +0 -55
- data/lib/locales/tl.yml +0 -55
- data/lib/locales/tr.yml +0 -55
- data/lib/locales/tt.yml +0 -55
- data/lib/locales/ug.yml +0 -55
- data/lib/locales/uk.yml +0 -55
- data/lib/locales/ur.yml +0 -55
- data/lib/locales/uz.yml +0 -55
- data/lib/locales/vi.yml +0 -55
- data/lib/locales/wo.yml +0 -55
- data/lib/locales/zh-CN.yml +0 -55
- data/lib/locales/zh-HK.yml +0 -55
- data/lib/locales/zh-TW.yml +0 -55
- data/lib/locales/zh-YUE.yml +0 -55
data/lib/cmdx/result.rb
CHANGED
|
@@ -1,635 +1,358 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module CMDx
|
|
4
|
-
#
|
|
5
|
-
# status
|
|
4
|
+
# Frozen outcome of a task execution. Provides read-only access to the
|
|
5
|
+
# task's signal (state/status/reason/metadata/cause), the chain it belongs
|
|
6
|
+
# to, its context, and lifecycle metadata (retries, duration, rollback,
|
|
7
|
+
# deprecated). Constructed by Runtime at the end of `execute`.
|
|
6
8
|
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
# checking and conditional handling.
|
|
9
|
+
# @see Runtime#finalize_result
|
|
10
|
+
# @see Signal
|
|
10
11
|
class Result
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
# @
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
]
|
|
28
|
-
|
|
29
|
-
# @
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
# @rbs FAILURE_KEY_REGEX: Regexp
|
|
39
|
-
FAILURE_KEY_REGEX = /_failure\z/
|
|
40
|
-
private_constant :FAILURE_KEY_REGEX
|
|
41
|
-
|
|
42
|
-
# Returns the task instance associated with this result.
|
|
43
|
-
#
|
|
44
|
-
# @return [CMDx::Task] The task instance
|
|
45
|
-
#
|
|
46
|
-
# @example
|
|
47
|
-
# result.task.id # => "users/create"
|
|
48
|
-
#
|
|
49
|
-
# @rbs @task: Task
|
|
50
|
-
attr_reader :task
|
|
51
|
-
|
|
52
|
-
# Returns the current execution state of the result.
|
|
53
|
-
#
|
|
54
|
-
# @return [String] One of: "initialized", "executing", "complete", "interrupted"
|
|
55
|
-
#
|
|
56
|
-
# @example
|
|
57
|
-
# result.state # => "complete"
|
|
58
|
-
#
|
|
59
|
-
# @rbs @state: String
|
|
60
|
-
attr_reader :state
|
|
13
|
+
EVENTS = Set[
|
|
14
|
+
*Signal::STATES,
|
|
15
|
+
*Signal::STATUSES,
|
|
16
|
+
:ok,
|
|
17
|
+
:ko
|
|
18
|
+
].map!(&:to_sym).freeze
|
|
19
|
+
private_constant :EVENTS
|
|
20
|
+
|
|
21
|
+
attr_reader :chain
|
|
22
|
+
|
|
23
|
+
# @param chain [Chain] the chain this result belongs to
|
|
24
|
+
# @param task [Task] the executed task instance
|
|
25
|
+
# @param signal [Signal] the final signal from the task's lifecycle
|
|
26
|
+
# @param options [Hash{Symbol => Object}] frozen execution metadata
|
|
27
|
+
# @option options [String] :tid
|
|
28
|
+
# @option options [Boolean] :strict
|
|
29
|
+
# @option options [Boolean] :deprecated
|
|
30
|
+
# @option options [Boolean] :rolled_back
|
|
31
|
+
# @option options [Integer] :retries
|
|
32
|
+
# @option options [Float] :duration milliseconds
|
|
33
|
+
def initialize(chain, task, signal, **options)
|
|
34
|
+
@chain = chain
|
|
35
|
+
@task = task
|
|
36
|
+
@signal = signal
|
|
37
|
+
@options = options.freeze
|
|
38
|
+
end
|
|
61
39
|
|
|
62
|
-
#
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
# @example
|
|
67
|
-
# result.status # => "success"
|
|
68
|
-
#
|
|
69
|
-
# @rbs @status: String
|
|
70
|
-
attr_reader :status
|
|
40
|
+
# @return [String] uuid_v7 identifier for this execution
|
|
41
|
+
def tid
|
|
42
|
+
@options[:tid]
|
|
43
|
+
end
|
|
71
44
|
|
|
72
|
-
#
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
# @example
|
|
77
|
-
# result.metadata # => { duration: 1.5, code: 200, message: "Success" }
|
|
78
|
-
#
|
|
79
|
-
# @rbs @metadata: Hash[Symbol, untyped]
|
|
80
|
-
attr_reader :metadata
|
|
45
|
+
# @return [Class<Task>] the task class that ran
|
|
46
|
+
def task
|
|
47
|
+
@task.class
|
|
48
|
+
end
|
|
81
49
|
|
|
82
|
-
#
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
# @example
|
|
87
|
-
# result.reason # => "Validation failed"
|
|
88
|
-
#
|
|
89
|
-
# @rbs @reason: (String | nil)
|
|
90
|
-
attr_reader :reason
|
|
50
|
+
# @return [String] `"Task"` or `"Workflow"`
|
|
51
|
+
def type
|
|
52
|
+
task.type
|
|
53
|
+
end
|
|
91
54
|
|
|
92
|
-
#
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
# @example
|
|
97
|
-
# result.cause # => #<StandardError: Connection timeout>
|
|
98
|
-
#
|
|
99
|
-
# @rbs @cause: (Exception | nil)
|
|
100
|
-
attr_reader :cause
|
|
55
|
+
# @return [String, nil] correlation id or the global configuration's correlation id
|
|
56
|
+
def xid
|
|
57
|
+
chain.xid
|
|
58
|
+
end
|
|
101
59
|
|
|
102
|
-
#
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
# @example
|
|
107
|
-
# result.retries # => 2
|
|
108
|
-
#
|
|
109
|
-
# @rbs @retries: Integer
|
|
110
|
-
attr_accessor :retries
|
|
60
|
+
# @return [String] uuid_v7 identifier for the chain this result belongs to
|
|
61
|
+
def cid
|
|
62
|
+
chain.id
|
|
63
|
+
end
|
|
111
64
|
|
|
112
|
-
#
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
# @return [Boolean] Whether the result is strict
|
|
117
|
-
#
|
|
118
|
-
# @example
|
|
119
|
-
# result.strict? # => true
|
|
120
|
-
#
|
|
121
|
-
# @rbs @strict: bool
|
|
122
|
-
attr_reader :strict
|
|
65
|
+
# @return [Integer, nil] this result's position in the chain
|
|
66
|
+
def index
|
|
67
|
+
@chain.index(self)
|
|
68
|
+
end
|
|
123
69
|
|
|
124
|
-
#
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
# @example
|
|
129
|
-
# result.rolled_back? # => true
|
|
130
|
-
#
|
|
131
|
-
# @rbs @rolled_back: bool
|
|
132
|
-
attr_accessor :rolled_back
|
|
70
|
+
# @return [Boolean] true when this result is the root of the chain
|
|
71
|
+
def root?
|
|
72
|
+
!!@options[:root]
|
|
73
|
+
end
|
|
133
74
|
|
|
134
|
-
|
|
75
|
+
# @return [Context] frozen after the root task's teardown
|
|
76
|
+
def context
|
|
77
|
+
@task.context
|
|
78
|
+
end
|
|
135
79
|
alias ctx context
|
|
136
80
|
|
|
137
|
-
# @
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
#
|
|
141
|
-
# @raise [TypeError] When task is not a CMDx::Task instance
|
|
142
|
-
#
|
|
143
|
-
# @example
|
|
144
|
-
# result = CMDx::Result.new(my_task)
|
|
145
|
-
# result.state # => "initialized"
|
|
146
|
-
#
|
|
147
|
-
# @rbs (Task) -> void
|
|
148
|
-
def initialize(task)
|
|
149
|
-
raise TypeError, "must be a CMDx::Task" unless task.is_a?(CMDx::Task)
|
|
150
|
-
|
|
151
|
-
@task = task
|
|
152
|
-
@state = INITIALIZED
|
|
153
|
-
@status = SUCCESS
|
|
154
|
-
@metadata = {}
|
|
155
|
-
@reason = nil
|
|
156
|
-
@cause = nil
|
|
157
|
-
@retries = 0
|
|
158
|
-
@strict = true
|
|
159
|
-
@rolled_back = false
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
STATES.each do |s|
|
|
163
|
-
# @return [Boolean] Whether the result is in the specified state
|
|
164
|
-
#
|
|
165
|
-
# @example
|
|
166
|
-
# result.initialized? # => true
|
|
167
|
-
# result.executing? # => false
|
|
168
|
-
#
|
|
169
|
-
# @rbs () -> bool
|
|
170
|
-
define_method(:"#{s}?") { state == s }
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
# @return [self] Returns self for method chaining
|
|
174
|
-
#
|
|
175
|
-
# @example
|
|
176
|
-
# result.executed! # Transitions to complete or interrupted
|
|
177
|
-
#
|
|
178
|
-
# @rbs () -> self
|
|
179
|
-
def executed!
|
|
180
|
-
success? ? complete! : interrupt!
|
|
81
|
+
# @return [Errors] frozen by Runtime teardown
|
|
82
|
+
def errors
|
|
83
|
+
@task.errors
|
|
181
84
|
end
|
|
182
85
|
|
|
183
|
-
# @return [
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
# result.executed? # => true if complete? || interrupted?
|
|
187
|
-
#
|
|
188
|
-
# @rbs () -> bool
|
|
189
|
-
def executed?
|
|
190
|
-
complete? || interrupted?
|
|
86
|
+
# @return [String] one of {Signal::STATES}
|
|
87
|
+
def state
|
|
88
|
+
@signal.state
|
|
191
89
|
end
|
|
192
90
|
|
|
193
|
-
# @
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
# result.executing! # Transitions from initialized to executing
|
|
197
|
-
#
|
|
198
|
-
# @rbs () -> void
|
|
199
|
-
def executing!
|
|
200
|
-
return if executing?
|
|
201
|
-
|
|
202
|
-
raise "can only transition to #{EXECUTING} from #{INITIALIZED}" unless initialized?
|
|
203
|
-
|
|
204
|
-
@state = EXECUTING
|
|
91
|
+
# @return [Boolean]
|
|
92
|
+
def complete?
|
|
93
|
+
@signal.complete?
|
|
205
94
|
end
|
|
206
95
|
|
|
207
|
-
# @
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
# result.complete! # Transitions from executing to complete
|
|
211
|
-
#
|
|
212
|
-
# @rbs () -> void
|
|
213
|
-
def complete!
|
|
214
|
-
return if complete?
|
|
215
|
-
|
|
216
|
-
raise "can only transition to #{COMPLETE} from #{EXECUTING}" unless executing?
|
|
217
|
-
|
|
218
|
-
@state = COMPLETE
|
|
96
|
+
# @return [Boolean]
|
|
97
|
+
def interrupted?
|
|
98
|
+
@signal.interrupted?
|
|
219
99
|
end
|
|
220
100
|
|
|
221
|
-
# @
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
# result.interrupt! # Transitions from executing to interrupted
|
|
225
|
-
#
|
|
226
|
-
# @rbs () -> void
|
|
227
|
-
def interrupt!
|
|
228
|
-
return if interrupted?
|
|
229
|
-
|
|
230
|
-
raise "cannot transition to #{INTERRUPTED} from #{COMPLETE}" if complete?
|
|
231
|
-
|
|
232
|
-
@state = INTERRUPTED
|
|
101
|
+
# @return [String] one of {Signal::STATUSES}
|
|
102
|
+
def status
|
|
103
|
+
@signal.status
|
|
233
104
|
end
|
|
234
105
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
# @example
|
|
239
|
-
# result.success? # => true
|
|
240
|
-
# result.failed? # => false
|
|
241
|
-
#
|
|
242
|
-
# @rbs () -> bool
|
|
243
|
-
define_method(:"#{s}?") { status == s }
|
|
106
|
+
# @return [Boolean]
|
|
107
|
+
def success?
|
|
108
|
+
@signal.success?
|
|
244
109
|
end
|
|
245
110
|
|
|
246
|
-
# @return [Boolean]
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
# result.good? # => true if !failed?
|
|
250
|
-
#
|
|
251
|
-
# @rbs () -> bool
|
|
252
|
-
def good?
|
|
253
|
-
!failed?
|
|
111
|
+
# @return [Boolean]
|
|
112
|
+
def skipped?
|
|
113
|
+
@signal.skipped?
|
|
254
114
|
end
|
|
255
|
-
alias ok? good?
|
|
256
115
|
|
|
257
|
-
# @return [Boolean]
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
# result.bad? # => true if !success?
|
|
261
|
-
#
|
|
262
|
-
# @rbs () -> bool
|
|
263
|
-
def bad?
|
|
264
|
-
!success?
|
|
116
|
+
# @return [Boolean]
|
|
117
|
+
def failed?
|
|
118
|
+
@signal.failed?
|
|
265
119
|
end
|
|
266
120
|
|
|
267
|
-
# @
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
#
|
|
271
|
-
# @raise [ArgumentError] When no block is provided
|
|
272
|
-
#
|
|
273
|
-
# @example
|
|
274
|
-
# result.on(:bad) { |r| puts "Task had issues: #{r.reason}" }
|
|
275
|
-
# result.on(:success, :complete) { |r| puts "Task completed successfully" }
|
|
276
|
-
#
|
|
277
|
-
# @rbs () { (Result) -> void } -> self
|
|
278
|
-
def on(*states_or_statuses, &)
|
|
279
|
-
raise ArgumentError, "block required" unless block_given?
|
|
280
|
-
|
|
281
|
-
yield(self) if states_or_statuses.any? { |s| public_send(:"#{s}?") }
|
|
282
|
-
self
|
|
121
|
+
# @return [Boolean]
|
|
122
|
+
def ok?
|
|
123
|
+
@signal.ok?
|
|
283
124
|
end
|
|
284
125
|
|
|
285
|
-
#
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
#
|
|
289
|
-
# @param reason [String, nil] Reason or note for the success
|
|
290
|
-
# @param halt [Boolean] Whether to halt execution after success
|
|
291
|
-
# @param metadata [Hash] Additional metadata about the success
|
|
292
|
-
# @option metadata [Object] :* Any key-value pairs for additional metadata
|
|
293
|
-
#
|
|
294
|
-
# @raise [RuntimeError] When status is not success
|
|
295
|
-
#
|
|
296
|
-
# @example
|
|
297
|
-
# result.success!("Created 42 records")
|
|
298
|
-
# result.success!("Imported", halt: false, rows: 100)
|
|
299
|
-
#
|
|
300
|
-
# @rbs (?String? reason, halt: bool, **untyped metadata) -> void
|
|
301
|
-
def success!(reason = nil, halt: true, **metadata)
|
|
302
|
-
raise "can only be used while #{SUCCESS}" unless success?
|
|
303
|
-
|
|
304
|
-
@reason = reason
|
|
305
|
-
@metadata = metadata
|
|
306
|
-
|
|
307
|
-
throw(:cmdx_halt) if halt
|
|
126
|
+
# @return [Boolean]
|
|
127
|
+
def ko?
|
|
128
|
+
@signal.ko?
|
|
308
129
|
end
|
|
309
130
|
|
|
310
|
-
#
|
|
311
|
-
#
|
|
312
|
-
# @param cause [Exception, nil] Exception that caused the skip
|
|
313
|
-
# @param strict [Boolean] Whether this skip is strict (default: true).
|
|
314
|
-
# When false, {CMDx::Executor#halt_execution?} returns false regardless of task settings.
|
|
315
|
-
# @param metadata [Hash] Additional metadata about the skip
|
|
316
|
-
# @option metadata [Object] :* Any key-value pairs for additional metadata
|
|
131
|
+
# Dispatches the block when any of `keys` matches a truthy predicate on
|
|
132
|
+
# this result. Returns `self` for chaining.
|
|
317
133
|
#
|
|
318
|
-
# @
|
|
134
|
+
# @param keys [Array<Symbol, String>] any of the predicate bases:
|
|
135
|
+
# `complete`, `interrupted`, `success`, `skipped`, `failed`, `ok`, `ko`
|
|
136
|
+
# @yieldparam result [Result] this result
|
|
137
|
+
# @return [Result] self for chaining
|
|
138
|
+
# @raise [ArgumentError] when no block is given or a key is unknown
|
|
319
139
|
#
|
|
320
140
|
# @example
|
|
321
|
-
# result
|
|
322
|
-
#
|
|
323
|
-
#
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
def skip!(reason = nil, halt: true, cause: nil, strict: true, **metadata)
|
|
327
|
-
return if skipped?
|
|
141
|
+
# result
|
|
142
|
+
# .on(:success) { |r| deliver(r.context) }
|
|
143
|
+
# .on(:failed) { |r| alert(r.reason) }
|
|
144
|
+
def on(*keys)
|
|
145
|
+
raise ArgumentError, "block required" unless block_given?
|
|
328
146
|
|
|
329
|
-
|
|
147
|
+
yield(self) if keys.any? do |k|
|
|
148
|
+
unless EVENTS.include?(k.to_sym)
|
|
149
|
+
raise ArgumentError,
|
|
150
|
+
"unknown event #{k.inspect}, must be one of #{EVENTS.join(', ')}"
|
|
151
|
+
end
|
|
330
152
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
@reason = reason || Locale.t("cmdx.reasons.unspecified")
|
|
334
|
-
@cause = cause
|
|
335
|
-
@strict = strict
|
|
336
|
-
@metadata = metadata
|
|
153
|
+
public_send(:"#{k}?")
|
|
154
|
+
end
|
|
337
155
|
|
|
338
|
-
|
|
156
|
+
self
|
|
339
157
|
end
|
|
340
158
|
|
|
341
|
-
# @
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
# @param strict [Boolean] Whether this failure is strict (default: true).
|
|
345
|
-
# When false, {CMDx::Executor#halt_execution?} returns false regardless of task settings.
|
|
346
|
-
# @param metadata [Hash] Additional metadata about the failure
|
|
347
|
-
# @option metadata [Object] :* Any key-value pairs for additional metadata
|
|
348
|
-
#
|
|
349
|
-
# @raise [RuntimeError] When attempting to fail from invalid status
|
|
350
|
-
#
|
|
351
|
-
# @example
|
|
352
|
-
# result.fail!("Validation failed", cause: validation_error)
|
|
353
|
-
# result.fail!("Network timeout", halt: false, timeout: 30)
|
|
354
|
-
# result.fail!("Soft failure", strict: false)
|
|
355
|
-
#
|
|
356
|
-
# @rbs (?String? reason, halt: bool, cause: Exception?, strict: bool, **untyped metadata) -> void
|
|
357
|
-
def fail!(reason = nil, halt: true, cause: nil, strict: true, **metadata)
|
|
358
|
-
return if failed?
|
|
359
|
-
|
|
360
|
-
raise "can only transition to #{FAILED} from #{SUCCESS}" unless success?
|
|
361
|
-
|
|
362
|
-
@state = INTERRUPTED
|
|
363
|
-
@status = FAILED
|
|
364
|
-
@reason = reason || Locale.t("cmdx.reasons.unspecified")
|
|
365
|
-
@cause = cause
|
|
366
|
-
@strict = strict
|
|
367
|
-
@metadata = metadata
|
|
368
|
-
|
|
369
|
-
halt! if halt
|
|
159
|
+
# @return [String, nil]
|
|
160
|
+
def reason
|
|
161
|
+
@signal.reason
|
|
370
162
|
end
|
|
371
163
|
|
|
372
|
-
# @
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
# @example
|
|
376
|
-
# result.halt! # Raises appropriate fault based on status
|
|
377
|
-
#
|
|
378
|
-
# @rbs () -> void
|
|
379
|
-
def halt!
|
|
380
|
-
return if success?
|
|
381
|
-
|
|
382
|
-
klass = skipped? ? SkipFault : FailFault
|
|
383
|
-
fault = klass.new(self)
|
|
384
|
-
|
|
385
|
-
# Strip the first two frames (this method and the delegator)
|
|
386
|
-
frames = caller_locations(3..-1)
|
|
387
|
-
|
|
388
|
-
unless frames.empty?
|
|
389
|
-
frames = frames.map(&:to_s)
|
|
390
|
-
|
|
391
|
-
if (cleaner = task.class.settings.backtrace_cleaner)
|
|
392
|
-
cleaner.call(frames)
|
|
393
|
-
end
|
|
394
|
-
|
|
395
|
-
fault.set_backtrace(frames)
|
|
396
|
-
end
|
|
397
|
-
|
|
398
|
-
raise(fault)
|
|
164
|
+
# @return [Hash{Symbol => Object}] frozen empty hash when none provided
|
|
165
|
+
def metadata
|
|
166
|
+
@signal.metadata
|
|
399
167
|
end
|
|
400
168
|
|
|
401
|
-
#
|
|
402
|
-
#
|
|
403
|
-
#
|
|
404
|
-
# @param metadata [Hash] Additional metadata to merge
|
|
405
|
-
# @option metadata [Object] :* Any key-value pairs for additional metadata
|
|
406
|
-
#
|
|
407
|
-
# @raise [TypeError] When result is not a CMDx::Result instance
|
|
169
|
+
# The upstream failed result this one was echoed from (via `Task#throw!`
|
|
170
|
+
# or a rescued {Fault} inside `work`). `nil` when this is a locally
|
|
171
|
+
# originated failure or the result didn't fail.
|
|
408
172
|
#
|
|
409
|
-
# @
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
# @rbs (Result result, halt: bool, cause: Exception?, **untyped metadata) -> void
|
|
414
|
-
def throw!(result, halt: true, cause: nil, **metadata)
|
|
415
|
-
raise TypeError, "must be a CMDx::Result" unless result.is_a?(Result)
|
|
416
|
-
|
|
417
|
-
@state = result.state
|
|
418
|
-
@status = result.status
|
|
419
|
-
@reason = result.reason
|
|
420
|
-
@cause = cause || result.cause
|
|
421
|
-
@metadata = result.metadata.merge(metadata)
|
|
173
|
+
# @return [Result, nil]
|
|
174
|
+
def origin
|
|
175
|
+
@signal.origin
|
|
176
|
+
end
|
|
422
177
|
|
|
423
|
-
|
|
178
|
+
# @return [Exception, nil]
|
|
179
|
+
def cause
|
|
180
|
+
@signal.cause
|
|
424
181
|
end
|
|
425
182
|
|
|
426
|
-
#
|
|
427
|
-
#
|
|
428
|
-
#
|
|
429
|
-
# cause = result.caused_failure
|
|
430
|
-
# puts "Caused by: #{cause.task.id}" if cause
|
|
183
|
+
# The originating failed result at the bottom of the propagation chain.
|
|
184
|
+
# Walks `origin` recursively. `self` when this result is the originator;
|
|
185
|
+
# `nil` when not failed.
|
|
431
186
|
#
|
|
432
|
-
# @
|
|
187
|
+
# @return [Result, nil]
|
|
433
188
|
def caused_failure
|
|
434
189
|
return unless failed?
|
|
435
190
|
|
|
436
|
-
|
|
191
|
+
@caused_failure ||= origin ? origin.caused_failure : self
|
|
437
192
|
end
|
|
438
193
|
|
|
439
|
-
# @return [Boolean]
|
|
440
|
-
#
|
|
441
|
-
# @example
|
|
442
|
-
# if result.caused_failure?
|
|
443
|
-
# puts "This task caused the failure"
|
|
444
|
-
# end
|
|
445
|
-
#
|
|
446
|
-
# @rbs () -> bool
|
|
194
|
+
# @return [Boolean] true when this result originated the failure chain
|
|
447
195
|
def caused_failure?
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
caused_failure == self
|
|
196
|
+
failed? && origin.nil?
|
|
451
197
|
end
|
|
452
198
|
|
|
453
|
-
#
|
|
454
|
-
#
|
|
455
|
-
# @example
|
|
456
|
-
# thrown = result.threw_failure
|
|
457
|
-
# puts "Thrown by: #{thrown.task.id}" if thrown
|
|
199
|
+
# The nearest upstream failed result. `self` when this result is the
|
|
200
|
+
# originator; `nil` when not failed.
|
|
458
201
|
#
|
|
459
|
-
# @
|
|
202
|
+
# @return [Result, nil]
|
|
460
203
|
def threw_failure
|
|
461
204
|
return unless failed?
|
|
462
205
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
chain.results.each do |r|
|
|
467
|
-
next unless r.failed?
|
|
468
|
-
|
|
469
|
-
return r if r.index > current
|
|
470
|
-
|
|
471
|
-
last_failed = r
|
|
472
|
-
end
|
|
206
|
+
origin || self
|
|
207
|
+
end
|
|
473
208
|
|
|
474
|
-
|
|
209
|
+
# @return [Boolean] true when this result re-threw an upstream failure
|
|
210
|
+
def thrown_failure?
|
|
211
|
+
failed? && !origin.nil?
|
|
475
212
|
end
|
|
476
213
|
|
|
477
|
-
#
|
|
478
|
-
#
|
|
479
|
-
#
|
|
480
|
-
# if result.threw_failure?
|
|
481
|
-
# puts "This task threw the failure"
|
|
482
|
-
# end
|
|
214
|
+
# The backtrace captured by `fail!` / `throw!` for Fault propagation.
|
|
215
|
+
# `nil` when this result is not a failure or the failure didn't capture
|
|
216
|
+
# a backtrace.
|
|
483
217
|
#
|
|
484
|
-
# @
|
|
485
|
-
def
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
threw_failure == self
|
|
218
|
+
# @return [Array<String>, nil]
|
|
219
|
+
def backtrace
|
|
220
|
+
@signal.backtrace
|
|
489
221
|
end
|
|
490
222
|
|
|
491
|
-
# @return [
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
# if result.thrown_failure?
|
|
495
|
-
# puts "This failure was thrown from another task"
|
|
496
|
-
# end
|
|
497
|
-
#
|
|
498
|
-
# @rbs () -> bool
|
|
499
|
-
def thrown_failure?
|
|
500
|
-
failed? && !caused_failure?
|
|
223
|
+
# @return [Integer]
|
|
224
|
+
def retries
|
|
225
|
+
@options[:retries] || 0
|
|
501
226
|
end
|
|
502
227
|
|
|
503
|
-
# @return [Boolean]
|
|
504
|
-
#
|
|
505
|
-
# @example
|
|
506
|
-
# result.retried? # => true
|
|
507
|
-
#
|
|
508
|
-
# @rbs () -> bool
|
|
228
|
+
# @return [Boolean]
|
|
509
229
|
def retried?
|
|
510
230
|
retries.positive?
|
|
511
231
|
end
|
|
512
232
|
|
|
513
|
-
# @return [Boolean]
|
|
514
|
-
#
|
|
515
|
-
# @example
|
|
516
|
-
# result.strict? # => true
|
|
517
|
-
#
|
|
518
|
-
# @rbs () -> bool
|
|
233
|
+
# @return [Boolean] true when produced via `execute!`
|
|
519
234
|
def strict?
|
|
520
|
-
!!@strict
|
|
235
|
+
!!@options[:strict]
|
|
521
236
|
end
|
|
522
237
|
|
|
523
|
-
# @return [Boolean]
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
# @
|
|
238
|
+
# @return [Boolean] true when the task class is marked deprecated
|
|
239
|
+
def deprecated?
|
|
240
|
+
!!@options[:deprecated]
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# @return [Boolean] true when a failing task's `rollback` ran
|
|
529
244
|
def rolled_back?
|
|
530
|
-
!!@rolled_back
|
|
245
|
+
!!@options[:rolled_back]
|
|
531
246
|
end
|
|
532
247
|
|
|
533
|
-
# @return [
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
# position = result.index
|
|
537
|
-
# puts "Task #{position + 1} of #{chain.results.count}"
|
|
538
|
-
#
|
|
539
|
-
# @rbs () -> Integer
|
|
540
|
-
def index
|
|
541
|
-
@chain_index || chain.index(self)
|
|
248
|
+
# @return [Float, nil] lifecycle duration in milliseconds
|
|
249
|
+
def duration
|
|
250
|
+
@options[:duration]
|
|
542
251
|
end
|
|
543
252
|
|
|
544
|
-
# @return [String]
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
# result.outcome # => "success" or "interrupted"
|
|
548
|
-
#
|
|
549
|
-
# @rbs () -> String
|
|
550
|
-
def outcome
|
|
551
|
-
initialized? || thrown_failure? ? state : status
|
|
253
|
+
# @return [Array<Symbol, String>]
|
|
254
|
+
def tags
|
|
255
|
+
task.settings.tags
|
|
552
256
|
end
|
|
553
257
|
|
|
554
|
-
# @return [Hash
|
|
555
|
-
#
|
|
556
|
-
#
|
|
557
|
-
# result.to_h
|
|
558
|
-
# # => {state: "complete", status: "success", outcome: "success", reason: "Unspecified", metadata: {}}
|
|
559
|
-
#
|
|
560
|
-
# @rbs () -> Hash[Symbol, untyped]
|
|
258
|
+
# @return [Hash{Symbol => Object}] memoized serialization. Includes
|
|
259
|
+
# `:cause`, `:origin`, `:threw_failure`, `:caused_failure`, `:rolled_back`
|
|
260
|
+
# on failure.
|
|
561
261
|
def to_h
|
|
562
|
-
|
|
262
|
+
@to_h ||= {
|
|
263
|
+
xid:,
|
|
264
|
+
cid:,
|
|
265
|
+
index:,
|
|
266
|
+
root: root?,
|
|
267
|
+
type:,
|
|
268
|
+
task:,
|
|
269
|
+
tid:,
|
|
270
|
+
context:,
|
|
563
271
|
state:,
|
|
564
272
|
status:,
|
|
565
|
-
outcome:,
|
|
566
273
|
reason:,
|
|
567
|
-
metadata
|
|
568
|
-
|
|
569
|
-
|
|
274
|
+
metadata:,
|
|
275
|
+
strict: strict?,
|
|
276
|
+
deprecated: deprecated?,
|
|
277
|
+
retried: retried?,
|
|
278
|
+
retries:,
|
|
279
|
+
duration:,
|
|
280
|
+
tags:
|
|
281
|
+
}.tap do |hash|
|
|
282
|
+
if failed?
|
|
570
283
|
hash[:cause] = cause
|
|
284
|
+
hash[:origin] = hash_for_failure(:origin)
|
|
285
|
+
hash[:threw_failure] = hash_for_failure(:threw_failure)
|
|
286
|
+
hash[:caused_failure] = hash_for_failure(:caused_failure)
|
|
571
287
|
hash[:rolled_back] = rolled_back?
|
|
572
288
|
end
|
|
573
|
-
|
|
574
|
-
if failed?
|
|
575
|
-
STRIP_FAILURE.call(hash, self, :threw_failure)
|
|
576
|
-
STRIP_FAILURE.call(hash, self, :caused_failure)
|
|
577
|
-
end
|
|
578
289
|
end
|
|
579
290
|
end
|
|
580
291
|
|
|
581
|
-
#
|
|
292
|
+
# JSON-friendly hash view. Aliases the memoized {#to_h} for conventional
|
|
293
|
+
# `as_json` callers (e.g. Rails).
|
|
582
294
|
#
|
|
583
|
-
# @
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
295
|
+
# @return [Hash{Symbol => Object}]
|
|
296
|
+
def as_json(*)
|
|
297
|
+
to_h
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
# Serializes the result to a JSON string. Non-primitive entries (the
|
|
301
|
+
# `:task` Class, `:cause` Exception) emit via their stdlib `to_json`
|
|
302
|
+
# defaults; `:context` delegates to {Context#to_json}.
|
|
587
303
|
#
|
|
588
|
-
# @
|
|
304
|
+
# @param args [Array] forwarded to `Hash#to_json`
|
|
305
|
+
# @return [String]
|
|
306
|
+
def to_json(*args)
|
|
307
|
+
to_h.to_json(*args)
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
# @return [String] space-separated `key=value.inspect` pairs; failure
|
|
311
|
+
# references render as `<TaskClass uuid>`.
|
|
589
312
|
def to_s
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
"
|
|
313
|
+
@to_s ||= begin
|
|
314
|
+
buf = String.new(capacity: 256)
|
|
315
|
+
|
|
316
|
+
to_h.each_with_object(buf) do |(k, v), buf|
|
|
317
|
+
buf << " " unless buf.empty?
|
|
318
|
+
|
|
319
|
+
ks = k.name
|
|
320
|
+
|
|
321
|
+
if v.nil?
|
|
322
|
+
buf << ks << "=nil"
|
|
323
|
+
elsif ks == "origin" || ks.end_with?("_failure")
|
|
324
|
+
buf << ks << "=<" << v[:task].to_s << " " << v[:tid] << ">"
|
|
325
|
+
else
|
|
326
|
+
buf << ks << "=" << v.inspect
|
|
327
|
+
end
|
|
595
328
|
end
|
|
596
329
|
end
|
|
597
330
|
end
|
|
598
331
|
|
|
599
|
-
#
|
|
332
|
+
# Pattern-matching support for `case result in {...}`.
|
|
600
333
|
#
|
|
601
|
-
# @
|
|
602
|
-
#
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
# @rbs (*untyped) -> Array[untyped]
|
|
606
|
-
def deconstruct(*)
|
|
607
|
-
[state, status, reason, cause, metadata]
|
|
334
|
+
# @param keys [Array<Symbol>, nil] restrict the returned hash to these keys
|
|
335
|
+
# @return [Hash{Symbol => Object}]
|
|
336
|
+
def deconstruct_keys(keys)
|
|
337
|
+
keys.nil? ? to_h : to_h.slice(*keys)
|
|
608
338
|
end
|
|
609
339
|
|
|
610
|
-
#
|
|
340
|
+
# Pattern-matching support for `case result in [...]`.
|
|
611
341
|
#
|
|
612
|
-
# @
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
#
|
|
620
|
-
# @
|
|
621
|
-
def
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
cause: cause,
|
|
627
|
-
metadata: metadata,
|
|
628
|
-
outcome: outcome,
|
|
629
|
-
executed: executed?,
|
|
630
|
-
good: good?,
|
|
631
|
-
bad: bad?
|
|
632
|
-
}
|
|
342
|
+
# @return [Array<Array(Symbol, Object)>]
|
|
343
|
+
def deconstruct
|
|
344
|
+
to_h.to_a
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
private
|
|
348
|
+
|
|
349
|
+
# @param key [Symbol] reader name such as `:caused_failure` or `:threw_failure`
|
|
350
|
+
# @return [Hash{Symbol => Object}, nil] compact `{task:, tid:}` map for graph hints
|
|
351
|
+
def hash_for_failure(key)
|
|
352
|
+
r = public_send(key)
|
|
353
|
+
return if r.nil?
|
|
354
|
+
|
|
355
|
+
{ task: r.task, tid: r.tid }
|
|
633
356
|
end
|
|
634
357
|
|
|
635
358
|
end
|