cmdx 1.1.2 → 1.5.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.
Files changed (192) hide show
  1. checksums.yaml +4 -4
  2. data/.DS_Store +0 -0
  3. data/.cursor/prompts/docs.md +4 -1
  4. data/.cursor/prompts/llms.md +20 -0
  5. data/.cursor/prompts/rspec.md +4 -1
  6. data/.cursor/prompts/yardoc.md +3 -2
  7. data/.cursor/rules/cursor-instructions.mdc +55 -1
  8. data/.irbrc +6 -0
  9. data/.rubocop.yml +29 -18
  10. data/CHANGELOG.md +11 -132
  11. data/LLM.md +3317 -0
  12. data/README.md +68 -44
  13. data/docs/attributes/coercions.md +162 -0
  14. data/docs/attributes/defaults.md +90 -0
  15. data/docs/attributes/definitions.md +281 -0
  16. data/docs/attributes/naming.md +78 -0
  17. data/docs/attributes/validations.md +309 -0
  18. data/docs/basics/chain.md +56 -249
  19. data/docs/basics/context.md +56 -289
  20. data/docs/basics/execution.md +114 -0
  21. data/docs/basics/setup.md +37 -334
  22. data/docs/callbacks.md +89 -467
  23. data/docs/deprecation.md +91 -174
  24. data/docs/getting_started.md +212 -202
  25. data/docs/internationalization.md +11 -647
  26. data/docs/interruptions/exceptions.md +23 -198
  27. data/docs/interruptions/faults.md +71 -151
  28. data/docs/interruptions/halt.md +109 -186
  29. data/docs/logging.md +44 -256
  30. data/docs/middlewares.md +113 -426
  31. data/docs/outcomes/result.md +81 -228
  32. data/docs/outcomes/states.md +33 -221
  33. data/docs/outcomes/statuses.md +21 -311
  34. data/docs/tips_and_tricks.md +120 -70
  35. data/docs/workflows.md +99 -283
  36. data/lib/cmdx/.DS_Store +0 -0
  37. data/lib/cmdx/attribute.rb +229 -0
  38. data/lib/cmdx/attribute_registry.rb +94 -0
  39. data/lib/cmdx/attribute_value.rb +193 -0
  40. data/lib/cmdx/callback_registry.rb +69 -77
  41. data/lib/cmdx/chain.rb +56 -73
  42. data/lib/cmdx/coercion_registry.rb +52 -68
  43. data/lib/cmdx/coercions/array.rb +19 -18
  44. data/lib/cmdx/coercions/big_decimal.rb +20 -24
  45. data/lib/cmdx/coercions/boolean.rb +26 -25
  46. data/lib/cmdx/coercions/complex.rb +21 -22
  47. data/lib/cmdx/coercions/date.rb +25 -23
  48. data/lib/cmdx/coercions/date_time.rb +24 -25
  49. data/lib/cmdx/coercions/float.rb +25 -22
  50. data/lib/cmdx/coercions/hash.rb +31 -32
  51. data/lib/cmdx/coercions/integer.rb +30 -24
  52. data/lib/cmdx/coercions/rational.rb +29 -24
  53. data/lib/cmdx/coercions/string.rb +19 -22
  54. data/lib/cmdx/coercions/symbol.rb +37 -0
  55. data/lib/cmdx/coercions/time.rb +26 -25
  56. data/lib/cmdx/configuration.rb +49 -108
  57. data/lib/cmdx/context.rb +222 -44
  58. data/lib/cmdx/deprecator.rb +61 -0
  59. data/lib/cmdx/errors.rb +42 -252
  60. data/lib/cmdx/exceptions.rb +39 -0
  61. data/lib/cmdx/faults.rb +78 -39
  62. data/lib/cmdx/freezer.rb +51 -0
  63. data/lib/cmdx/identifier.rb +30 -0
  64. data/lib/cmdx/locale.rb +52 -0
  65. data/lib/cmdx/log_formatters/json.rb +21 -22
  66. data/lib/cmdx/log_formatters/key_value.rb +20 -22
  67. data/lib/cmdx/log_formatters/line.rb +15 -22
  68. data/lib/cmdx/log_formatters/logstash.rb +22 -23
  69. data/lib/cmdx/log_formatters/raw.rb +16 -22
  70. data/lib/cmdx/middleware_registry.rb +70 -74
  71. data/lib/cmdx/middlewares/correlate.rb +90 -54
  72. data/lib/cmdx/middlewares/runtime.rb +58 -0
  73. data/lib/cmdx/middlewares/timeout.rb +48 -68
  74. data/lib/cmdx/railtie.rb +12 -45
  75. data/lib/cmdx/result.rb +229 -314
  76. data/lib/cmdx/task.rb +194 -366
  77. data/lib/cmdx/utils/call.rb +49 -0
  78. data/lib/cmdx/utils/condition.rb +71 -0
  79. data/lib/cmdx/utils/format.rb +61 -0
  80. data/lib/cmdx/validator_registry.rb +63 -72
  81. data/lib/cmdx/validators/exclusion.rb +38 -67
  82. data/lib/cmdx/validators/format.rb +48 -49
  83. data/lib/cmdx/validators/inclusion.rb +43 -74
  84. data/lib/cmdx/validators/length.rb +101 -162
  85. data/lib/cmdx/validators/numeric.rb +95 -170
  86. data/lib/cmdx/validators/presence.rb +37 -50
  87. data/lib/cmdx/version.rb +1 -1
  88. data/lib/cmdx/worker.rb +178 -0
  89. data/lib/cmdx/workflow.rb +85 -81
  90. data/lib/cmdx.rb +19 -13
  91. data/lib/generators/cmdx/install_generator.rb +14 -13
  92. data/lib/generators/cmdx/task_generator.rb +25 -50
  93. data/lib/generators/cmdx/templates/install.rb +11 -46
  94. data/lib/generators/cmdx/templates/task.rb.tt +3 -2
  95. data/lib/locales/en.yml +18 -4
  96. data/src/cmdx-logo.png +0 -0
  97. metadata +32 -116
  98. data/docs/ai_prompts.md +0 -393
  99. data/docs/basics/call.md +0 -317
  100. data/docs/configuration.md +0 -344
  101. data/docs/parameters/coercions.md +0 -396
  102. data/docs/parameters/defaults.md +0 -335
  103. data/docs/parameters/definitions.md +0 -446
  104. data/docs/parameters/namespacing.md +0 -378
  105. data/docs/parameters/validations.md +0 -405
  106. data/docs/testing.md +0 -553
  107. data/lib/cmdx/callback.rb +0 -53
  108. data/lib/cmdx/chain_inspector.rb +0 -56
  109. data/lib/cmdx/chain_serializer.rb +0 -63
  110. data/lib/cmdx/coercion.rb +0 -57
  111. data/lib/cmdx/coercions/virtual.rb +0 -29
  112. data/lib/cmdx/core_ext/hash.rb +0 -83
  113. data/lib/cmdx/core_ext/module.rb +0 -98
  114. data/lib/cmdx/core_ext/object.rb +0 -125
  115. data/lib/cmdx/correlator.rb +0 -122
  116. data/lib/cmdx/error.rb +0 -67
  117. data/lib/cmdx/fault.rb +0 -140
  118. data/lib/cmdx/immutator.rb +0 -52
  119. data/lib/cmdx/lazy_struct.rb +0 -246
  120. data/lib/cmdx/log_formatters/pretty_json.rb +0 -40
  121. data/lib/cmdx/log_formatters/pretty_key_value.rb +0 -38
  122. data/lib/cmdx/log_formatters/pretty_line.rb +0 -41
  123. data/lib/cmdx/logger.rb +0 -49
  124. data/lib/cmdx/logger_ansi.rb +0 -68
  125. data/lib/cmdx/logger_serializer.rb +0 -116
  126. data/lib/cmdx/middleware.rb +0 -70
  127. data/lib/cmdx/parameter.rb +0 -312
  128. data/lib/cmdx/parameter_evaluator.rb +0 -231
  129. data/lib/cmdx/parameter_inspector.rb +0 -66
  130. data/lib/cmdx/parameter_registry.rb +0 -106
  131. data/lib/cmdx/parameter_serializer.rb +0 -59
  132. data/lib/cmdx/result_ansi.rb +0 -71
  133. data/lib/cmdx/result_inspector.rb +0 -71
  134. data/lib/cmdx/result_logger.rb +0 -59
  135. data/lib/cmdx/result_serializer.rb +0 -104
  136. data/lib/cmdx/rspec/matchers.rb +0 -28
  137. data/lib/cmdx/rspec/result_matchers/be_executed.rb +0 -42
  138. data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +0 -94
  139. data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +0 -94
  140. data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +0 -59
  141. data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +0 -57
  142. data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +0 -87
  143. data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +0 -51
  144. data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +0 -58
  145. data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +0 -59
  146. data/lib/cmdx/rspec/result_matchers/have_context.rb +0 -86
  147. data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +0 -54
  148. data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +0 -52
  149. data/lib/cmdx/rspec/result_matchers/have_metadata.rb +0 -114
  150. data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +0 -66
  151. data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +0 -64
  152. data/lib/cmdx/rspec/result_matchers/have_runtime.rb +0 -78
  153. data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +0 -76
  154. data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +0 -62
  155. data/lib/cmdx/rspec/task_matchers/have_callback.rb +0 -85
  156. data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +0 -68
  157. data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +0 -92
  158. data/lib/cmdx/rspec/task_matchers/have_middleware.rb +0 -46
  159. data/lib/cmdx/rspec/task_matchers/have_parameter.rb +0 -181
  160. data/lib/cmdx/task_deprecator.rb +0 -58
  161. data/lib/cmdx/task_processor.rb +0 -246
  162. data/lib/cmdx/task_serializer.rb +0 -57
  163. data/lib/cmdx/utils/ansi_color.rb +0 -73
  164. data/lib/cmdx/utils/log_timestamp.rb +0 -36
  165. data/lib/cmdx/utils/monotonic_runtime.rb +0 -34
  166. data/lib/cmdx/utils/name_affix.rb +0 -52
  167. data/lib/cmdx/validator.rb +0 -57
  168. data/lib/generators/cmdx/templates/workflow.rb.tt +0 -7
  169. data/lib/generators/cmdx/workflow_generator.rb +0 -84
  170. data/lib/locales/ar.yml +0 -35
  171. data/lib/locales/cs.yml +0 -35
  172. data/lib/locales/da.yml +0 -35
  173. data/lib/locales/de.yml +0 -35
  174. data/lib/locales/el.yml +0 -35
  175. data/lib/locales/es.yml +0 -35
  176. data/lib/locales/fi.yml +0 -35
  177. data/lib/locales/fr.yml +0 -35
  178. data/lib/locales/he.yml +0 -35
  179. data/lib/locales/hi.yml +0 -35
  180. data/lib/locales/it.yml +0 -35
  181. data/lib/locales/ja.yml +0 -35
  182. data/lib/locales/ko.yml +0 -35
  183. data/lib/locales/nl.yml +0 -35
  184. data/lib/locales/no.yml +0 -35
  185. data/lib/locales/pl.yml +0 -35
  186. data/lib/locales/pt.yml +0 -35
  187. data/lib/locales/ru.yml +0 -35
  188. data/lib/locales/sv.yml +0 -35
  189. data/lib/locales/th.yml +0 -35
  190. data/lib/locales/tr.yml +0 -35
  191. data/lib/locales/vi.yml +0 -35
  192. data/lib/locales/zh.yml +0 -35
