cmdx 1.1.1 → 1.5.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.
Files changed (193) 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 +56 -1
  8. data/.irbrc +6 -0
  9. data/.rubocop.yml +29 -18
  10. data/.ruby-version +1 -1
  11. data/CHANGELOG.md +6 -128
  12. data/LLM.md +3317 -0
  13. data/README.md +68 -44
  14. data/docs/attributes/coercions.md +162 -0
  15. data/docs/attributes/defaults.md +90 -0
  16. data/docs/attributes/definitions.md +281 -0
  17. data/docs/attributes/naming.md +78 -0
  18. data/docs/attributes/validations.md +309 -0
  19. data/docs/basics/chain.md +56 -249
  20. data/docs/basics/context.md +56 -289
  21. data/docs/basics/execution.md +114 -0
  22. data/docs/basics/setup.md +37 -334
  23. data/docs/callbacks.md +89 -467
  24. data/docs/deprecation.md +91 -174
  25. data/docs/getting_started.md +212 -202
  26. data/docs/internationalization.md +11 -647
  27. data/docs/interruptions/exceptions.md +23 -198
  28. data/docs/interruptions/faults.md +71 -151
  29. data/docs/interruptions/halt.md +109 -186
  30. data/docs/logging.md +44 -256
  31. data/docs/middlewares.md +113 -426
  32. data/docs/outcomes/result.md +81 -228
  33. data/docs/outcomes/states.md +33 -221
  34. data/docs/outcomes/statuses.md +21 -311
  35. data/docs/tips_and_tricks.md +120 -70
  36. data/docs/workflows.md +99 -283
  37. data/lib/cmdx/.DS_Store +0 -0
  38. data/lib/cmdx/attribute.rb +229 -0
  39. data/lib/cmdx/attribute_registry.rb +94 -0
  40. data/lib/cmdx/attribute_value.rb +193 -0
  41. data/lib/cmdx/callback_registry.rb +69 -77
  42. data/lib/cmdx/chain.rb +56 -73
  43. data/lib/cmdx/coercion_registry.rb +52 -68
  44. data/lib/cmdx/coercions/array.rb +19 -18
  45. data/lib/cmdx/coercions/big_decimal.rb +20 -24
  46. data/lib/cmdx/coercions/boolean.rb +26 -25
  47. data/lib/cmdx/coercions/complex.rb +21 -22
  48. data/lib/cmdx/coercions/date.rb +25 -23
  49. data/lib/cmdx/coercions/date_time.rb +24 -25
  50. data/lib/cmdx/coercions/float.rb +25 -22
  51. data/lib/cmdx/coercions/hash.rb +31 -32
  52. data/lib/cmdx/coercions/integer.rb +30 -24
  53. data/lib/cmdx/coercions/rational.rb +29 -24
  54. data/lib/cmdx/coercions/string.rb +19 -22
  55. data/lib/cmdx/coercions/symbol.rb +37 -0
  56. data/lib/cmdx/coercions/time.rb +26 -25
  57. data/lib/cmdx/configuration.rb +49 -108
  58. data/lib/cmdx/context.rb +222 -44
  59. data/lib/cmdx/deprecator.rb +61 -0
  60. data/lib/cmdx/errors.rb +42 -252
  61. data/lib/cmdx/exceptions.rb +39 -0
  62. data/lib/cmdx/faults.rb +78 -39
  63. data/lib/cmdx/freezer.rb +51 -0
  64. data/lib/cmdx/identifier.rb +30 -0
  65. data/lib/cmdx/locale.rb +52 -0
  66. data/lib/cmdx/log_formatters/json.rb +21 -22
  67. data/lib/cmdx/log_formatters/key_value.rb +20 -22
  68. data/lib/cmdx/log_formatters/line.rb +15 -22
  69. data/lib/cmdx/log_formatters/logstash.rb +22 -23
  70. data/lib/cmdx/log_formatters/raw.rb +16 -22
  71. data/lib/cmdx/middleware_registry.rb +70 -74
  72. data/lib/cmdx/middlewares/correlate.rb +90 -54
  73. data/lib/cmdx/middlewares/runtime.rb +58 -0
  74. data/lib/cmdx/middlewares/timeout.rb +48 -68
  75. data/lib/cmdx/railtie.rb +12 -45
  76. data/lib/cmdx/result.rb +229 -314
  77. data/lib/cmdx/task.rb +194 -366
  78. data/lib/cmdx/utils/call.rb +49 -0
  79. data/lib/cmdx/utils/condition.rb +71 -0
  80. data/lib/cmdx/utils/format.rb +61 -0
  81. data/lib/cmdx/validator_registry.rb +63 -72
  82. data/lib/cmdx/validators/exclusion.rb +38 -67
  83. data/lib/cmdx/validators/format.rb +48 -49
  84. data/lib/cmdx/validators/inclusion.rb +43 -74
  85. data/lib/cmdx/validators/length.rb +91 -154
  86. data/lib/cmdx/validators/numeric.rb +87 -162
  87. data/lib/cmdx/validators/presence.rb +37 -50
  88. data/lib/cmdx/version.rb +1 -1
  89. data/lib/cmdx/worker.rb +178 -0
  90. data/lib/cmdx/workflow.rb +85 -81
  91. data/lib/cmdx.rb +19 -13
  92. data/lib/generators/cmdx/install_generator.rb +14 -13
  93. data/lib/generators/cmdx/task_generator.rb +25 -50
  94. data/lib/generators/cmdx/templates/install.rb +11 -46
  95. data/lib/generators/cmdx/templates/task.rb.tt +3 -2
  96. data/lib/locales/en.yml +18 -4
  97. data/src/cmdx-logo.png +0 -0
  98. metadata +32 -116
  99. data/docs/ai_prompts.md +0 -393
  100. data/docs/basics/call.md +0 -317
  101. data/docs/configuration.md +0 -344
  102. data/docs/parameters/coercions.md +0 -396
  103. data/docs/parameters/defaults.md +0 -335
  104. data/docs/parameters/definitions.md +0 -446
  105. data/docs/parameters/namespacing.md +0 -378
  106. data/docs/parameters/validations.md +0 -405
  107. data/docs/testing.md +0 -553
  108. data/lib/cmdx/callback.rb +0 -53
  109. data/lib/cmdx/chain_inspector.rb +0 -56
  110. data/lib/cmdx/chain_serializer.rb +0 -63
  111. data/lib/cmdx/coercion.rb +0 -57
  112. data/lib/cmdx/coercions/virtual.rb +0 -29
  113. data/lib/cmdx/core_ext/hash.rb +0 -83
  114. data/lib/cmdx/core_ext/module.rb +0 -98
  115. data/lib/cmdx/core_ext/object.rb +0 -125
  116. data/lib/cmdx/correlator.rb +0 -122
  117. data/lib/cmdx/error.rb +0 -60
  118. data/lib/cmdx/fault.rb +0 -140
  119. data/lib/cmdx/immutator.rb +0 -52
  120. data/lib/cmdx/lazy_struct.rb +0 -246
  121. data/lib/cmdx/log_formatters/pretty_json.rb +0 -40
  122. data/lib/cmdx/log_formatters/pretty_key_value.rb +0 -38
  123. data/lib/cmdx/log_formatters/pretty_line.rb +0 -41
  124. data/lib/cmdx/logger.rb +0 -49
  125. data/lib/cmdx/logger_ansi.rb +0 -68
  126. data/lib/cmdx/logger_serializer.rb +0 -116
  127. data/lib/cmdx/middleware.rb +0 -70
  128. data/lib/cmdx/parameter.rb +0 -312
  129. data/lib/cmdx/parameter_evaluator.rb +0 -231
  130. data/lib/cmdx/parameter_inspector.rb +0 -66
  131. data/lib/cmdx/parameter_registry.rb +0 -106
  132. data/lib/cmdx/parameter_serializer.rb +0 -59
  133. data/lib/cmdx/result_ansi.rb +0 -71
  134. data/lib/cmdx/result_inspector.rb +0 -71
  135. data/lib/cmdx/result_logger.rb +0 -59
  136. data/lib/cmdx/result_serializer.rb +0 -104
  137. data/lib/cmdx/rspec/matchers.rb +0 -28
  138. data/lib/cmdx/rspec/result_matchers/be_executed.rb +0 -42
  139. data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +0 -94
  140. data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +0 -94
  141. data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +0 -59
  142. data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +0 -57
  143. data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +0 -87
  144. data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +0 -51
  145. data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +0 -58
  146. data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +0 -59
  147. data/lib/cmdx/rspec/result_matchers/have_context.rb +0 -86
  148. data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +0 -54
  149. data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +0 -52
  150. data/lib/cmdx/rspec/result_matchers/have_metadata.rb +0 -114
  151. data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +0 -66
  152. data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +0 -64
  153. data/lib/cmdx/rspec/result_matchers/have_runtime.rb +0 -78
  154. data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +0 -76
  155. data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +0 -62
  156. data/lib/cmdx/rspec/task_matchers/have_callback.rb +0 -85
  157. data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +0 -68
  158. data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +0 -92
  159. data/lib/cmdx/rspec/task_matchers/have_middleware.rb +0 -46
  160. data/lib/cmdx/rspec/task_matchers/have_parameter.rb +0 -181
  161. data/lib/cmdx/task_deprecator.rb +0 -52
  162. data/lib/cmdx/task_processor.rb +0 -246
  163. data/lib/cmdx/task_serializer.rb +0 -57
  164. data/lib/cmdx/utils/ansi_color.rb +0 -73
  165. data/lib/cmdx/utils/log_timestamp.rb +0 -36
  166. data/lib/cmdx/utils/monotonic_runtime.rb +0 -34
  167. data/lib/cmdx/utils/name_affix.rb +0 -52
  168. data/lib/cmdx/validator.rb +0 -57
  169. data/lib/generators/cmdx/templates/workflow.rb.tt +0 -7
  170. data/lib/generators/cmdx/workflow_generator.rb +0 -84
  171. data/lib/locales/ar.yml +0 -35
  172. data/lib/locales/cs.yml +0 -35
  173. data/lib/locales/da.yml +0 -35
  174. data/lib/locales/de.yml +0 -35
  175. data/lib/locales/el.yml +0 -35
  176. data/lib/locales/es.yml +0 -35
  177. data/lib/locales/fi.yml +0 -35
  178. data/lib/locales/fr.yml +0 -35
  179. data/lib/locales/he.yml +0 -35
  180. data/lib/locales/hi.yml +0 -35
  181. data/lib/locales/it.yml +0 -35
  182. data/lib/locales/ja.yml +0 -35
  183. data/lib/locales/ko.yml +0 -35
  184. data/lib/locales/nl.yml +0 -35
  185. data/lib/locales/no.yml +0 -35
  186. data/lib/locales/pl.yml +0 -35
  187. data/lib/locales/pt.yml +0 -35
  188. data/lib/locales/ru.yml +0 -35
  189. data/lib/locales/sv.yml +0 -35
  190. data/lib/locales/th.yml +0 -35
  191. data/lib/locales/tr.yml +0 -35
  192. data/lib/locales/vi.yml +0 -35
  193. data/lib/locales/zh.yml +0 -35
