cmdx 1.1.0 → 1.1.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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/.cursor/prompts/docs.md +9 -0
  3. data/.cursor/prompts/rspec.md +13 -12
  4. data/.cursor/prompts/yardoc.md +11 -6
  5. data/CHANGELOG.md +13 -2
  6. data/README.md +1 -0
  7. data/docs/ai_prompts.md +269 -195
  8. data/docs/basics/call.md +124 -58
  9. data/docs/basics/chain.md +190 -160
  10. data/docs/basics/context.md +242 -154
  11. data/docs/basics/setup.md +302 -32
  12. data/docs/callbacks.md +390 -94
  13. data/docs/configuration.md +181 -65
  14. data/docs/deprecation.md +245 -0
  15. data/docs/getting_started.md +161 -39
  16. data/docs/internationalization.md +590 -70
  17. data/docs/interruptions/exceptions.md +135 -118
  18. data/docs/interruptions/faults.md +150 -125
  19. data/docs/interruptions/halt.md +134 -80
  20. data/docs/logging.md +181 -118
  21. data/docs/middlewares.md +150 -377
  22. data/docs/outcomes/result.md +140 -112
  23. data/docs/outcomes/states.md +134 -99
  24. data/docs/outcomes/statuses.md +204 -146
  25. data/docs/parameters/coercions.md +232 -281
  26. data/docs/parameters/defaults.md +224 -169
  27. data/docs/parameters/definitions.md +289 -141
  28. data/docs/parameters/namespacing.md +250 -161
  29. data/docs/parameters/validations.md +260 -133
  30. data/docs/testing.md +191 -197
  31. data/docs/workflows.md +143 -98
  32. data/lib/cmdx/callback.rb +23 -19
  33. data/lib/cmdx/callback_registry.rb +1 -3
  34. data/lib/cmdx/chain_inspector.rb +23 -23
  35. data/lib/cmdx/chain_serializer.rb +38 -19
  36. data/lib/cmdx/coercion.rb +20 -12
  37. data/lib/cmdx/coercion_registry.rb +51 -32
  38. data/lib/cmdx/configuration.rb +84 -31
  39. data/lib/cmdx/context.rb +32 -21
  40. data/lib/cmdx/core_ext/hash.rb +13 -13
  41. data/lib/cmdx/core_ext/module.rb +1 -1
  42. data/lib/cmdx/core_ext/object.rb +12 -12
  43. data/lib/cmdx/correlator.rb +60 -39
  44. data/lib/cmdx/errors.rb +105 -131
  45. data/lib/cmdx/fault.rb +66 -45
  46. data/lib/cmdx/immutator.rb +20 -21
  47. data/lib/cmdx/lazy_struct.rb +78 -70
  48. data/lib/cmdx/log_formatters/json.rb +1 -1
  49. data/lib/cmdx/log_formatters/key_value.rb +1 -1
  50. data/lib/cmdx/log_formatters/line.rb +1 -1
  51. data/lib/cmdx/log_formatters/logstash.rb +1 -1
  52. data/lib/cmdx/log_formatters/pretty_json.rb +1 -1
  53. data/lib/cmdx/log_formatters/pretty_key_value.rb +1 -1
  54. data/lib/cmdx/log_formatters/pretty_line.rb +1 -1
  55. data/lib/cmdx/log_formatters/raw.rb +2 -2
  56. data/lib/cmdx/logger.rb +19 -14
  57. data/lib/cmdx/logger_ansi.rb +33 -17
  58. data/lib/cmdx/logger_serializer.rb +85 -24
  59. data/lib/cmdx/middleware.rb +39 -21
  60. data/lib/cmdx/middleware_registry.rb +4 -3
  61. data/lib/cmdx/parameter.rb +151 -89
  62. data/lib/cmdx/parameter_inspector.rb +34 -21
  63. data/lib/cmdx/parameter_registry.rb +36 -30
  64. data/lib/cmdx/parameter_serializer.rb +21 -14
  65. data/lib/cmdx/result.rb +136 -135
  66. data/lib/cmdx/result_ansi.rb +31 -17
  67. data/lib/cmdx/result_inspector.rb +32 -27
  68. data/lib/cmdx/result_logger.rb +23 -14
  69. data/lib/cmdx/result_serializer.rb +65 -27
  70. data/lib/cmdx/task.rb +234 -113
  71. data/lib/cmdx/task_deprecator.rb +22 -25
  72. data/lib/cmdx/task_processor.rb +89 -88
  73. data/lib/cmdx/task_serializer.rb +27 -14
  74. data/lib/cmdx/utils/monotonic_runtime.rb +2 -4
  75. data/lib/cmdx/validator.rb +25 -16
  76. data/lib/cmdx/validator_registry.rb +53 -31
  77. data/lib/cmdx/validators/exclusion.rb +1 -1
  78. data/lib/cmdx/validators/format.rb +2 -2
  79. data/lib/cmdx/validators/inclusion.rb +2 -2
  80. data/lib/cmdx/validators/length.rb +2 -2
  81. data/lib/cmdx/validators/numeric.rb +3 -3
  82. data/lib/cmdx/validators/presence.rb +2 -2
  83. data/lib/cmdx/version.rb +1 -1
  84. data/lib/cmdx/workflow.rb +54 -33
  85. data/lib/generators/cmdx/task_generator.rb +6 -6
  86. data/lib/generators/cmdx/workflow_generator.rb +6 -6
  87. metadata +3 -1
