cmdx 0.5.0 → 1.0.1
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/.DS_Store +0 -0
- data/.cursor/rules/cursor-instructions.mdc +6 -0
- data/.rubocop.yml +19 -1
- data/.ruby-version +1 -1
- data/CHANGELOG.md +95 -28
- data/README.md +73 -25
- data/docs/ai_prompts.md +319 -0
- data/docs/basics/call.md +234 -14
- data/docs/basics/chain.md +280 -0
- data/docs/basics/context.md +241 -33
- data/docs/basics/setup.md +85 -12
- data/docs/callbacks.md +283 -0
- data/docs/configuration.md +155 -30
- data/docs/getting_started.md +145 -22
- data/docs/internationalization.md +148 -0
- data/docs/interruptions/exceptions.md +198 -11
- data/docs/interruptions/faults.md +196 -44
- data/docs/interruptions/halt.md +188 -35
- data/docs/logging.md +204 -53
- data/docs/middlewares.md +745 -0
- data/docs/outcomes/result.md +305 -10
- data/docs/outcomes/states.md +212 -31
- data/docs/outcomes/statuses.md +284 -30
- data/docs/parameters/coercions.md +411 -29
- data/docs/parameters/defaults.md +258 -25
- data/docs/parameters/definitions.md +247 -72
- data/docs/parameters/namespacing.md +259 -27
- data/docs/parameters/validations.md +173 -168
- data/docs/testing.md +560 -0
- data/docs/tips_and_tricks.md +103 -42
- data/docs/workflows.md +329 -0
- data/lib/cmdx/.DS_Store +0 -0
- data/lib/cmdx/callback.rb +69 -0
- data/lib/cmdx/callback_registry.rb +106 -0
- data/lib/cmdx/chain.rb +190 -0
- data/lib/cmdx/chain_inspector.rb +149 -0
- data/lib/cmdx/chain_serializer.rb +175 -0
- data/lib/cmdx/coercions/array.rb +37 -0
- data/lib/cmdx/coercions/big_decimal.rb +33 -0
- data/lib/cmdx/coercions/boolean.rb +41 -1
- data/lib/cmdx/coercions/complex.rb +31 -0
- data/lib/cmdx/coercions/date.rb +39 -0
- data/lib/cmdx/coercions/date_time.rb +39 -0
- data/lib/cmdx/coercions/float.rb +31 -0
- data/lib/cmdx/coercions/hash.rb +42 -0
- data/lib/cmdx/coercions/integer.rb +32 -0
- data/lib/cmdx/coercions/rational.rb +31 -0
- data/lib/cmdx/coercions/string.rb +31 -0
- data/lib/cmdx/coercions/time.rb +39 -0
- data/lib/cmdx/coercions/virtual.rb +31 -0
- data/lib/cmdx/configuration.rb +217 -9
- data/lib/cmdx/context.rb +173 -2
- data/lib/cmdx/core_ext/hash.rb +72 -0
- data/lib/cmdx/core_ext/module.rb +94 -0
- data/lib/cmdx/core_ext/object.rb +105 -0
- data/lib/cmdx/correlator.rb +217 -0
- data/lib/cmdx/error.rb +210 -8
- data/lib/cmdx/errors.rb +256 -1
- data/lib/cmdx/fault.rb +177 -2
- data/lib/cmdx/faults.rb +158 -2
- data/lib/cmdx/immutator.rb +121 -2
- data/lib/cmdx/lazy_struct.rb +261 -18
- data/lib/cmdx/log_formatters/json.rb +46 -0
- data/lib/cmdx/log_formatters/key_value.rb +46 -0
- data/lib/cmdx/log_formatters/line.rb +54 -0
- data/lib/cmdx/log_formatters/logstash.rb +64 -0
- data/lib/cmdx/log_formatters/pretty_json.rb +57 -0
- data/lib/cmdx/log_formatters/pretty_key_value.rb +51 -0
- data/lib/cmdx/log_formatters/pretty_line.rb +60 -0
- data/lib/cmdx/log_formatters/raw.rb +54 -0
- data/lib/cmdx/logger.rb +85 -0
- data/lib/cmdx/logger_ansi.rb +93 -7
- data/lib/cmdx/logger_serializer.rb +116 -0
- data/lib/cmdx/middleware.rb +74 -0
- data/lib/cmdx/middleware_registry.rb +106 -0
- data/lib/cmdx/middlewares/correlate.rb +266 -0
- data/lib/cmdx/middlewares/timeout.rb +232 -0
- data/lib/cmdx/parameter.rb +228 -1
- data/lib/cmdx/parameter_inspector.rb +61 -0
- data/lib/cmdx/parameter_registry.rb +125 -0
- data/lib/cmdx/parameter_serializer.rb +83 -0
- data/lib/cmdx/parameter_validator.rb +62 -0
- data/lib/cmdx/parameter_value.rb +109 -1
- data/lib/cmdx/parameters_inspector.rb +59 -0
- data/lib/cmdx/parameters_serializer.rb +102 -0
- data/lib/cmdx/railtie.rb +123 -3
- data/lib/cmdx/result.rb +367 -25
- data/lib/cmdx/result_ansi.rb +105 -9
- data/lib/cmdx/result_inspector.rb +76 -0
- data/lib/cmdx/result_logger.rb +90 -3
- data/lib/cmdx/result_serializer.rb +137 -0
- data/lib/cmdx/rspec/result_matchers.rb +917 -0
- data/lib/cmdx/rspec/task_matchers.rb +570 -0
- data/lib/cmdx/task.rb +405 -37
- data/lib/cmdx/task_serializer.rb +74 -2
- data/lib/cmdx/utils/ansi_color.rb +95 -0
- data/lib/cmdx/utils/log_timestamp.rb +48 -0
- data/lib/cmdx/utils/monotonic_runtime.rb +71 -4
- data/lib/cmdx/utils/name_affix.rb +78 -0
- data/lib/cmdx/validators/custom.rb +82 -0
- data/lib/cmdx/validators/exclusion.rb +94 -0
- data/lib/cmdx/validators/format.rb +102 -8
- data/lib/cmdx/validators/inclusion.rb +104 -0
- data/lib/cmdx/validators/length.rb +128 -0
- data/lib/cmdx/validators/numeric.rb +128 -0
- data/lib/cmdx/validators/presence.rb +93 -7
- data/lib/cmdx/version.rb +7 -1
- data/lib/cmdx/workflow.rb +394 -0
- data/lib/cmdx.rb +25 -64
- data/lib/generators/cmdx/install_generator.rb +37 -1
- data/lib/generators/cmdx/task_generator.rb +69 -1
- data/lib/generators/cmdx/templates/install.rb +43 -15
- data/lib/generators/cmdx/workflow_generator.rb +109 -0
- data/lib/locales/ar.yml +36 -0
- data/lib/locales/cs.yml +36 -0
- data/lib/locales/da.yml +36 -0
- data/lib/locales/de.yml +36 -0
- data/lib/locales/el.yml +36 -0
- data/lib/locales/en.yml +20 -20
- data/lib/locales/es.yml +20 -20
- data/lib/locales/fi.yml +36 -0
- data/lib/locales/fr.yml +36 -0
- data/lib/locales/he.yml +36 -0
- data/lib/locales/hi.yml +36 -0
- data/lib/locales/it.yml +36 -0
- data/lib/locales/ja.yml +36 -0
- data/lib/locales/ko.yml +36 -0
- data/lib/locales/nl.yml +36 -0
- data/lib/locales/no.yml +36 -0
- data/lib/locales/pl.yml +36 -0
- data/lib/locales/pt.yml +36 -0
- data/lib/locales/ru.yml +36 -0
- data/lib/locales/sv.yml +36 -0
- data/lib/locales/th.yml +36 -0
- data/lib/locales/tr.yml +36 -0
- data/lib/locales/vi.yml +36 -0
- data/lib/locales/zh.yml +36 -0
- metadata +77 -15
- data/docs/basics/run.md +0 -34
- data/docs/batch.md +0 -53
- data/docs/example.md +0 -82
- data/docs/hooks.md +0 -62
- data/lib/cmdx/batch.rb +0 -43
- data/lib/cmdx/parameters.rb +0 -35
- data/lib/cmdx/run.rb +0 -39
- data/lib/cmdx/run_inspector.rb +0 -26
- data/lib/cmdx/run_serializer.rb +0 -20
- data/lib/cmdx/task_hook.rb +0 -18
- data/lib/generators/cmdx/batch_generator.rb +0 -30
- /data/lib/generators/cmdx/templates/{batch.rb.tt → workflow.rb.tt} +0 -0
data/docs/outcomes/result.md
CHANGED
@@ -1,17 +1,312 @@
|
|
1
|
-
# Outcomes -
|
1
|
+
# Outcomes - Result
|
2
2
|
|
3
|
-
The result object is
|
4
|
-
|
3
|
+
The result object is the comprehensive return value of task execution, providing
|
4
|
+
complete information about the execution outcome, state, timing, and any data
|
5
|
+
produced during the task lifecycle. Results serve as the primary interface for
|
6
|
+
inspecting task execution outcomes and chaining task operations.
|
7
|
+
|
8
|
+
## Table of Contents
|
9
|
+
|
10
|
+
- [TLDR](#tldr)
|
11
|
+
- [Core Result Attributes](#core-result-attributes)
|
12
|
+
- [State and Status Information](#state-and-status-information)
|
13
|
+
- [Execution Outcome Analysis](#execution-outcome-analysis)
|
14
|
+
- [Runtime and Performance](#runtime-and-performance)
|
15
|
+
- [Failure Chain Analysis](#failure-chain-analysis)
|
16
|
+
- [Index and Position](#index-and-position)
|
17
|
+
- [Result Callbacks and Chaining](#result-callbacks-and-chaining)
|
18
|
+
- [Pattern Matching](#pattern-matching)
|
19
|
+
- [Serialization and Inspection](#serialization-and-inspection)
|
20
|
+
|
21
|
+
## TLDR
|
22
|
+
|
23
|
+
- **Result object** - Comprehensive return value from task execution with `task`, `context`, `chain`, `metadata`
|
24
|
+
- **Status checking** - Use `result.success?`, `result.failed?`, `result.skipped?` for outcomes
|
25
|
+
- **State checking** - Use `result.complete?`, `result.interrupted?`, `result.executed?` for lifecycle
|
26
|
+
- **Callbacks** - Chain with `.on_success`, `.on_failed`, `.on_good`, `.on_bad` for conditional logic
|
27
|
+
- **Failure analysis** - Use `result.caused_failure`, `result.threw_failure` to trace failure chains
|
28
|
+
|
29
|
+
## Core Result Attributes
|
30
|
+
|
31
|
+
Every result provides access to essential execution information:
|
5
32
|
|
6
33
|
```ruby
|
7
|
-
result =
|
8
|
-
|
9
|
-
|
10
|
-
result.
|
11
|
-
result.
|
34
|
+
result = ProcessUserOrderTask.call(order_id: 123)
|
35
|
+
|
36
|
+
# Core objects
|
37
|
+
result.task #=> <ProcessUserOrderTask instance>
|
38
|
+
result.context #=> <CMDx::Context with all task data>
|
39
|
+
result.chain #=> <CMDx::Chain execution tracking>
|
40
|
+
result.metadata #=> Hash with execution metadata
|
41
|
+
|
42
|
+
# Execution information
|
43
|
+
result.id #=> "abc123..." (unique task execution ID)
|
44
|
+
result.state #=> "complete" (execution state)
|
45
|
+
result.status #=> "success" (execution outcome)
|
46
|
+
result.runtime #=> 0.5 (execution time in seconds)
|
12
47
|
```
|
13
48
|
|
49
|
+
> [!NOTE]
|
50
|
+
> Result objects are immutable after task execution completes. All result data reflects the final state of the task execution and cannot be modified.
|
51
|
+
|
52
|
+
## State and Status Information
|
53
|
+
|
54
|
+
Results provide comprehensive methods for checking execution state and status:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
result = ProcessUserOrderTask.call
|
58
|
+
|
59
|
+
# State predicates (execution lifecycle)
|
60
|
+
result.initialized? #=> false (after execution)
|
61
|
+
result.executing? #=> false (after execution)
|
62
|
+
result.complete? #=> true (successful completion)
|
63
|
+
result.interrupted? #=> false (no interruption)
|
64
|
+
result.executed? #=> true (execution finished)
|
65
|
+
|
66
|
+
# Status predicates (execution outcome)
|
67
|
+
result.success? #=> true (successful execution)
|
68
|
+
result.skipped? #=> false (not skipped)
|
69
|
+
result.failed? #=> false (no failure)
|
70
|
+
|
71
|
+
# Outcome categorization
|
72
|
+
result.good? #=> true (success or skipped)
|
73
|
+
result.bad? #=> false (skipped or failed)
|
74
|
+
```
|
75
|
+
|
76
|
+
## Execution Outcome Analysis
|
77
|
+
|
78
|
+
Results provide additional methods for understanding execution outcomes:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
result = ProcessOrderWorkflowTask.call
|
82
|
+
|
83
|
+
# Outcome determination
|
84
|
+
result.outcome #=> "success" (combines state and status)
|
85
|
+
```
|
86
|
+
|
87
|
+
## Runtime and Performance
|
88
|
+
|
89
|
+
Results capture detailed timing information:
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
result = ProcessUserOrderTask.call
|
93
|
+
|
94
|
+
# Execution timing
|
95
|
+
result.runtime #=> 0.5 (total execution time in seconds)
|
96
|
+
```
|
97
|
+
|
98
|
+
## Failure Chain Analysis
|
99
|
+
|
100
|
+
For failed results, comprehensive failure analysis is available:
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
result = ProcessOrderWorkflowTask.call
|
104
|
+
|
105
|
+
if result.failed?
|
106
|
+
# Find the original cause of failure
|
107
|
+
original_failure = result.caused_failure
|
108
|
+
if original_failure
|
109
|
+
puts "Original failure: #{original_failure.task.class.name}"
|
110
|
+
puts "Reason: #{original_failure.metadata[:reason]}"
|
111
|
+
end
|
112
|
+
|
113
|
+
# Find what threw the failure to this result
|
114
|
+
throwing_task = result.threw_failure
|
115
|
+
if throwing_task
|
116
|
+
puts "Failure thrown by: #{throwing_task.task.class.name}"
|
117
|
+
end
|
118
|
+
|
119
|
+
# Failure classification
|
120
|
+
result.caused_failure? #=> true if this result was the original cause
|
121
|
+
result.threw_failure? #=> true if this result threw a failure
|
122
|
+
result.thrown_failure? #=> true if this result received a thrown failure
|
123
|
+
end
|
124
|
+
```
|
125
|
+
|
126
|
+
> [!IMPORTANT]
|
127
|
+
> Failure chain analysis is only available for failed results. Use these methods to trace the root cause of failures in complex task workflows.
|
128
|
+
|
129
|
+
## Index and Position
|
130
|
+
|
131
|
+
Results track their position within execution chains:
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
result = ProcessUserOrderTask.call
|
135
|
+
|
136
|
+
# Position in execution sequence
|
137
|
+
result.index #=> 0 (first task in chain)
|
138
|
+
|
139
|
+
# Access via chain
|
140
|
+
result.chain.results[result.index] == result #=> true
|
141
|
+
```
|
142
|
+
|
143
|
+
## Result Callbacks and Chaining
|
144
|
+
|
145
|
+
Results support fluent callback patterns for conditional logic:
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
result = ProcessUserOrderTask.call
|
149
|
+
|
150
|
+
# State-based callbacks
|
151
|
+
result
|
152
|
+
.on_complete { |r| logger.info "Task completed successfully" }
|
153
|
+
.on_interrupted { |r| logger.warn "Task was interrupted" }
|
154
|
+
.on_executed { |r| update_metrics(r.runtime) }
|
155
|
+
|
156
|
+
# Status-based callbacks
|
157
|
+
result
|
158
|
+
.on_success { |r| send_confirmation_email(r.context) }
|
159
|
+
.on_skipped { |r| logger.info "Skipped: #{r.metadata[:reason]}" }
|
160
|
+
.on_failed { |r| handle_failure(r) }
|
161
|
+
|
162
|
+
# Outcome-based callbacks
|
163
|
+
result
|
164
|
+
.on_good { |r| celebrate_success }
|
165
|
+
.on_bad { |r| handle_problem }
|
166
|
+
```
|
167
|
+
|
168
|
+
### Callback Chaining Examples
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
ProcessUserOrderTask
|
172
|
+
.call(order_id: 123)
|
173
|
+
.on_success { |result|
|
174
|
+
SendOrderNotificationTask.call(result.context)
|
175
|
+
}
|
176
|
+
.on_failed { |result|
|
177
|
+
ErrorReporter.notify(result.metadata)
|
178
|
+
}
|
179
|
+
.on_executed { |result|
|
180
|
+
MetricsService.record_execution_time(result.runtime)
|
181
|
+
}
|
182
|
+
```
|
183
|
+
|
184
|
+
> [!TIP]
|
185
|
+
> Use result callbacks for clean, functional-style conditional logic. Callbacks return the result object, enabling method chaining and fluent interfaces.
|
186
|
+
|
187
|
+
## Pattern Matching
|
188
|
+
|
189
|
+
Results support Ruby's pattern matching (Ruby 3.0+) through array and hash deconstruction:
|
190
|
+
|
191
|
+
### Array Pattern Matching
|
192
|
+
|
193
|
+
Match against state and status in order:
|
194
|
+
|
195
|
+
```ruby
|
196
|
+
result = ProcessUserOrderTask.call
|
197
|
+
|
198
|
+
case result
|
199
|
+
in ["complete", "success"]
|
200
|
+
puts "Task completed successfully"
|
201
|
+
in ["interrupted", "failed"]
|
202
|
+
puts "Task failed during execution"
|
203
|
+
in ["complete", "skipped"]
|
204
|
+
puts "Task was skipped but completed"
|
205
|
+
end
|
206
|
+
```
|
207
|
+
|
208
|
+
### Hash Pattern Matching
|
209
|
+
|
210
|
+
Match against specific result attributes:
|
211
|
+
|
212
|
+
```ruby
|
213
|
+
result = ProcessUserOrderTask.call
|
214
|
+
|
215
|
+
case result
|
216
|
+
in { state: "complete", status: "success" }
|
217
|
+
puts "Perfect execution!"
|
218
|
+
in { state: "interrupted", status: "failed", metadata: { retryable: true } }
|
219
|
+
puts "Failed but can retry"
|
220
|
+
in { good: true }
|
221
|
+
puts "Execution went well overall"
|
222
|
+
in { bad: true, metadata: { reason: String => reason } }
|
223
|
+
puts "Something went wrong: #{reason}"
|
224
|
+
end
|
225
|
+
```
|
226
|
+
|
227
|
+
### Pattern Matching with Guards
|
228
|
+
|
229
|
+
Use guard clauses for conditional matching:
|
230
|
+
|
231
|
+
```ruby
|
232
|
+
case result
|
233
|
+
in { status: "failed", metadata: { attempts: n } } if n < 3
|
234
|
+
retry_task(result)
|
235
|
+
in { status: "failed", metadata: { attempts: n } } if n >= 3
|
236
|
+
give_up_task(result)
|
237
|
+
in { runtime: time } if time > threshold
|
238
|
+
log_performance_issue(result)
|
239
|
+
end
|
240
|
+
```
|
241
|
+
|
242
|
+
> [!NOTE]
|
243
|
+
> Pattern matching requires Ruby 3.0+. The `deconstruct` method returns `[state, status]` for array patterns, while `deconstruct_keys` provides hash access to `state`, `status`, `metadata`, `executed`, `good`, and `bad` attributes.
|
244
|
+
|
245
|
+
## Serialization and Inspection
|
246
|
+
|
247
|
+
Results provide comprehensive serialization and inspection capabilities:
|
248
|
+
|
249
|
+
### Hash Serialization
|
250
|
+
|
251
|
+
```ruby
|
252
|
+
result = ProcessUserOrderTask.call
|
253
|
+
|
254
|
+
result.to_h
|
255
|
+
#=> {
|
256
|
+
# class: "ProcessUserOrderTask",
|
257
|
+
# type: "Task",
|
258
|
+
# index: 0,
|
259
|
+
# id: "abc123...",
|
260
|
+
# chain_id: "def456...",
|
261
|
+
# tags: [],
|
262
|
+
# state: "complete",
|
263
|
+
# status: "success",
|
264
|
+
# outcome: "success",
|
265
|
+
# metadata: {},
|
266
|
+
# runtime: 0.5
|
267
|
+
# }
|
268
|
+
```
|
269
|
+
|
270
|
+
### Human-Readable Inspection
|
271
|
+
|
272
|
+
```ruby
|
273
|
+
result = ProcessUserOrderTask.call
|
274
|
+
|
275
|
+
result.to_s
|
276
|
+
#=> "ProcessUserOrderTask: type=Task index=0 id=abc123... state=complete status=success outcome=success metadata={} runtime=0.5"
|
277
|
+
```
|
278
|
+
|
279
|
+
### Failure Chain Serialization
|
280
|
+
|
281
|
+
Failed results include failure chain information:
|
282
|
+
|
283
|
+
```ruby
|
284
|
+
failed_result = ProcessOrderWorkflowTask.call
|
285
|
+
|
286
|
+
failed_result.to_h
|
287
|
+
#=> {
|
288
|
+
# # ... standard result data ...
|
289
|
+
# caused_failure: {
|
290
|
+
# class: "ValidateUserOrderTask",
|
291
|
+
# index: 1,
|
292
|
+
# id: "xyz789...",
|
293
|
+
# state: "interrupted",
|
294
|
+
# status: "failed"
|
295
|
+
# },
|
296
|
+
# threw_failure: {
|
297
|
+
# class: "ProcessOrderPaymentTask",
|
298
|
+
# index: 2,
|
299
|
+
# id: "uvw123...",
|
300
|
+
# state: "interrupted",
|
301
|
+
# status: "failed"
|
302
|
+
# }
|
303
|
+
# }
|
304
|
+
```
|
305
|
+
|
306
|
+
> [!NOTE]
|
307
|
+
> Serialized results include complete failure chain information for debugging and audit trails. Use `to_h` for structured data and `to_s` for human-readable output.
|
308
|
+
|
14
309
|
---
|
15
310
|
|
16
|
-
- **Prev:** [Interruptions - Exceptions](
|
17
|
-
- **Next:** [Outcomes - Statuses](
|
311
|
+
- **Prev:** [Interruptions - Exceptions](../interruptions/exceptions.md)
|
312
|
+
- **Next:** [Outcomes - Statuses](statuses.md)
|
data/docs/outcomes/states.md
CHANGED
@@ -1,52 +1,233 @@
|
|
1
|
-
# Outcomes -
|
1
|
+
# Outcomes - States
|
2
2
|
|
3
|
-
|
3
|
+
States represent the execution lifecycle condition of task execution, tracking
|
4
|
+
the progress of tasks through their complete execution journey. States provide
|
5
|
+
insight into where a task is in its lifecycle and enable lifecycle-based
|
6
|
+
decision making and monitoring.
|
4
7
|
|
5
|
-
|
8
|
+
## Table of Contents
|
9
|
+
|
10
|
+
- [TLDR](#tldr)
|
11
|
+
- [State Definitions](#state-definitions)
|
12
|
+
- [State Transitions](#state-transitions)
|
13
|
+
- [State Predicates](#state-predicates)
|
14
|
+
- [State-Based Callbacks](#state-based-callbacks)
|
15
|
+
- [State vs Status Distinction](#state-vs-status-distinction)
|
16
|
+
- [State Inspection and Monitoring](#state-inspection-and-monitoring)
|
17
|
+
- [State Persistence and Logging](#state-persistence-and-logging)
|
18
|
+
|
19
|
+
## TLDR
|
20
|
+
|
21
|
+
- **States** - Track execution lifecycle: `initialized` → `executing` → `complete`/`interrupted`
|
22
|
+
- **Automatic** - States are managed automatically by the framework, never modify manually
|
23
|
+
- **Predicates** - Check with `result.complete?`, `result.interrupted?`, `result.executed?`
|
24
|
+
- **Callbacks** - Use `.on_complete`, `.on_interrupted`, `.on_executed` for lifecycle events
|
25
|
+
- **vs Status** - State = where in lifecycle, Status = how execution ended
|
26
|
+
|
27
|
+
## State Definitions
|
28
|
+
|
29
|
+
| State | Description |
|
6
30
|
| ------------- | ----------- |
|
7
|
-
| `initialized` |
|
8
|
-
| `executing` | Task is actively
|
9
|
-
| `complete` | Task
|
10
|
-
| `interrupted` | Task
|
31
|
+
| `initialized` | Task created but execution not yet started. Default state for new tasks. |
|
32
|
+
| `executing` | Task is actively running its business logic. Transient state during execution. |
|
33
|
+
| `complete` | Task finished execution successfully without any interruption or halt. |
|
34
|
+
| `interrupted` | Task execution was stopped due to a fault, exception, or explicit halt. |
|
35
|
+
|
36
|
+
## State Transitions
|
11
37
|
|
12
|
-
|
13
|
-
> States are automatically transitioned and should **NEVER** be altered manually.
|
38
|
+
States follow a strict lifecycle with controlled transitions:
|
14
39
|
|
15
40
|
```ruby
|
16
|
-
|
17
|
-
|
41
|
+
# Valid state transition flow
|
42
|
+
initialized -> executing -> complete (successful execution)
|
43
|
+
initialized -> executing -> interrupted (failed/halted execution)
|
44
|
+
```
|
45
|
+
|
46
|
+
> [!IMPORTANT]
|
47
|
+
> States are automatically managed during task execution and should **never** be modified manually. State transitions are handled internally by the CMDx framework.
|
18
48
|
|
19
|
-
|
20
|
-
result.executing? #=> false
|
21
|
-
result.complete? #=> true
|
22
|
-
result.interrupted? #=> false
|
49
|
+
### Automatic State Management
|
23
50
|
|
24
|
-
|
25
|
-
|
51
|
+
States are automatically managed during task execution and should **never** be modified manually:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
task = ProcessUserOrderTask.new
|
55
|
+
task.result.state #=> "initialized"
|
56
|
+
|
57
|
+
# During task execution, states transition automatically:
|
58
|
+
# 1. initialized -> executing (when call begins)
|
59
|
+
# 2. executing -> complete (successful completion)
|
60
|
+
# 3. executing -> interrupted (on failure/halt)
|
61
|
+
|
62
|
+
result = ProcessUserOrderTask.call
|
63
|
+
result.state #=> "complete" (if successful)
|
26
64
|
```
|
27
65
|
|
28
|
-
|
66
|
+
### State Transition Methods (Internal Use)
|
29
67
|
|
30
|
-
|
31
|
-
and will have the result available as local variable. Callback handlers can be
|
32
|
-
chained and repeated.
|
68
|
+
These methods handle state transitions internally and are not intended for direct use:
|
33
69
|
|
34
70
|
```ruby
|
35
|
-
result =
|
36
|
-
|
71
|
+
result = ProcessUserOrderTask.new.result
|
72
|
+
|
73
|
+
# Internal state transition methods
|
74
|
+
result.executing! # initialized -> executing
|
75
|
+
result.complete! # executing -> complete
|
76
|
+
result.interrupt! # executing -> interrupted
|
77
|
+
result.executed! # executing -> complete OR interrupted (based on status)
|
78
|
+
```
|
79
|
+
|
80
|
+
> [!WARNING]
|
81
|
+
> State transition methods (`executing!`, `complete!`, `interrupt!`) are for internal framework use only. Never call these methods directly in your application code.
|
37
82
|
|
38
|
-
|
83
|
+
## State Predicates
|
39
84
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
85
|
+
Use state predicates to check the current execution lifecycle:
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
result = ProcessUserOrderTask.call
|
89
|
+
|
90
|
+
# Check current state
|
91
|
+
result.initialized? #=> false (after execution)
|
92
|
+
result.executing? #=> false (after execution)
|
93
|
+
result.complete? #=> true (successful completion)
|
94
|
+
result.interrupted? #=> false (no interruption)
|
95
|
+
|
96
|
+
# Combined state checking
|
97
|
+
result.executed? #=> true (complete OR interrupted)
|
98
|
+
```
|
99
|
+
|
100
|
+
## State-Based Callbacks
|
101
|
+
|
102
|
+
Results provide callback methods for state-based conditional execution:
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
result = ProcessUserOrderTask.call
|
106
|
+
|
107
|
+
# Individual state callbacks
|
108
|
+
result
|
109
|
+
.on_initialized { |r| log_task_created(r) }
|
110
|
+
.on_executing { |r| show_progress_indicator(r) }
|
111
|
+
.on_complete { |r| celebrate_success(r) }
|
112
|
+
.on_interrupted { |r| handle_interruption(r) }
|
113
|
+
|
114
|
+
# Execution completion callback (complete OR interrupted)
|
115
|
+
result
|
116
|
+
.on_executed { |r| cleanup_resources(r) }
|
117
|
+
```
|
118
|
+
|
119
|
+
### Callback Chaining and Combinations
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
ProcessUserOrderTask
|
123
|
+
.call(order_id: 123)
|
124
|
+
.on_complete { |result|
|
125
|
+
# Only runs if task completed successfully
|
126
|
+
send_confirmation_email(result.context)
|
127
|
+
update_order_status(result.context.order)
|
128
|
+
}
|
129
|
+
.on_interrupted { |result|
|
130
|
+
# Only runs if task was interrupted
|
131
|
+
log_interruption(result.metadata)
|
132
|
+
schedule_retry(result) if result.metadata[:retryable]
|
133
|
+
}
|
134
|
+
.on_executed { |result|
|
135
|
+
# Always runs after execution (complete OR interrupted)
|
136
|
+
update_metrics(result.runtime)
|
137
|
+
cleanup_temporary_files(result.context)
|
138
|
+
}
|
44
139
|
```
|
45
140
|
|
46
141
|
> [!TIP]
|
47
|
-
>
|
142
|
+
> Use state-based callbacks for lifecycle event handling. The `on_executed` callback is particularly useful for cleanup operations that should run regardless of success or failure.
|
143
|
+
|
144
|
+
## State vs Status Distinction
|
145
|
+
|
146
|
+
Understanding the difference between states and statuses is crucial:
|
147
|
+
|
148
|
+
- **State**: Execution lifecycle position (`initialized` → `executing` → `complete`/`interrupted`)
|
149
|
+
- **Status**: Execution outcome (`success`, `skipped`, `failed`)
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
result = ProcessUserOrderTask.call
|
153
|
+
|
154
|
+
# State indicates WHERE in the lifecycle
|
155
|
+
result.state #=> "complete" (finished executing)
|
156
|
+
|
157
|
+
# Status indicates HOW the execution ended
|
158
|
+
result.status #=> "success" (executed successfully)
|
159
|
+
|
160
|
+
# Both can be different for interrupted tasks
|
161
|
+
failed_result = ProcessFailingOrderTask.call
|
162
|
+
failed_result.state #=> "interrupted" (execution stopped)
|
163
|
+
failed_result.status #=> "failed" (outcome was failure)
|
164
|
+
```
|
165
|
+
|
166
|
+
### State-Status Combinations
|
167
|
+
|
168
|
+
| State | Status | Meaning |
|
169
|
+
| ------------- | --------- | ------- |
|
170
|
+
| `initialized` | `success` | Task created, not yet executed |
|
171
|
+
| `executing` | `success` | Task currently running |
|
172
|
+
| `complete` | `success` | Task finished successfully |
|
173
|
+
| `complete` | `skipped` | Task finished by skipping execution |
|
174
|
+
| `interrupted` | `failed` | Task stopped due to failure |
|
175
|
+
| `interrupted` | `skipped` | Task stopped by skip condition |
|
176
|
+
|
177
|
+
> [!NOTE]
|
178
|
+
> State tracks the execution lifecycle (where the task is), while status tracks the outcome (how the task ended). Both provide valuable but different information about task execution.
|
179
|
+
|
180
|
+
## State Inspection and Monitoring
|
181
|
+
|
182
|
+
States provide valuable information for monitoring and debugging:
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
result = ProcessUserOrderTask.call
|
186
|
+
|
187
|
+
# Basic state information
|
188
|
+
puts "Execution state: #{result.state}"
|
189
|
+
puts "Task completed: #{result.executed?}"
|
190
|
+
|
191
|
+
# State in serialized form
|
192
|
+
result.to_h[:state] #=> "complete"
|
193
|
+
|
194
|
+
# Human-readable inspection
|
195
|
+
result.to_s
|
196
|
+
#=> "ProcessUserOrderTask: type=Task index=0 state=complete status=success outcome=success..."
|
197
|
+
```
|
198
|
+
|
199
|
+
## State Persistence and Logging
|
200
|
+
|
201
|
+
States are automatically captured in result serialization:
|
202
|
+
|
203
|
+
```ruby
|
204
|
+
result = ProcessUserOrderTask.call
|
205
|
+
|
206
|
+
# Hash representation includes state
|
207
|
+
result.to_h
|
208
|
+
#=> {
|
209
|
+
# class: "ProcessUserOrderTask",
|
210
|
+
# index: 0,
|
211
|
+
# state: "complete",
|
212
|
+
# status: "success",
|
213
|
+
# outcome: "success",
|
214
|
+
# # ... other attributes
|
215
|
+
# }
|
216
|
+
|
217
|
+
# Chain-level state aggregation
|
218
|
+
result.chain.to_h
|
219
|
+
#=> {
|
220
|
+
# id: "chain-uuid...",
|
221
|
+
# state: "complete", # Derived from first result
|
222
|
+
# status: "success", # Derived from first result
|
223
|
+
# results: [
|
224
|
+
# { state: "complete", status: "success", ... },
|
225
|
+
# # ... other results
|
226
|
+
# ]
|
227
|
+
# }
|
228
|
+
```
|
48
229
|
|
49
230
|
---
|
50
231
|
|
51
|
-
- **Prev:** [Outcomes - Statuses](
|
52
|
-
- **Next:** [
|
232
|
+
- **Prev:** [Outcomes - Statuses](statuses.md)
|
233
|
+
- **Next:** [Parameters - Definitions](../parameters/definitions.md)
|