@@ -1,232 +1,57 @@
1
1
  # Interruptions - Exceptions
2
2
 
3
- CMDx provides robust exception handling that differs between the `call` and `call!` methods. Understanding how unhandled exceptions are processed is crucial for building reliable task execution flows and implementing proper error handling strategies.
3
+ CMDx provides robust exception handling that differs between the `execute` and `execute!` methods. Understanding how unhandled exceptions are processed is crucial for building reliable task execution flows and implementing proper error handling strategies.
4
4
 
5
5
  ## Table of Contents
6
6
 
7
- - [TLDR](#tldr)
8
- - [Exception Handling Methods](#exception-handling-methods)
9
- - [Exception Metadata](#exception-metadata)
10
- - [Bang Call Behavior](#bang-call-behavior)
11
- - [Exception Classification](#exception-classification)
12
- - [Error Handling Patterns](#error-handling-patterns)
7
+ - [Exception Handling](#exception-handling)
8
+ - [Non-bang execution](#non-bang-execution)
9
+ - [Bang execution](#bang-execution)
13
10
 
14
- ## TLDR
15
-
16
- ```ruby
17
- # Non-bang call - captures ALL exceptions
18
- result = ProcessOrderTask.call # Never raises, always returns result
19
- result.failed? # true if exception occurred
20
- result.metadata[:original_exception] # Access original exception
21
-
22
- # Bang call - lets exceptions propagate
23
- ProcessOrderTask.call! # Raises exceptions (except configured faults)
24
-
25
- # Exception info always available in metadata
26
- result.metadata[:reason] # Human-readable error message
27
- result.metadata[:original_exception] # Original exception object
28
- ```
29
-
30
- ## Exception Handling Methods
11
+ ## Exception Handling
31
12
 
32
13
  > [!IMPORTANT]
33
- > The key difference: `call` guarantees a result object, while `call!` allows exceptions to propagate for standard error handling patterns.
34
-
35
- ### Non-bang Call (`call`)
14
+ > When designing tasks try not to `raise` your own exceptions directly, instead use `skip!` or `fail!` to signal intent clearly.
36
15
 
37
- The `call` method captures **all** unhandled exceptions and converts them to failed results, ensuring predictable behavior and consistent result processing.
16
+ ### Non-bang execution
38
17
 
39
- | Behavior | Description |
40
- |----------|-------------|
41
- | **Exception Capture** | All exceptions caught and converted |
42
- | **Return Value** | Always returns a result object |
43
- | **State** | `"interrupted"` for exception failures |
44
- | **Status** | `"failed"` for all captured exceptions |
45
- | **Metadata** | Exception details preserved |
18
+ The `execute` method captures **all** unhandled exceptions and converts them to failed results, ensuring predictable behavior and consistent result processing.
46
19
 
47
20
  ```ruby
48
- class ProcessPaymentTask < CMDx::Task
49
- def call
50
- raise ActiveRecord::RecordNotFound, "Payment method not found"
21
+ class CompressDocument < CMDx::Task
22
+ def work
23
+ document = Document.find(context.document_id)
24
+ document.compress!
51
25
  end
52
26
  end
53
27
 
54
- result = ProcessPaymentTask.call
28
+ result = CompressDocument.execute(document_id: "unknown-doc-id")
55
29
  result.state #=> "interrupted"
56
30
  result.status #=> "failed"
57
31
  result.failed? #=> true
32
+ result.reason #=> "[ActiveRecord::NotFoundError] record not found"
33
+ result.cause #=> <ActiveRecord::NotFoundError>
58
34
  ```
59
35
 
60
- ### Bang Call (`call!`)
36
+ ### Bang execution
61
37
 
62
- The `call!` method allows unhandled exceptions to propagate, enabling standard Ruby exception handling while respecting CMDx fault configuration.
38
+ The `execute!` method allows unhandled exceptions to propagate, enabling standard Ruby exception handling while respecting CMDx fault configuration.
63
39
 
64
40
  ```ruby
65
- class ProcessPaymentTask < CMDx::Task
66
- def call
67
- raise StandardError, "Payment gateway unavailable"
41
+ class CompressDocument < CMDx::Task
42
+ def work
43
+ document = Document.find(context.document_id)
44
+ document.compress!
68
45
  end
69
46
  end
70
47
 
71
48
  begin
72
- ProcessPaymentTask.call!
73
- rescue StandardError => e
49
+ CompressDocument.execute!(document_id: "unknown-doc-id")
50
+ rescue ActiveRecord::NotFoundError => e
74
51
  puts "Handle exception: #{e.message}"
75
52
  end
76
53
  ```
77
54
 
78
- ## Exception Metadata
79
-
80
- > [!NOTE]
81
- > Exception information is preserved in result metadata, providing full debugging context while maintaining clean result interfaces.
82
-
83
- ### Metadata Structure
84
-
85
- ```ruby
86
- result = ProcessPaymentTask.call
87
-
88
- # Exception metadata always includes:
89
- result.metadata[:reason] #=> "[StandardError] Payment gateway unavailable"
90
- result.metadata[:original_exception] #=> <StandardError instance>
91
-
92
- # Access original exception properties
93
- exception = result.metadata[:original_exception]
94
- exception.class #=> StandardError
95
- exception.message #=> "Payment gateway unavailable"
96
- exception.backtrace #=> ["lib/tasks/payment.rb:15:in `call'", ...]
97
- ```
98
-
99
- ### Exception Type Checking
100
-
101
- ```ruby
102
- class DatabaseTask < CMDx::Task
103
- def call
104
- raise ActiveRecord::ConnectionNotEstablished, "Database unavailable"
105
- end
106
- end
107
-
108
- result = DatabaseTask.call
109
-
110
- if result.failed? && result.metadata[:original_exception]
111
- case result.metadata[:original_exception]
112
- when ActiveRecord::ConnectionNotEstablished
113
- retry_with_fallback_database
114
- when Net::TimeoutError
115
- retry_with_increased_timeout
116
- when StandardError
117
- log_and_alert_administrators
118
- end
119
- end
120
- ```
121
-
122
- ## Bang Call Behavior
123
-
124
- > [!WARNING]
125
- > `call!` propagates exceptions immediately, bypassing result object creation. Only use when you need direct exception handling or integration with exception-based error handling systems.
126
-
127
- ### Fault vs Exception Handling
128
-
129
- CMDx faults receive special treatment based on `task_halt` configuration:
130
-
131
- ```ruby
132
- class ProcessOrderTask < CMDx::Task
133
- cmd_settings!(task_halt: [CMDx::Result::FAILED])
134
-
135
- def call
136
- if context.payment_invalid
137
- fail!(reason: "Invalid payment method") # CMDx fault
138
- else
139
- raise StandardError, "System error" # Regular exception
140
- end
141
- end
142
- end
143
-
144
- # Fault behavior (converted to exception due to task_halt)
145
- begin
146
- ProcessOrderTask.call!(payment_invalid: true)
147
- rescue CMDx::Failed => e
148
- puts "Controlled fault: #{e.message}"
149
- end
150
-
151
- # Exception behavior (propagates normally)
152
- begin
153
- ProcessOrderTask.call!(payment_invalid: false)
154
- rescue StandardError => e
155
- puts "System exception: #{e.message}"
156
- end
157
- ```
158
-
159
- ## Exception Classification
160
-
161
- ### Protected Exceptions
162
-
163
- > [!IMPORTANT]
164
- > CMDx framework exceptions always propagate regardless of call method, ensuring framework integrity and proper error reporting.
165
-
166
- Certain exceptions are never converted to failed results:
167
-
168
- ```ruby
169
- class InvalidTask < CMDx::Task
170
- # Intentionally not implementing call method
171
- end
172
-
173
- # Framework exceptions always propagate
174
- begin
175
- InvalidTask.call # Even non-bang call propagates framework exceptions
176
- rescue CMDx::UndefinedCallError => e
177
- puts "Framework exception: #{e.message}"
178
- end
179
- ```
180
-
181
- ### Exception Hierarchy
182
-
183
- | Exception Type | `call` Behavior | `call!` Behavior |
184
- |----------------|-----------------|------------------|
185
- | **CMDx Framework** | Propagates | Propagates |
186
- | **CMDx Faults** | Converts to result | Respects `task_halt` config |
187
- | **Standard Exceptions** | Converts to result | Propagates |
188
- | **Custom Exceptions** | Converts to result | Propagates |
189
-
190
- ## Error Handling Patterns
191
-
192
- ### Graceful Degradation
193
-
194
- ```ruby
195
- class ProcessUserDataTask < CMDx::Task
196
- def call
197
- user_data = fetch_user_data
198
- process_data(user_data)
199
- end
200
-
201
- private
202
-
203
- def fetch_user_data
204
- # May raise various exceptions
205
- external_api.get_user_data(context.user_id)
206
- end
207
- end
208
-
209
- # Handle with graceful degradation
210
- result = ProcessUserDataTask.call(user_id: 12345)
211
-
212
- if result.failed?
213
- case result.metadata[:original_exception]
214
- when Net::TimeoutError
215
- # Retry with cached data
216
- fallback_processor.process_cached_data(user_id)
217
- when JSON::ParserError
218
- # Handle malformed response
219
- error_reporter.log_api_format_error
220
- else
221
- # Generic error handling
222
- notify_administrators(result.metadata[:reason])
223
- end
224
- end
225
- ```
226
-
227
- > [!TIP]
228
- > Use `call` for workflow processing where you need guaranteed result objects, and `call!` for direct integration with existing exception-based error handling patterns.
229
-
230
55
  ---
231
56
 
232
57
  - **Prev:** [Interruptions - Faults](faults.md)
@@ -1,113 +1,72 @@
1
1
  # Interruptions - Faults
2
2
 
3
- Faults are exception mechanisms that halt task execution via `skip!` and `fail!` methods. When tasks execute with the `call!` method, fault exceptions matching the task's interruption status are raised, enabling sophisticated exception handling and control flow patterns.
3
+ Faults are exception mechanisms that halt task execution via `skip!` and `fail!` methods. When tasks execute with the `execute!` method, fault exceptions matching the task's interruption status are raised, enabling sophisticated exception handling and control flow patterns.
4
4
 
5
5
  ## Table of Contents
6
6
 
7
- - [TLDR](#tldr)
8
7
  - [Fault Types](#fault-types)
9
- - [Exception Handling](#exception-handling)
10
- - [Fault Context Access](#fault-context-access)
8
+ - [Fault Handling](#fault-handling)
9
+ - [Data Access](#data-access)
11
10
  - [Advanced Matching](#advanced-matching)
11
+ - [Task-Specific Matching](#task-specific-matching)
12
+ - [Custom Logic Matching](#custom-logic-matching)
12
13
  - [Fault Propagation](#fault-propagation)
14
+ - [Basic Propagation](#basic-propagation)
15
+ - [Additional Metadata](#additional-metadata)
13
16
  - [Chain Analysis](#chain-analysis)
14
- - [Configuration](#configuration)
15
-
16
- ## TLDR
17
-
18
- ```ruby
19
- # Basic exception handling
20
- begin
21
- PaymentProcessor.call!(amount: 100)
22
- rescue CMDx::Skipped => e
23
- handle_skipped_payment(e.result.metadata[:reason])
24
- rescue CMDx::Failed => e
25
- handle_failed_payment(e.result.metadata[:error])
26
- rescue CMDx::Fault => e
27
- handle_any_interruption(e)
28
- end
29
-
30
- # Advanced matching
31
- rescue CMDx::Failed.for?(PaymentProcessor, CardValidator) => e
32
- rescue CMDx::Fault.matches? { |f| f.context.amount > 1000 } => e
33
-
34
- # Fault propagation
35
- throw!(validation_result) if validation_result.failed?
36
- ```
37
17
 
38
18
  ## Fault Types
39
19
 
40
20
  | Type | Triggered By | Use Case |
41
21
  |------|--------------|----------|
42
- | `CMDx::Skipped` | `skip!` method | Optional processing, early returns |
43
- | `CMDx::Failed` | `fail!` method | Validation errors, processing failures |
44
22
  | `CMDx::Fault` | Base class | Catch-all for any interruption |
23
+ | `CMDx::SkipFault` | `skip!` method | Optional processing, early returns |
24
+ | `CMDx::FailFault` | `fail!` method | Validation errors, processing failures |
45
25
 
46
- > [!NOTE]
26
+ > [!IMPORTANT]
47
27
  > All fault exceptions inherit from `CMDx::Fault` and provide access to the complete task execution context including result, task, context, and chain information.
48
28
 
49
- ## Exception Handling
50
-
51
- ### Basic Rescue Patterns
29
+ ## Fault Handling
52
30
 
53
31
  ```ruby
54
32
  begin
55
- ProcessOrderTask.call!(order_id: 123)
56
- rescue CMDx::Skipped => e
57
- logger.info "Order processing skipped: #{e.message}"
58
- schedule_retry(e.context.order_id)
59
- rescue CMDx::Failed => e
60
- logger.error "Order processing failed: #{e.message}"
61
- notify_customer(e.context.customer_email, e.result.metadata[:error])
33
+ ProcessTicket.execute!(ticket_id: 456)
34
+ rescue CMDx::SkipFault => e
35
+ logger.info "Ticket processing skipped: #{e.message}"
36
+ schedule_retry(e.context.ticket_id)
37
+ rescue CMDx::FailFault => e
38
+ logger.error "Ticket processing failed: #{e.message}"
39
+ notify_admin(e.context.assigned_agent, e.result.metadata[:error_code])
62
40
  rescue CMDx::Fault => e
63
- logger.warn "Order processing interrupted: #{e.message}"
64
- rollback_transaction
65
- end
66
- ```
67
-
68
- ### Error-Specific Handling
69
-
70
- ```ruby
71
- begin
72
- PaymentProcessor.call!(card_token: token, amount: amount)
73
- rescue CMDx::Failed => e
74
- case e.result.metadata[:error_code]
75
- when "INSUFFICIENT_FUNDS"
76
- suggest_different_payment_method
77
- when "CARD_DECLINED"
78
- request_card_verification
79
- when "NETWORK_ERROR"
80
- retry_payment_later
81
- else
82
- escalate_to_support(e)
83
- end
41
+ logger.warn "Ticket processing interrupted: #{e.message}"
42
+ rollback_changes
84
43
  end
85
44
  ```
86
45
 
87
- ## Fault Context Access
46
+ ## Data Access
88
47
 
89
- Faults provide comprehensive access to execution context:
48
+ Faults provide comprehensive access to execution context, eg:
90
49
 
91
50
  ```ruby
92
51
  begin
93
- UserRegistration.call!(email: email, password: password)
52
+ LicenseActivation.execute!(license_key: key, machine_id: machine)
94
53
  rescue CMDx::Fault => e
95
54
  # Result information
96
- e.result.status #=> "failed" or "skipped"
97
- e.result.metadata[:reason] #=> "Email already exists"
98
- e.result.runtime #=> 0.05
55
+ e.result.state #=> "interrupted"
56
+ e.result.status #=> "failed" or "skipped"
57
+ e.result.reason #=> "License key already activated"
99
58
 
100
59
  # Task information
101
- e.task.class.name #=> "UserRegistration"
102
- e.task.id #=> "abc123..."
60
+ e.task.class #=> <LicenseActivation>
61
+ e.task.id #=> "abc123..."
103
62
 
104
63
  # Context data
105
- e.context.email #=> "user@example.com"
106
- e.context.password #=> "[FILTERED]"
64
+ e.context.license_key #=> "ABC-123-DEF"
65
+ e.context.machine_id #=> "[FILTERED]"
107
66
 
108
- # Chain information (for workflows)
109
- e.chain&.id #=> "def456..."
110
- e.chain&.results&.size #=> 3
67
+ # Chain information
68
+ e.chain.id #=> "def456..."
69
+ e.chain.size #=> 3
111
70
  end
112
71
  ```
113
72
 
@@ -115,18 +74,17 @@ end
115
74
 
116
75
  ### Task-Specific Matching
117
76
 
118
- > [!TIP]
119
- > Use `for?` to handle faults only from specific task classes, enabling targeted exception handling in complex workflows.
77
+ Use `for?` to handle faults only from specific task classes, enabling targeted exception handling in complex workflows.
120
78
 
121
79
  ```ruby
122
80
  begin
123
- PaymentWorkflow.call!(payment_data: data)
124
- rescue CMDx::Failed.for?(CardValidator, PaymentProcessor) => e
125
- # Handle only payment-related failures
126
- retry_with_backup_method(e.context)
127
- rescue CMDx::Skipped.for?(FraudCheck, RiskAssessment) => e
81
+ DocumentWorkflow.execute!(document_data: data)
82
+ rescue CMDx::FailFault.for?(FormatValidator, ContentProcessor) => e
83
+ # Handle only document-related failures
84
+ retry_with_alternate_parser(e.context)
85
+ rescue CMDx::SkipFault.for?(VirusScanner, ContentFilter) => e
128
86
  # Handle security-related skips
129
- flag_for_manual_review(e.context.transaction_id)
87
+ quarantine_for_review(e.context.document_id)
130
88
  end
131
89
  ```
132
90
 
@@ -134,74 +92,76 @@ end
134
92
 
135
93
  ```ruby
136
94
  begin
137
- OrderProcessor.call!(order: order_data)
138
- rescue CMDx::Fault.matches? { |f| f.context.order_value > 1000 } => e
139
- escalate_high_value_failure(e)
140
- rescue CMDx::Failed.matches? { |f| f.result.metadata[:retry_count] > 3 } => e
141
- abandon_processing(e)
142
- rescue CMDx::Fault.matches? { |f| f.result.metadata[:error_type] == "timeout" } => e
143
- increase_timeout_and_retry(e)
95
+ ReportGenerator.execute!(report: report_data)
96
+ rescue CMDx::Fault.matches? { |f| f.context.data_size > 10_000 } => e
97
+ escalate_large_dataset_failure(e)
98
+ rescue CMDx::FailFault.matches? { |f| f.result.metadata[:attempt_count] > 3 } => e
99
+ abandon_report_generation(e)
100
+ rescue CMDx::Fault.matches? { |f| f.result.metadata[:error_type] == "memory" } => e
101
+ increase_memory_and_retry(e)
144
102
  end
145
103
  ```
146
104
 
147
105
  ## Fault Propagation
148
106
 
149
- > [!IMPORTANT]
150
- > Use `throw!` to propagate failures while preserving fault context and maintaining the error chain for debugging.
107
+ Use `throw!` to propagate failures while preserving fault context and maintaining the error chain for debugging.
151
108
 
152
109
  ### Basic Propagation
153
110
 
154
111
  ```ruby
155
- class OrderProcessor < CMDx::Task
156
- def call
157
- # Validate order data
158
- validation_result = OrderValidator.call(context)
159
- throw!(validation_result) if validation_result.failed?
112
+ class ReportGenerator < CMDx::Task
113
+ def work
114
+ # Throw if skipped or failed
115
+ validation_result = DataValidator.execute(context)
116
+ throw!(validation_result)
117
+
118
+ # Only throw if skipped
119
+ check_permissions = CheckPermissions.execute(context)
120
+ throw!(check_permissions) if check_permissions.skipped?
160
121
 
161
- # Process payment
162
- payment_result = PaymentProcessor.call(context)
163
- throw!(payment_result) if payment_result.failed?
122
+ # Only throw if failed
123
+ data_result = DataProcessor.execute(context)
124
+ throw!(data_result) if data_result.failed?
164
125
 
165
126
  # Continue processing
166
- complete_order
127
+ generate_report
167
128
  end
168
129
  end
169
130
  ```
170
131
 
171
- ### Propagation with Context
132
+ ### Additional Metadata
172
133
 
173
134
  ```ruby
174
- class WorkflowProcessor < CMDx::Task
175
- def call
176
- step_result = DataValidation.call(context)
135
+ class BatchProcessor < CMDx::Task
136
+ def work
137
+ step_result = FileValidation.execute(context)
177
138
 
178
139
  if step_result.failed?
179
140
  throw!(step_result, {
180
- workflow_stage: "validation",
141
+ batch_stage: "validation",
181
142
  can_retry: true,
182
- next_step: "data_cleanup"
143
+ next_step: "file_repair"
183
144
  })
184
145
  end
185
146
 
186
- continue_workflow
147
+ continue_batch
187
148
  end
188
149
  end
189
150
  ```
190
151
 
191
152
  ## Chain Analysis
192
153
 
193
- > [!NOTE]
194
- > Results provide methods to analyze fault propagation and identify original failure sources in complex execution chains.
154
+ Results provide methods to analyze fault propagation and identify original failure sources in complex execution chains.
195
155
 
196
156
  ```ruby
197
- result = PaymentWorkflow.call(invalid_data)
157
+ result = DocumentWorkflow.execute(invalid_data)
198
158
 
199
159
  if result.failed?
200
160
  # Trace the original failure
201
161
  original = result.caused_failure
202
162
  if original
203
163
  puts "Original failure: #{original.task.class.name}"
204
- puts "Reason: #{original.metadata[:reason]}"
164
+ puts "Reason: #{original.reason}"
205
165
  end
206
166
 
207
167
  # Find what propagated the failure
@@ -220,46 +180,6 @@ if result.failed?
220
180
  end
221
181
  ```
222
182
 
223
- ## Configuration
224
-
225
- ### Task Halt Settings
226
-
227
- Control which statuses raise exceptions using `task_halt`:
228
-
229
- ```ruby
230
- class DataProcessor < CMDx::Task
231
- # Only failures raise exceptions
232
- cmd_settings!(task_halt: [CMDx::Result::FAILED])
233
-
234
- def call
235
- skip!(reason: "No data to process") if data.empty?
236
- # Skip will NOT raise exception on call!
237
- end
238
- end
239
-
240
- class CriticalValidator < CMDx::Task
241
- # Both failures and skips raise exceptions
242
- cmd_settings!(task_halt: [CMDx::Result::FAILED, CMDx::Result::SKIPPED])
243
-
244
- def call
245
- skip!(reason: "Validation bypassed") if bypass_mode?
246
- # Skip WILL raise exception on call!
247
- end
248
- end
249
- ```
250
-
251
- > [!WARNING]
252
- > Task halt configuration only affects the `call!` method. The `call` method always captures exceptions and converts them to result objects regardless of halt settings.
253
-
254
- ### Global Configuration
255
-
256
- ```ruby
257
- # Configure default halt behavior
258
- CMDx.configure do |config|
259
- config.task_halt = [CMDx::Result::FAILED] # Default: only failures halt
260
- end
261
- ```
262
-
263
183
  ---
264
184
 
265
185
  - **Prev:** [Interruptions - Halt](halt.md)