@@ -1,9 +1,6 @@
1
1
  # Outcomes - Result
2
2
 
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.
3
+ The result object is the comprehensive return value of task execution, providing complete information about the execution outcome, state, timing, and any data produced during the task lifecycle. Results serve as the primary interface for inspecting task execution outcomes and chaining task operations.
7
4
 
8
5
  ## Table of Contents
9
6
 
@@ -20,228 +17,261 @@ inspecting task execution outcomes and chaining task operations.
20
17
 
21
18
  ## TLDR
22
19
 
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
20
+ ```ruby
21
+ # Basic result inspection
22
+ result = ProcessOrderTask.call(order_id: 123)
23
+ result.success? # true/false
24
+ result.failed? # true/false
25
+ result.runtime # → 0.5 (seconds)
26
+
27
+ # Fluent callbacks
28
+ result
29
+ .on_success { |r| send_notification(r.context) }
30
+ .on_failed { |r| handle_error(r.metadata) }
31
+
32
+ # Failure chain analysis
33
+ if result.failed?
34
+ original = result.caused_failure # Find root cause
35
+ thrower = result.threw_failure # Find failure source
36
+ end
37
+ ```
28
38
 
29
39
  ## Core Result Attributes
30
40
 
41
+ > [!NOTE]
42
+ > Result objects are immutable after task execution completes. All result data reflects the final state of the task execution and cannot be modified.
43
+
31
44
  Every result provides access to essential execution information:
32
45
 
33
46
  ```ruby
34
- result = ProcessUserOrderTask.call(order_id: 123)
47
+ result = ProcessOrderTask.call(order_id: 123)
35
48
 
36
49
  # 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
50
+ result.task # ProcessOrderTask instance
51
+ result.context # CMDx::Context with all task data
52
+ result.chain # CMDx::Chain execution tracking
53
+ result.metadata # Hash with execution metadata
41
54
 
42
55
  # 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)
56
+ result.id # "abc123..." (unique execution ID)
57
+ result.state # "complete"
58
+ result.status # "success"
59
+ result.runtime # 0.5 (execution time in seconds)
47
60
  ```
48
61
 
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
62
  ## State and Status Information
53
63
 
54
64
  Results provide comprehensive methods for checking execution state and status:
55
65
 