@@ -7,39 +7,12 @@ decision making and monitoring.
7
7
 
8
8
  ## Table of Contents
9
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 Persistence and Logging](#state-persistence-and-logging)
10
+ - [Definitions](#definitions)
11
+ - [Transitions](#transitions)
12
+ - [Predicates](#predicates)
13
+ - [Handlers](#handlers)
17
14
 
18
- ## TLDR
19
-
20
- ```ruby
21
- # Check execution lifecycle
22
- result.initialized? # → false (after execution)
23
- result.executing? # → false (after execution)
24
- result.complete? # → true (successful completion)
25
- result.interrupted? # → false (no interruption)
26
- result.executed? # → true (complete OR interrupted)
27
-
28
- # State-based callbacks
29
- result
30
- .on_complete { |r| send_confirmation_email(r.context) }
31
- .on_interrupted { |r| log_error_and_retry(r) }
32
- .on_executed { |r| cleanup_resources(r) }
33
-
34
- # States: WHERE in lifecycle, Status: HOW it ended
35
- result.state #=> "complete" (finished executing)
36
- result.status #=> "success" (executed successfully)
37
- ```
38
-
39
- ## State Definitions
40
-
41
- > [!IMPORTANT]
42
- > States are automatically managed during task execution and should **never** be modified manually. State transitions are handled internally by the CMDx framework.
15
+ ## Definitions
43
16
 
44
17
  | State | Description |
45
18
  | ----- | ----------- |
@@ -48,221 +21,60 @@ result.status #=> "success" (executed successfully)
48
21
  | `complete` | Task finished execution successfully without any interruption or halt. |
49
22
  | `interrupted` | Task execution was stopped due to a fault, exception, or explicit halt. |
50
23
 
51
- ## State Transitions
52
-
53
- States follow a strict lifecycle with controlled transitions:
54
-
55
- ```ruby
56
- # Valid state transition flow
57
- initialized → executing → complete (successful execution)
58
- initialized → executing → interrupted (failed/halted execution)
59
- ```
60
-
61
- ### Automatic State Management
62
-
63
- ```ruby
64
- class ProcessPaymentTask < CMDx::Task
65
- def call
66
- # State automatically managed:
67
- # 1. initialized → executing (when call begins)
68
- # 2. executing → complete (successful completion)
69
- # 3. executing → interrupted (on failure/halt)
70
-
71
- charge_customer(amount)
72
- send_receipt(email)
73
- end
74
- end
24
+ State-Status combinations:
75
25
 
76
- task = ProcessPaymentTask.new
77
- task.result.state #=> "initialized"
78
-
79
- result = ProcessPaymentTask.call
80
- result.state #=> "complete" (if successful)
81
- ```
26
+ | State | Status | Meaning |
27
+ | ----- | ------ | ------- |
28
+ | `initialized` | `success` | Task created, not yet executed |
29
+ | `executing` | `success` | Task currently running |
30
+ | `complete` | `success` | Task finished successfully |
31
+ | `complete` | `skipped` | Task finished by skipping execution |
32
+ | `interrupted` | `failed` | Task stopped due to failure |
33
+ | `interrupted` | `skipped` | Task stopped by skip condition |
82
34
 
83
- ### Internal State Transition Methods
35
+ ## Transitions
84
36
 
85
- > [!WARNING]
86
- > State transition methods (`executing!`, `complete!`, `interrupt!`) are for internal framework use only. Never call these methods directly in your application code.
37
+ > [!CAUTION]
38
+ > States are automatically managed during task execution and should **never** be modified manually. State transitions are handled internally by the CMDx framework.
87
39
 
88
40
  ```ruby
89
- result = ProcessPaymentTask.new.result
90
-
91
- # Internal state transition methods (DO NOT USE)
92
- result.executing! # initialized → executing
93
- result.complete! # executing → complete
94
- result.interrupt! # executing → interrupted
95
- result.executed! # executing → complete OR interrupted (based on status)
41
+ # Valid state transition flow
42
+ initialized → executing → complete (successful execution)
43
+ initialized executing interrupted (skipped/failed execution)
96
44
  ```
97
45
 
98
- ## State Predicates
46
+ ## Predicates
99
47
 
100
48
  Use state predicates to check the current execution lifecycle:
101
49
 
102
50
  ```ruby
103
- class OrderFulfillmentTask < CMDx::Task
104
- def call
105
- process_order
106
- ship_items
107
- end
108
- end
51
+ result = ProcessVideoUpload.execute
109
52
 
110
- result = OrderFulfillmentTask.call
111
-
112
- # Check current state
53
+ # Individual state checks
113
54
  result.initialized? #=> false (after execution)
114
55
  result.executing? #=> false (after execution)
115
56
  result.complete? #=> true (successful completion)
116
57
  result.interrupted? #=> false (no interruption)
117
58
 
118
- # Combined state checking
59
+ # State categorization
119
60
  result.executed? #=> true (complete OR interrupted)
120
61
  ```
121
62
 
122
- ### State Checking in Conditional Logic
123
-
124
- ```ruby
125
- def handle_task_result(result)
126
- if result.complete?
127
- notify_success(result.context)
128
- elsif result.interrupted?
129
- handle_failure(result.metadata)
130
- end
63
+ ## Handlers
131
64
 
132
- # Always cleanup when execution finished
133
- cleanup_resources if result.executed?
134
- end
135
- ```
136
-
137
- ## State-Based Callbacks
138
-
139
- > [!TIP]
140
- > 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.
65
+ Use state-based handlers for lifecycle event handling. The `on_executed` handler is particularly useful for cleanup operations that should run regardless of success, skipped, or failure.
141
66
 
142
67
  ```ruby
143
- class ProcessOrderTask < CMDx::Task
144
- def call
145
- validate_inventory
146
- charge_payment
147
- update_stock
148
- end
149
- end
150
-
151
- result = ProcessOrderTask.call
68
+ result = ProcessVideoUpload.execute
152
69
 
153
- # Individual state callbacks
70
+ # Individual state handlers
154
71
  result
155
- .on_complete { |r| send_confirmation_email(r.context.customer_email) }
156
- .on_interrupted { |r| log_error(r.metadata) && schedule_retry(r) }
157
- .on_executed { |r| update_analytics(r.runtime) }
158
- ```
159
-
160
- ### Advanced Callback Patterns
161
-
162
- ```ruby
163
- ProcessOrderTask
164
- .call(order_id: 123)
165
- .on_complete { |result|
166
- # Only runs if task completed successfully
167
- OrderMailer.confirmation(result.context.order).deliver_now
168
- Analytics.track("order_processed", order_id: result.context.order_id)
169
- }
170
- .on_interrupted { |result|
171
- # Only runs if task was interrupted
172
- ErrorLogger.log(result.metadata[:error])
173
-
174
- if result.metadata[:retryable]
175
- RetryWorker.perform_later(result.context.order_id)
176
- end
177
- }
178
- .on_executed { |result|
179
- # Always runs after execution (complete OR interrupted)
180
- PerformanceTracker.record(result.runtime)
181
- TempFileCleanup.perform(result.context.temp_files)
182
- }
183
- ```
184
-
185
- ## State vs Status Distinction
186
-
187
- > [!NOTE]
188
- > 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.
189
-
190
- Understanding the difference between states and statuses is crucial:
191
-
192
- - **State**: Execution lifecycle position (`initialized` → `executing` → `complete`/`interrupted`)
193
- - **Status**: Execution outcome (`success`, `skipped`, `failed`)
194
-
195
- ```ruby
196
- class ProcessRefundTask < CMDx::Task
197
- def call
198
- return unless eligible_for_refund?
199
-
200
- process_refund
201
- notify_customer
202
- end
203
- end
204
-
205
- # Successful execution
206
- result = ProcessRefundTask.call
207
- result.state #=> "complete" (finished executing)
208
- result.status #=> "success" (executed successfully)
209
-
210
- # Failed execution
211
- failed_result = ProcessRefundTask.call(invalid_order_id: "xyz")
212
- failed_result.state #=> "interrupted" (execution stopped)
213
- failed_result.status #=> "failed" (outcome was failure)
214
- ```
215
-
216
- ### State-Status Combinations
217
-
218
- | State | Status | Meaning |
219
- | ----- | ------ | ------- |
220
- | `initialized` | `success` | Task created, not yet executed |
221
- | `executing` | `success` | Task currently running |
222
- | `complete` | `success` | Task finished successfully |
223
- | `complete` | `skipped` | Task finished by skipping execution |
224
- | `interrupted` | `failed` | Task stopped due to failure |
225
- | `interrupted` | `skipped` | Task stopped by skip condition |
226
-
227
- ## State Persistence and Logging
228
-
229
- > [!IMPORTANT]
230
- > States are automatically captured in result serialization and logging. All state information persists through the complete task execution lifecycle.
231
-
232
- ```ruby
233
- result = ProcessOrderTask.call
234
-
235
- # Hash representation includes state
236
- result.to_h
237
- #=> {
238
- # class: "ProcessOrderTask",
239
- # index: 0,
240
- # state: "complete",
241
- # status: "success",
242
- # outcome: "success",
243
- # runtime: 0.045,
244
- # metadata: {},
245
- # context: { order_id: 123 }
246
- # }
247
-
248
- # Human-readable inspection
249
- result.to_s
250
- #=> "ProcessOrderTask: type=Task index=0 state=complete status=success outcome=success runtime=0.045s"
251
-
252
- # Chain-level state aggregation
253
- result.chain.to_h
254
- #=> {
255
- # id: "chain-550e8400-e29b-41d4-a716-446655440000",
256
- # state: "complete", # Derived from overall chain state
257
- # status: "success", # Derived from overall chain status
258
- # results: [
259
- # { state: "complete", status: "success", ... },
260
- # { state: "complete", status: "success", ... }
261
- # ]
262
- # }
72
+ .on_complete { |result| send_upload_notification(result) }
73
+ .on_interrupted { |result| cleanup_temp_files(result) }
74
+ .on_executed { |result| log_upload_metrics(result) }
263
75
  ```
264
76
 
265
77
  ---
266
78
 
267
- - **Prev:** [Outcomes - Statuses](statuses.md)
268
- - **Next:** [Parameters - Definitions](../parameters/definitions.md)
79
+ - **Prev:** [Outcomes - Result](result.md)
80
+ - **Next:** [Outcomes - Statuses](statuses.md)
@@ -4,43 +4,12 @@ Statuses represent the business outcome of task execution logic, indicating how
4
4
 
5
5
  ## Table of Contents
6
6
 
7
- - [TLDR](#tldr)
8
- - [Status Definitions](#status-definitions)
9
- - [Status Transitions](#status-transitions)
10
- - [Status Predicates](#status-predicates)
11
- - [Status-Based Callbacks](#status-based-callbacks)
12
- - [Status Metadata](#status-metadata)
13
- - [Outcome-Based Logic](#outcome-based-logic)
14
- - [Status vs State vs Outcome](#status-vs-state-vs-outcome)
15
- - [Status Serialization and Inspection](#status-serialization-and-inspection)
7
+ - [Definitions](#definitions)
8
+ - [Transitions](#transitions)
9
+ - [Predicates](#predicates)
10
+ - [Handlers](#handlers)
16
11
 
17
- ## TLDR
18
-
19
- ```ruby
20
- # Check business outcomes
21
- result.success? # → true (default outcome)
22
- result.skipped? # → false (via skip!)
23
- result.failed? # → false (via fail!)
24
-
25
- # Outcome-based logic
26
- result.good? # → true (success OR skipped)
27
- result.bad? # → false (skipped OR failed)
28
-
29
- # Status-based callbacks
30
- result
31
- .on_success { |r| process_success(r) }
32
- .on_skipped { |r| handle_skip_condition(r) }
33
- .on_failed { |r| handle_business_failure(r) }
34
-
35
- # Statuses: HOW it ended, States: WHERE in lifecycle
36
- result.status #=> "success" (business outcome)
37
- result.state #=> "complete" (execution lifecycle)
38
- ```
39
-
40
- ## Status Definitions
41
-
42
- > [!IMPORTANT]
43
- > Statuses represent business outcomes, not technical execution states. A task can be technically "complete" but have a "failed" status if business logic determined the operation could not succeed.
12
+ ## Definitions
44
13
 
45
14
  | Status | Description |
46
15
  | ------ | ----------- |
@@ -48,13 +17,11 @@ result.state #=> "complete" (execution lifecycle)
48
17
  | `skipped` | Task intentionally stopped execution because conditions weren't met or continuation was unnecessary. |
49
18
  | `failed` | Task stopped execution due to business rule violations, validation errors, or exceptions. |
50
19
 
51
- ## Status Transitions
20
+ ## Transitions
52
21
 
53
- > [!WARNING]
22
+ > [!IMPORTANT]
54
23
  > Status transitions are unidirectional and final. Once a task is marked as skipped or failed, it cannot return to success status. Design your business logic accordingly.
55
24
 
56
- Unlike states, statuses can only transition from success to skipped/failed:
57
-
58
25
  ```ruby
59
26
  # Valid status transitions
60
27
  success → skipped # via skip!
@@ -67,46 +34,12 @@ failed → success # ❌ Cannot transition
67
34
  failed → skipped # ❌ Cannot transition
68
35
  ```
69
36
 
70
- ### Status Transition Examples
71
-
72
- ```ruby
73
- class ProcessOrderTask < CMDx::Task
74
- def call
75
- # Task starts with success status
76
- context.result.success? #=> true
77
-
78
- # Conditional skip
79
- if context.order.already_processed?
80
- skip!(reason: "Order already processed")
81
- # Status is now skipped, execution halts
82
- end
83
-
84
- # Conditional failure
85
- unless context.user.authorized?
86
- fail!(reason: "Insufficient permissions")
87
- # Status is now failed, execution halts
88
- end
89
-
90
- # Continue with business logic
91
- process_order
92
- # Status remains success
93
- end
94
- end
95
- ```
96
-
97
- ## Status Predicates
37
+ ## Predicates
98
38
 
99
39
  Use status predicates to check execution outcomes:
100
40
 
101
41
  ```ruby
102
- class PaymentProcessingTask < CMDx::Task
103
- def call
104
- charge_customer
105
- send_receipt
106
- end
107
- end
108
-
109
- result = PaymentProcessingTask.call
42
+ result = ProcessNotification.execute
110
43
 
111
44
  # Individual status checks
112
45
  result.success? #=> true/false
@@ -118,249 +51,26 @@ result.good? #=> true if success OR skipped
118
51
  result.bad? #=> true if skipped OR failed (not success)
119
52
  ```
120
53
 
121
- ### Status Checking in Business Logic
54
+ ## Handlers
122
55
 
123
- ```ruby
124
- def handle_payment_result(result)
125
- if result.success?
126
- send_confirmation_email(result.context.customer)
127
- elsif result.skipped?
128
- log_skip_reason(result.metadata[:reason])
129
- elsif result.failed?
130
- handle_payment_failure(result.metadata)
131
- end
132
- end
133
- ```
134
-
135
- ## Status-Based Callbacks
136
-
137
- > [!TIP]
138
- > Use status-based callbacks for business logic branching. The `on_good` and `on_bad` callbacks are particularly useful for handling success/skip vs failed outcomes respectively.
56
+ Use status-based handlers for business logic branching. The `on_good` and `on_bad` handlers are particularly useful for handling success/skip vs failed outcomes respectively.
139
57
 
140
58
  ```ruby
141
- class OrderFulfillmentTask < CMDx::Task
142
- def call
143
- validate_inventory
144
- process_payment
145
- schedule_shipping
146
- end
147
- end
148
-
149
- result = OrderFulfillmentTask.call
59
+ result = ProcessNotification.execute
150
60
 
151
- # Individual status callbacks
61
+ # Individual status handlers
152
62
  result
153
- .on_success { |r| schedule_delivery(r.context.order) }
154
- .on_skipped { |r| notify_backorder(r.context.customer) }
155
- .on_failed { |r| refund_payment(r.context.payment_id) }
63
+ .on_success { |result| mark_notification_sent(result) }
64
+ .on_skipped { |result| log_notification_skipped(result) }
65
+ .on_failed { |result| queue_retry_notification(result) }
156
66
 
157
- # Outcome-based callbacks
67
+ # Outcome-based handlers
158
68
  result
159
- .on_good { |r| update_inventory(r.context.items) }
160
- .on_bad { |r| log_negative_outcome(r.metadata) }
161
- ```
162
-
163
- ## Status Metadata
164
-
165
- > [!NOTE]
166
- > Always include rich metadata with skip and fail operations. This information is invaluable for debugging, user feedback, and automated error handling.
167
-
168
- ### Success Metadata
169
-
170
- ```ruby
171
- class ProcessRefundTask < CMDx::Task
172
- def call
173
- refund = create_refund(context.payment_id)
174
- context.refund_id = refund.id
175
- context.processed_at = Time.now
176
- end
177
- end
178
-
179
- result = ProcessRefundTask.call(payment_id: "pay_123")
180
- result.success? #=> true
181
- result.metadata #=> {} (typically empty for success)
182
- ```
183
-
184
- ### Skip Metadata
185
-
186
- ```ruby
187
- class ProcessSubscriptionTask < CMDx::Task
188
- def call
189
- subscription = Subscription.find(context.subscription_id)
190
-
191
- if subscription.cancelled?
192
- skip!(
193
- reason: "Subscription already cancelled",
194
- cancelled_at: subscription.cancelled_at,
195
- skip_code: "ALREADY_CANCELLED"
196
- )
197
- end
198
-
199
- process_subscription(subscription)
200
- end
201
- end
202
-
203
- result = ProcessSubscriptionTask.call(subscription_id: 123)
204
- if result.skipped?
205
- result.metadata[:reason] #=> "Subscription already cancelled"
206
- result.metadata[:cancelled_at] #=> 2023-10-01 10:30:00 UTC
207
- result.metadata[:skip_code] #=> "ALREADY_CANCELLED"
208
- end
209
- ```
210
-
211
- ### Failure Metadata
212
-
213
- ```ruby
214
- class ValidateUserDataTask < CMDx::Task
215
- def call
216
- user = User.find(context.user_id)
217
-
218
- unless user.valid?
219
- fail!(
220
- reason: "User validation failed",
221
- errors: user.errors.full_messages,
222
- error_code: "VALIDATION_FAILED",
223
- retryable: false
224
- )
225
- end
226
-
227
- context.validated_user = user
228
- end
229
- end
230
-
231
- result = ValidateUserDataTask.call(user_id: 123)
232
- if result.failed?
233
- result.metadata[:reason] #=> "User validation failed"
234
- result.metadata[:errors] #=> ["Email is invalid", "Name can't be blank"]
235
- result.metadata[:error_code] #=> "VALIDATION_FAILED"
236
- result.metadata[:retryable] #=> false
237
- end
238
- ```
239
-
240
- ## Outcome-Based Logic
241
-
242
- Statuses enable sophisticated outcome-based decision making:
243
-
244
- ### Good vs Bad Outcomes
245
-
246
- ```ruby
247
- class EmailDeliveryTask < CMDx::Task
248
- def call
249
- # Business logic here
250
- send_email
251
- end
252
- end
253
-
254
- result = EmailDeliveryTask.call
255
-
256
- # Good outcomes (success OR skipped)
257
- if result.good?
258
- # Both success and skipped are "good" outcomes
259
- update_user_interface(result)
260
- track_completion_metrics(result)
261
- end
262
-
263
- # Bad outcomes (skipped OR failed, excluding success)
264
- if result.bad?
265
- # Handle any non-success outcome
266
- show_error_message(result.metadata[:reason])
267
- track_failure_metrics(result)
268
- end
269
- ```
270
-
271
- ### Conditional Processing
272
-
273
- ```ruby
274
- def process_batch_results(results)
275
- successful_count = results.count(&:success?)
276
- skipped_count = results.count(&:skipped?)
277
- failed_count = results.count(&:failed?)
278
-
279
- if results.all?(&:good?)
280
- mark_batch_complete
281
- elsif results.any?(&:failed?)
282
- schedule_batch_retry(results.select(&:failed?))
283
- end
284
- end
285
- ```
286
-
287
- ## Status vs State vs Outcome
288
-
289
- > [!NOTE]
290
- > Status tracks the business outcome (how the task ended), while state tracks the execution lifecycle (where the task is). Both provide valuable but different information about task execution.
291
-
292
- Understanding the relationship between these concepts:
293
-
294
- - **Status**: Business execution outcome (`success`, `skipped`, `failed`)
295
- - **State**: Technical execution lifecycle (`initialized`, `executing`, `complete`, `interrupted`)
296
- - **Outcome**: Combined representation for unified logic
297
-
298
- ```ruby
299
- class DataImportTask < CMDx::Task
300
- def call
301
- import_data
302
- validate_data
303
- end
304
- end
305
-
306
- result = DataImportTask.call
307
-
308
- # Successful execution
309
- result.state #=> "complete" (execution finished)
310
- result.status #=> "success" (business outcome)
311
- result.outcome #=> "success" (same as status when complete)
312
-
313
- # Skipped execution
314
- skipped_result = DataImportTask.call(skip_import: true)
315
- skipped_result.state #=> "complete" (execution finished)
316
- skipped_result.status #=> "skipped" (business outcome)
317
- skipped_result.outcome #=> "skipped" (same as status)
318
-
319
- # Failed execution
320
- failed_result = DataImportTask.call(invalid_data: true)
321
- failed_result.state #=> "interrupted" (execution stopped)
322
- failed_result.status #=> "failed" (business outcome)
323
- failed_result.outcome #=> "interrupted" (reflects state for interrupted tasks)
324
- ```
325
-
326
- ## Status Serialization and Inspection
327
-
328
- > [!IMPORTANT]
329
- > Statuses are automatically captured in result serialization and logging. All status information persists through the complete task execution lifecycle.
330
-
331
- ```ruby
332
- result = ProcessOrderTask.call
333
-
334
- # Hash representation includes status
335
- result.to_h
336
- #=> {
337
- # class: "ProcessOrderTask",
338
- # index: 0,
339
- # state: "complete",
340
- # status: "success",
341
- # outcome: "success",
342
- # runtime: 0.045,
343
- # metadata: {},
344
- # context: { order_id: 123 }
345
- # }
346
-
347
- # Human-readable inspection
348
- result.to_s
349
- #=> "ProcessOrderTask: type=Task index=0 state=complete status=success outcome=success runtime=0.045s"
350
-
351
- # Chain-level status aggregation
352
- result.chain.to_h
353
- #=> {
354
- # id: "chain-550e8400-e29b-41d4-a716-446655440000",
355
- # state: "complete",
356
- # status: "success",
357
- # results: [
358
- # { state: "complete", status: "success", ... }
359
- # ]
360
- # }
69
+ .on_good { |result| update_message_stats(result) }
70
+ .on_bad { |result| track_delivery_failure(result) }
361
71
  ```
362
72
 
363
73
  ---
364
74
 
365
- - **Prev:** [Outcomes - Result](result.md)
366
- - **Next:** [Outcomes - States](states.md)
75
+ - **Prev:** [Outcomes - States](states.md)
76
+ - **Next:** [Attributes - Definitions](../attributes/definitions.md)