56
66
  ```ruby
57
- result = ProcessUserOrderTask.call
67
+ result = ProcessOrderTask.call(order_id: 123)
58
68
 
59
69
  # 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)
70
+ result.complete? # true (successful completion)
71
+ result.interrupted? # → false (no interruption)
72
+ result.executed? # true (execution finished)
65
73
 
66
74
  # Status predicates (execution outcome)
67
- result.success? #=> true (successful execution)
68
- result.skipped? #=> false (not skipped)
69
- result.failed? #=> false (no failure)
75
+ result.success? # true (successful execution)
76
+ result.failed? # false (no failure)
77
+ result.skipped? # false (not skipped)
70
78
 
71
79
  # Outcome categorization
72
- result.good? #=> true (success or skipped)
73
- result.bad? #=> false (skipped or failed)
80
+ result.good? # true (success or skipped)
81
+ result.bad? # false (failed only)
74
82
  ```
75
83
 
76
84
  ## Execution Outcome Analysis
77
85
 
78
- Results provide additional methods for understanding execution outcomes:
86
+ Results provide unified outcome determination:
79
87
 
80
88
  ```ruby
81
- result = ProcessOrderWorkflowTask.call
89
+ result = ProcessOrderTask.call(order_id: 123)
82
90
 
83
- # Outcome determination
84
- result.outcome #=> "success" (combines state and status)
91
+ result.outcome # "success" (combines state and status)
85
92
  ```
86
93
 
87
94
  ## Runtime and Performance
88
95
 
89
- Results capture detailed timing information:
96
+ Results capture detailed timing information for performance analysis:
90
97
 
91
98
  ```ruby
92
- result = ProcessUserOrderTask.call
99
+ result = ProcessOrderTask.call(order_id: 123)
93
100
 
94
101
  # Execution timing
95
- result.runtime #=> 0.5 (total execution time in seconds)
102
+ result.runtime # 0.5 (total execution time in seconds)
103
+
104
+ # Performance monitoring
105
+ result
106
+ .on_executed { |r|
107
+ MetricsService.record_execution_time(r.task.class.name, r.runtime)
108
+ }
96
109
  ```
97
110
 
98
111
  ## Failure Chain Analysis
99
112
 
113
+ > [!IMPORTANT]
114
+ > Failure chain analysis is only available for failed results. Use these methods to trace the root cause of failures in complex task workflows.
115
+
100
116
  For failed results, comprehensive failure analysis is available:
101
117
 
102
118
  ```ruby
103
- result = ProcessOrderWorkflowTask.call
119
+ result = ProcessOrderWorkflowTask.call(order_id: 123)
104
120
 
105
121
  if result.failed?
106
122
  # 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}"
123
+ if original_failure = result.caused_failure
124
+ puts "Root cause: #{original_failure.task.class.name}"
110
125
  puts "Reason: #{original_failure.metadata[:reason]}"
111
126
  end
112
127
 
113
128
  # 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}"
129
+ if throwing_task = result.threw_failure
130
+ puts "Failure source: #{throwing_task.task.class.name}"
117
131
  end
118
132
 
119
133
  # 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
134
+ result.caused_failure? # true if this result was the original cause
135
+ result.threw_failure? # true if this result threw a failure
136
+ result.thrown_failure? # true if this result received a thrown failure
123
137
  end
124
138
  ```
125
139
 
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.
140
+ ### Error Handling Patterns
141
+
142
+ ```ruby
143
+ result = ProcessPaymentTask.call(amount: "invalid")
144
+
145
+ if result.failed?
146
+ case result.metadata[:reason]
147
+ when /validation/i
148
+ handle_validation_error(result)
149
+ when /network/i
150
+ schedule_retry(result)
151
+ else
152
+ escalate_error(result)
153
+ end
154
+ end
155
+ ```
128
156
 
129
157
  ## Index and Position
130
158
 
131
159
  Results track their position within execution chains:
132
160
 
133
161
  ```ruby
134
- result = ProcessUserOrderTask.call
162
+ result = ProcessOrderTask.call(order_id: 123)
135
163
 
136
164
  # Position in execution sequence
137
- result.index #=> 0 (first task in chain)
165
+ result.index # 0 (first task in chain)
138
166
 
139
167
  # Access via chain
140
- result.chain.results[result.index] == result #=> true
168
+ result.chain.results[result.index] == result # true
141
169
  ```
142
170
 
143
171
  ## Result Callbacks and Chaining
144
172
 
173
+ > [!TIP]
174
+ > Use result callbacks for clean, functional-style conditional logic. Callbacks return the result object, enabling method chaining and fluent interfaces.
175
+
145
176
  Results support fluent callback patterns for conditional logic:
146
177
 
147
178
  ```ruby
148
- result = ProcessUserOrderTask.call
179
+ result = ProcessOrderTask.call(order_id: 123)
149
180
 
150
- # State-based callbacks
181
+ # Status-based callbacks
151
182
  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) }
183
+ .on_success { |r| send_confirmation_email(r.context.email) }
184
+ .on_failed { |r| handle_payment_failure(r) }
185
+ .on_skipped { |r| log_skip_reason(r.metadata[:reason]) }
155
186
 
156
- # Status-based callbacks
187
+ # State-based callbacks
157
188
  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) }
189
+ .on_complete { |r| update_order_status("processed") }
190
+ .on_interrupted { |r| cleanup_partial_state(r.context) }
161
191
 
162
192
  # Outcome-based callbacks
163
193
  result
164
- .on_good { |r| celebrate_success }
165
- .on_bad { |r| handle_problem }
194
+ .on_good { |r| increment_success_counter }
195
+ .on_bad { |r| alert_operations_team }
166
196
  ```
167
197
 
168
- ### Callback Chaining Examples
198
+ ### Practical Callback Examples
169
199
 
170
200
  ```ruby
171
- ProcessUserOrderTask
172
- .call(order_id: 123)
201
+ # Order processing pipeline
202
+ ProcessOrderTask
203
+ .call(order_id: params[:order_id])
173
204
  .on_success { |result|
174
- SendOrderNotificationTask.call(result.context)
205
+ # Chain to notification task
206
+ SendOrderConfirmationTask.call(result.context)
175
207
  }
176
208
  .on_failed { |result|
177
- ErrorReporter.notify(result.metadata)
209
+ # Handle specific failure types
210
+ case result.metadata[:error_type]
211
+ when "payment_declined"
212
+ redirect_to payment_retry_path
213
+ when "inventory_unavailable"
214
+ redirect_to out_of_stock_path
215
+ else
216
+ redirect_to error_path
217
+ end
178
218
  }
179
219
  .on_executed { |result|
180
- MetricsService.record_execution_time(result.runtime)
220
+ # Always log performance metrics
221
+ Rails.logger.info "Order processing took #{result.runtime}s"
181
222
  }
182
223
  ```
183
224
 
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
225
  ## Pattern Matching
188
226
 
189
- Results support Ruby's pattern matching (Ruby 3.0+) through array and hash deconstruction:
227
+ > [!NOTE]
228
+ > Pattern matching requires Ruby 3.0+. The `deconstruct` method returns `[state, status]` for array patterns, while `deconstruct_keys` provides hash access to result attributes.
229
+
230
+ Results support Ruby's pattern matching through array and hash deconstruction:
190
231
 
191
232
  ### Array Pattern Matching
192
233
 
193
- Match against state and status in order:
194
-
195
234
  ```ruby
196
- result = ProcessUserOrderTask.call
235
+ result = ProcessOrderTask.call(order_id: 123)
197
236
 
198
237
  case result
199
238
  in ["complete", "success"]
200
- puts "Task completed successfully"
239
+ redirect_to success_page
201
240
  in ["interrupted", "failed"]
202
- puts "Task failed during execution"
241
+ retry_with_backoff(result)
203
242
  in ["complete", "skipped"]
204
- puts "Task was skipped but completed"
243
+ log_skip_and_continue
205
244
  end
206
245
  ```
207
246
 
208
247
  ### Hash Pattern Matching
209
248
 
210
- Match against specific result attributes:
211
-
212
249
  ```ruby
213
- result = ProcessUserOrderTask.call
250
+ result = ProcessOrderTask.call(order_id: 123)
214
251
 
215
252
  case result
216
253
  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"
254
+ celebrate_success
255
+ in { status: "failed", metadata: { retryable: true } }
256
+ schedule_retry(result)
222
257
  in { bad: true, metadata: { reason: String => reason } }
223
- puts "Something went wrong: #{reason}"
258
+ escalate_error("Failed: #{reason}")
224
259
  end
225
260
  ```
226
261
 
227
262
  ### Pattern Matching with Guards
228
263
 
229
- Use guard clauses for conditional matching:
230
-
231
264
  ```ruby
232
265
  case result
233
266
  in { status: "failed", metadata: { attempts: n } } if n < 3
234
- retry_task(result)
267
+ retry_task_with_delay(result, n * 2)
235
268
  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)
269
+ mark_permanently_failed(result)
270
+ in { runtime: time } if time > performance_threshold
271
+ investigate_performance_issue(result)
239
272
  end
240
273
  ```
241
274
 
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
275
  ## Serialization and Inspection
246
276
 
247
277
  Results provide comprehensive serialization and inspection capabilities:
@@ -249,11 +279,11 @@ Results provide comprehensive serialization and inspection capabilities:
249
279
  ### Hash Serialization
250
280
 
251
281
  ```ruby
252
- result = ProcessUserOrderTask.call
282
+ result = ProcessOrderTask.call(order_id: 123)
253
283
 
254
284
  result.to_h
255
- #=> {
256
- # class: "ProcessUserOrderTask",
285
+ # {
286
+ # class: "ProcessOrderTask",
257
287
  # type: "Task",
258
288
  # index: 0,
259
289
  # id: "abc123...",
@@ -270,31 +300,32 @@ result.to_h
270
300
  ### Human-Readable Inspection
271
301
 
272
302
  ```ruby
273
- result = ProcessUserOrderTask.call
303
+ result = ProcessOrderTask.call(order_id: 123)
274
304
 
275
305
  result.to_s
276
- #=> "ProcessUserOrderTask: type=Task index=0 id=abc123... state=complete status=success outcome=success metadata={} runtime=0.5"
306
+ # "ProcessOrderTask: type=Task index=0 id=abc123... state=complete status=success outcome=success metadata={} runtime=0.5"
277
307
  ```
278
308
 
279
309
  ### Failure Chain Serialization
280
310
 
281
- Failed results include failure chain information:
311
+ > [!WARNING]
312
+ > Failed results include complete failure chain information. This data can be substantial in complex workflows - consider filtering when logging or persisting.
282
313
 
283
314
  ```ruby
284
- failed_result = ProcessOrderWorkflowTask.call
315
+ failed_result = ProcessOrderWorkflowTask.call(order_id: 123)
285
316
 
286
317
  failed_result.to_h
287
- #=> {
318
+ # {
288
319
  # # ... standard result data ...
289
320
  # caused_failure: {
290
- # class: "ValidateUserOrderTask",
321
+ # class: "ValidateOrderTask",
291
322
  # index: 1,
292
323
  # id: "xyz789...",
293
324
  # state: "interrupted",
294
325
  # status: "failed"
295
326
  # },
296
327
  # threw_failure: {
297
- # class: "ProcessOrderPaymentTask",
328
+ # class: "ProcessPaymentTask",
298
329
  # index: 2,
299
330
  # id: "uvw123...",
300
331
  # state: "interrupted",
@@ -303,9 +334,6 @@ failed_result.to_h
303
334
  # }
304
335
  ```
305
336
 
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
-
309
337
  ---
310
338
 
311
339
  - **Prev:** [Interruptions - Exceptions](../interruptions/exceptions.md)