cmdx 1.1.2 → 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 (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 +56 -1
  8. data/.irbrc +6 -0
  9. data/.rubocop.yml +29 -18
  10. data/CHANGELOG.md +5 -133
  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 +91 -154
  85. data/lib/cmdx/validators/numeric.rb +87 -162
  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
data/docs/basics/call.md DELETED
@@ -1,317 +0,0 @@
1
- # Basics - Call
2
-
3
- Task execution in CMDx provides two distinct methods that handle success and failure scenarios differently. Understanding when to use each method is crucial for proper error handling and control flow in your application workflows.
4
-
5
- ## Table of Contents
6
-
7
- - [TLDR](#tldr)
8
- - [Execution Methods Overview](#execution-methods-overview)
9
- - [Non-bang Call (`call`)](#non-bang-call-call)
10
- - [Bang Call (`call!`)](#bang-call-call)
11
- - [Direct Instantiation](#direct-instantiation)
12
- - [Parameter Passing](#parameter-passing)
13
- - [Result Propagation (`throw!`)](#result-propagation-throw)
14
- - [Result Callbacks](#result-callbacks)
15
- - [Task State Lifecycle](#task-state-lifecycle)
16
- - [Error Handling](#error-handling)
17
- - [Return Value Details](#return-value-details)
18
-
19
- ## TLDR
20
-
21
- ```ruby
22
- # Standard execution (preferred)
23
- result = ProcessOrderTask.call(order_id: 12345)
24
- result.success? # → true/false
25
-
26
- # Exception-based execution
27
- begin
28
- result = ProcessOrderTask.call!(order_id: 12345)
29
- # Handle success
30
- rescue CMDx::Failed => e
31
- # Handle failure
32
- end
33
-
34
- # Result callbacks
35
- ProcessOrderTask.call(order_id: 12345)
36
- .on_success { |result| notify_customer(result) }
37
- .on_failed { |result| handle_error(result) }
38
-
39
- # Propagate failures
40
- throw!(validation_result) if validation_result.failed?
41
- ```
42
-
43
- ## Execution Methods Overview
44
-
45
- > [!NOTE]
46
- > Tasks are single-use objects. Once executed, they are frozen and cannot be called again. Create a new instance for subsequent executions.
47
-
48
- | Method | Returns | Exceptions | Use Case |
49
- |--------|---------|------------|----------|
50
- | `call` | Always returns `CMDx::Result` | Never raises | Predictable result handling |
51
- | `call!` | Returns `CMDx::Result` on success | Raises `CMDx::Fault` on failure/skip | Exception-based control flow |
52
-
53
- ## Non-bang Call (`call`)
54
-
55
- The `call` method always returns a `CMDx::Result` object regardless of execution outcome. This is the preferred method for most use cases.
56
-
57
- ```ruby
58
- result = ProcessOrderTask.call(order_id: 12345)
59
-
60
- # Check execution state
61
- result.success? # → true/false
62
- result.failed? # → true/false
63
- result.skipped? # → true/false
64
-
65
- # Access result data
66
- result.context.order_id # → 12345
67
- result.runtime # → 0.05 (seconds)
68
- result.state # → "complete"
69
- result.status # → "success"
70
- ```
71
-
72
- ### Handling Different Outcomes
73
-
74
- ```ruby
75
- result = ProcessOrderTask.call(order_id: 12345)
76
-
77
- case result.status
78
- when "success"
79
- SendConfirmationTask.call(result.context)
80
- when "skipped"
81
- Rails.logger.info("Order skipped: #{result.metadata[:reason]}")
82
- when "failed"
83
- RetryOrderJob.perform_later(result.context.order_id)
84
- end
85
- ```
86
-
87
- ## Bang Call (`call!`)
88
-
89
- The bang `call!` method raises a `CMDx::Fault` exception when tasks fail or are skipped. It returns a `CMDx::Result` object only on success.
90
-
91
- > [!WARNING]
92
- > `call!` behavior depends on the `task_halt` configuration. By default, it raises exceptions for both failures and skips.
93
-
94
- ```ruby
95
- begin
96
- result = ProcessOrderTask.call!(order_id: 12345)
97
- SendConfirmationTask.call(result.context)
98
- rescue CMDx::Failed => e
99
- RetryOrderJob.perform_later(e.result.context.order_id)
100
- rescue CMDx::Skipped => e
101
- Rails.logger.info("Order skipped: #{e.result.metadata[:reason]}")
102
- end
103
- ```
104
-
105
- ### Exception Types
106
-
107
- | Exception | Raised When | Access Result |
108
- |-----------|-------------|---------------|
109
- | `CMDx::Failed` | Task execution fails | `exception.result` |
110
- | `CMDx::Skipped` | Task execution is skipped | `exception.result` |
111
-
112
- ## Direct Instantiation
113
-
114
- Tasks can be instantiated directly for advanced use cases, testing, and custom execution patterns:
115
-
116
- ```ruby
117
- # Direct instantiation
118
- task = ProcessOrderTask.new(order_id: 12345, notify_customer: true)
119
-
120
- # Access properties before execution
121
- task.id # → "abc123..." (unique task ID)
122
- task.context.order_id # → 12345
123
- task.context.notify_customer # → true
124
- task.result.state # → "initialized"
125
-
126
- # Manual execution
127
- task.process
128
- task.result.success? # → true/false
129
- ```
130
-
131
- ### Execution Approaches
132
-
133
- | Approach | Use Case | Benefits |
134
- |----------|----------|----------|
135
- | `TaskClass.call(...)` | Standard execution | Simple, handles full lifecycle |
136
- | `TaskClass.call!(...)` | Exception-based flow | Automatic fault raising |
137
- | `TaskClass.new(...).process` | Advanced scenarios | Full control, testing flexibility |
138
-
139
- ## Parameter Passing
140
-
141
- All methods accept parameters that become available in the task context:
142
-
143
- ```ruby
144
- # Direct parameters
145
- result = ProcessOrderTask.call(
146
- order_id: 12345,
147
- notify_customer: true,
148
- priority: "high"
149
- )
150
-
151
- # From another task result
152
- validation_result = ValidateOrderTask.call(order_id: 12345)
153
-
154
- # Pass Result object directly
155
- result = ProcessOrderTask.call(validation_result)
156
-
157
- # Pass context from previous result
158
- result = ProcessOrderTask.call(validation_result.context)
159
- ```
160
-
161
- ## Result Propagation (`throw!`)
162
-
163
- The `throw!` method enables result propagation, allowing tasks to bubble up failures from subtasks while preserving the original fault information:
164
-
165
- > [!IMPORTANT]
166
- > Use `throw!` to maintain failure context and prevent nested error handling in complex workflows.
167
-
168
- ```ruby
169
- class ProcessOrderTask < CMDx::Task
170
- def call
171
- # Validate order
172
- validation_result = ValidateOrderTask.call(context)
173
- throw!(validation_result) if validation_result.failed?
174
-
175
- # Process payment
176
- payment_result = ProcessPaymentTask.call(context)
177
- throw!(payment_result) if payment_result.failed?
178
-
179
- # Schedule delivery
180
- delivery_result = ScheduleDeliveryTask.call(context)
181
- throw!(delivery_result) unless delivery_result.success?
182
-
183
- # Continue with main logic
184
- finalize_order_processing
185
- end
186
- end
187
- ```
188
-
189
- ## Result Callbacks
190
-
191
- Results support fluent callback patterns for conditional logic:
192
-
193
- ```ruby
194
- ProcessOrderTask
195
- .call(order_id: 12345)
196
- .on_success { |result|
197
- SendOrderConfirmationTask.call(result.context)
198
- }
199
- .on_failed { |result|
200
- ErrorReportingService.notify(result.metadata[:error])
201
- }
202
- .on_executed { |result|
203
- MetricsService.timing('order.processing_time', result.runtime)
204
- }
205
- ```
206
-
207
- ### Available Callbacks
208
-
209
- > [!TIP]
210
- > Callbacks return the result object, enabling method chaining for complex conditional logic.
211
-
212
- ```ruby
213
- result = ProcessOrderTask.call(order_id: 12345)
214
-
215
- # State-based callbacks
216
- result
217
- .on_complete { |r| cleanup_resources(r) }
218
- .on_interrupted { |r| handle_interruption(r) }
219
- .on_executed { |r| log_execution_time(r) }
220
-
221
- # Status-based callbacks
222
- result
223
- .on_success { |r| handle_success(r) }
224
- .on_skipped { |r| handle_skip(r) }
225
- .on_failed { |r| handle_failure(r) }
226
-
227
- # Outcome-based callbacks
228
- result
229
- .on_good { |r| log_positive_outcome(r) } # success or skipped
230
- .on_bad { |r| log_negative_outcome(r) } # failed only
231
- ```
232
-
233
- ## Task State Lifecycle
234
-
235
- Tasks progress through defined states during execution:
236
-
237
- ```ruby
238
- result = ProcessOrderTask.call(order_id: 12345)
239
-
240
- # Execution states
241
- result.state # → "initialized" → "executing" → "complete"/"interrupted"
242
-
243
- # Outcome statuses
244
- result.status # → "success"/"failed"/"skipped"
245
- ```
246
-
247
- ## Error Handling
248
-
249
- ### Common Error Scenarios
250
-
251
- ```ruby
252
- # Parameter validation failure
253
- result = ProcessOrderTask.call(order_id: nil)
254
- result.failed? # → true
255
- result.metadata[:reason] # → "order_id is required"
256
-
257
- # Business logic failure
258
- result = ProcessOrderTask.call(order_id: 99999)
259
- result.failed? # → true
260
- result.metadata[:error].class # → ActiveRecord::RecordNotFound
261
-
262
- # Task skipped due to conditions
263
- result = ProcessOrderTask.call(order_id: 12345, force: false)
264
- result.skipped? # → true (if order already processed)
265
- result.metadata[:reason] # → "Order already processed"
266
- ```
267
-
268
- ### Exception Handling with `call!`
269
-
270
- ```ruby
271
- begin
272
- result = ProcessOrderTask.call!(order_id: 12345)
273
- rescue CMDx::Failed => e
274
- # Access original error details
275
- error_type = e.result.metadata[:error].class
276
- error_message = e.result.metadata[:reason]
277
-
278
- case error_type
279
- when ActiveRecord::RecordNotFound
280
- render json: { error: "Order not found" }, status: 404
281
- when PaymentError
282
- render json: { error: "Payment failed" }, status: 402
283
- else
284
- render json: { error: "Processing failed" }, status: 500
285
- end
286
- end
287
- ```
288
-
289
- ## Return Value Details
290
-
291
- The `Result` object provides comprehensive execution information:
292
-
293
- ```ruby
294
- result = ProcessOrderTask.call(order_id: 12345)
295
-
296
- # Execution metadata
297
- result.id # → "abc123..." (unique execution ID)
298
- result.runtime # → 0.05 (execution time in seconds)
299
- result.task # → ProcessOrderTask instance
300
- result.chain # → Chain object for tracking executions
301
-
302
- # Context and metadata
303
- result.context # → Context with all task data
304
- result.metadata # → Hash with execution metadata
305
-
306
- # State checking methods
307
- result.good? # → true for success/skipped
308
- result.bad? # → true for failed only
309
- result.complete? # → true when execution finished normally
310
- result.interrupted? # → true for failed/skipped
311
- result.executed? # → true for any completed execution
312
- ```
313
-
314
- ---
315
-
316
- - **Prev:** [Basics - Setup](setup.md)
317
- - **Next:** [Basics - Context](context.md)
@@ -1,344 +0,0 @@
1
- # Configuration
2
-
3
- CMDx provides a flexible configuration system that allows customization at both global and task levels. Configuration follows a hierarchy where global settings serve as defaults that can be overridden at the task level.
4
-
5
- ## Table of Contents
6
-
7
- - [TLDR](#tldr)
8
- - [Configuration Hierarchy](#configuration-hierarchy)
9
- - [Global Configuration](#global-configuration)
10
- - [Configuration Options](#configuration-options)
11
- - [Global Middlewares](#global-middlewares)
12
- - [Global Callbacks](#global-callbacks)
13
- - [Global Coercions](#global-coercions)
14
- - [Global Validators](#global-validators)
15
- - [Task Settings](#task-settings)
16
- - [Available Task Settings](#available-task-settings)
17
- - [Workflow Configuration](#workflow-configuration)
18
- - [Configuration Management](#configuration-management)
19
- - [Accessing Configuration](#accessing-configuration)
20
- - [Resetting Configuration](#resetting-configuration)
21
- - [Error Handling](#error-handling)
22
-
23
- ## TLDR
24
-
25
- ```ruby
26
- # Generate configuration file
27
- rails g cmdx:install
28
-
29
- # Global configuration
30
- CMDx.configure do |config|
31
- config.task_halt = ["failed", "skipped"] # Multiple halt statuses
32
- config.logger = Rails.logger # Custom logger
33
- config.middlewares.use TimeoutMiddleware # Global middleware
34
- config.callbacks.register :on_failure, :log # Global callback
35
- end
36
-
37
- # Task-level overrides
38
- class PaymentTask < CMDx::Task
39
- cmd_settings!(task_halt: "failed", tags: ["payments"])
40
-
41
- def call
42
- halt_on = cmd_setting(:task_halt) # Access settings
43
- end
44
- end
45
- ```
46
-
47
- ## Configuration Hierarchy
48
-
49
- CMDx follows a three-tier configuration hierarchy:
50
-
51
- 1. **Global Configuration**: Framework-wide defaults
52
- 2. **Task Settings**: Class-level overrides via `cmd_settings!`
53
- 3. **Runtime Parameters**: Instance-specific overrides during execution
54
-
55
- > [!IMPORTANT]
56
- > Task-level settings take precedence over global configuration. Settings are inherited from superclasses and can be overridden in subclasses.
57
-
58
- ## Global Configuration
59
-
60
- Generate a configuration file using the Rails generator:
61
-
62
- ```bash
63
- rails g cmdx:install
64
- ```
65
-
66
- This creates `config/initializers/cmdx.rb` with sensible defaults.
67
-
68
- ### Configuration Options
69
-
70
- | Option | Type | Default | Description |
71
- |---------------|-----------------------|----------------|-------------|
72
- | `task_halt` | String, Array<String> | `"failed"` | Result statuses that cause `call!` to raise faults |
73
- | `workflow_halt` | String, Array<String> | `"failed"` | Result statuses that halt workflow execution |
74
- | `logger` | Logger | Line formatter | Logger instance for task execution logging |
75
- | `middlewares` | MiddlewareRegistry | Empty registry | Global middleware registry applied to all tasks |
76
- | `callbacks` | CallbackRegistry | Empty registry | Global callback registry applied to all tasks |
77
- | `coercions` | CoercionRegistry | Built-in coercions | Global coercion registry for custom parameter types |
78
- | `validators` | ValidatorRegistry | Built-in validators | Global validator registry for parameter validation |
79
-
80
- ### Global Middlewares
81
-
82
- Configure middlewares that automatically apply to all tasks:
83
-
84
- ```ruby
85
- CMDx.configure do |config|
86
- # Simple middleware registration
87
- config.middlewares.use CMDx::Middlewares::Timeout
88
-
89
- # Middleware with configuration
90
- config.middlewares.use CMDx::Middlewares::Timeout, seconds: 30
91
-
92
- # Multiple middlewares
93
- config.middlewares.use AuthenticationMiddleware
94
- config.middlewares.use LoggingMiddleware, level: :debug
95
- config.middlewares.use MetricsMiddleware, namespace: "app.tasks"
96
- end
97
- ```
98
-
99
- > [!NOTE]
100
- > Middlewares are executed in registration order. Each middleware wraps the next, creating an execution chain around task logic.
101
-
102
- ### Global Callbacks
103
-
104
- Configure callbacks that automatically apply to all tasks:
105
-
106
- ```ruby
107
- CMDx.configure do |config|
108
- # Method callbacks
109
- config.callbacks.register :before_execution, :setup_request_context
110
- config.callbacks.register :after_execution, :cleanup_temp_files
111
-
112
- # Conditional callbacks
113
- config.callbacks.register :on_failure, :notify_admin, if: :production?
114
- config.callbacks.register :on_success, :update_metrics, unless: :test?
115
-
116
- # Proc callbacks with context
117
- config.callbacks.register :on_complete, proc { |task, type|
118
- duration = task.metadata[:runtime]
119
- StatsD.histogram("task.duration", duration, tags: ["class:#{task.class.name}"])
120
- }
121
- end
122
- ```
123
-
124
- ### Global Coercions
125
-
126
- Configure custom coercions for domain-specific types:
127
-
128
- ```ruby
129
- CMDx.configure do |config|
130
- # Simple coercion classes
131
- config.coercions.register :money, MoneyCoercion
132
- config.coercions.register :email, EmailCoercion
133
-
134
- # Complex coercions with options
135
- config.coercions.register :csv_array, proc { |value, options|
136
- separator = options[:separator] || ','
137
- max_items = options[:max_items] || 100
138
-
139
- items = value.to_s.split(separator).map(&:strip).reject(&:empty?)
140
- items.first(max_items)
141
- }
142
- end
143
- ```
144
-
145
- ### Global Validators
146
-
147
- Configure custom validators for parameter validation:
148
-
149
- ```ruby
150
- CMDx.configure do |config|
151
- # Validator classes
152
- config.validators.register :email, EmailValidator
153
- config.validators.register :phone, PhoneValidator
154
-
155
- # Proc validators with options
156
- config.validators.register :api_key, proc { |value, options|
157
- required_prefix = options.dig(:api_key, :prefix) || "sk_"
158
- min_length = options.dig(:api_key, :min_length) || 32
159
-
160
- value.start_with?(required_prefix) && value.length >= min_length
161
- }
162
- end
163
- ```
164
-
165
- ## Task Settings
166
-
167
- Override global configuration for specific tasks using `cmd_settings!`:
168
-
169
- ```ruby
170
- class ProcessPaymentTask < CMDx::Task
171
- cmd_settings!(
172
- task_halt: ["failed"], # Only halt on failures
173
- tags: ["payments", "critical"], # Logging tags
174
- logger: PaymentLogger.new, # Custom logger
175
- log_level: :info, # Log level override
176
- log_formatter: CMDx::LogFormatters::Json.new # JSON formatting
177
- )
178
-
179
- def call
180
- # Payment processing logic
181
- charge_customer(amount, payment_method)
182
- end
183
-
184
- private
185
-
186
- def charge_customer(amount, method)
187
- # Implementation details
188
- end
189
- end
190
- ```
191
-
192
- ### Available Task Settings
193
-
194
- | Setting | Type | Description |
195
- |-----------------|-----------------------|-------------|
196
- | `task_halt` | String, Array<String> | Result statuses that cause `call!` to raise faults |
197
- | `workflow_halt` | String, Array<String> | Result statuses that halt workflow execution |
198
- | `tags` | Array<String> | Tags automatically appended to logs |
199
- | `logger` | Logger | Custom logger instance |
200
- | `log_level` | Symbol | Log level (`:debug`, `:info`, `:warn`, `:error`, `:fatal`) |
201
- | `log_formatter` | LogFormatter | Custom log formatter |
202
-
203
- > [!TIP]
204
- > Use task-level settings for tasks that require special handling, such as payment processing, external API calls, or critical system operations.
205
-
206
- ### Workflow Configuration
207
-
208
- Configure halt behavior and logging for workflows:
209
-
210
- ```ruby
211
- class OrderProcessingWorkflow < CMDx::Workflow
212
- # Halt on any non-success status
213
- cmd_settings!(
214
- workflow_halt: ["failed", "skipped"],
215
- tags: ["orders", "e-commerce"],
216
- log_level: :info
217
- )
218
-
219
- process ValidateOrderTask
220
- process ChargePaymentTask
221
- process UpdateInventoryTask
222
- process SendConfirmationTask
223
- end
224
-
225
- class DataMigrationWorkflow < CMDx::Workflow
226
- # Continue on skipped tasks, halt only on failures
227
- cmd_settings!(
228
- workflow_halt: "failed",
229
- tags: ["migration", "maintenance"]
230
- )
231
-
232
- process BackupDataTask
233
- process MigrateUsersTask
234
- process MigrateOrdersTask
235
- process ValidateDataTask
236
- end
237
- ```
238
-
239
- ## Configuration Management
240
-
241
- ### Accessing Configuration
242
-
243
- ```ruby
244
- # Global configuration access
245
- CMDx.configuration.logger #=> <Logger instance>
246
- CMDx.configuration.task_halt #=> "failed"
247
- CMDx.configuration.middlewares.middlewares #=> [<Middleware>, ...]
248
- CMDx.configuration.callbacks.callbacks #=> {before_execution: [...], ...}
249
-
250
- # Task-specific settings
251
- class DataProcessingTask < CMDx::Task
252
- cmd_settings!(
253
- tags: ["data", "analytics"],
254
- task_halt: ["failed", "skipped"]
255
- )
256
-
257
- def call
258
- # Access current task settings
259
- log_tags = cmd_setting(:tags) #=> ["data", "analytics"]
260
- halt_on = cmd_setting(:task_halt) #=> ["failed", "skipped"]
261
- logger_instance = cmd_setting(:logger) #=> Inherited from global
262
- end
263
- end
264
- ```
265
-
266
- ### Resetting Configuration
267
-
268
- > [!WARNING]
269
- > Resetting configuration affects the entire application. Use primarily in test environments or during application initialization.
270
-
271
- ```ruby
272
- # Reset to framework defaults
273
- CMDx.reset_configuration!
274
-
275
- # Verify reset
276
- CMDx.configuration.task_halt #=> "failed" (default)
277
- CMDx.configuration.middlewares #=> Empty registry
278
- CMDx.configuration.callbacks #=> Empty registry
279
-
280
- # Commonly used in test setup
281
- RSpec.configure do |config|
282
- config.before(:each) do
283
- CMDx.reset_configuration!
284
- end
285
- end
286
- ```
287
-
288
- ## Error Handling
289
-
290
- ### Configuration Validation
291
-
292
- ```ruby
293
- # Invalid configuration types
294
- CMDx.configure do |config|
295
- config.task_halt = :invalid_type # Error: must be String or Array
296
- config.logger = "not_a_logger" # Error: must respond to logging methods
297
- end
298
- ```
299
-
300
- ### Missing Settings Access
301
-
302
- ```ruby
303
- class ExampleTask < CMDx::Task
304
- def call
305
- # Accessing non-existent setting
306
- value = cmd_setting(:non_existent_setting) #=> nil (returns nil for undefined)
307
-
308
- # Check if setting exists
309
- if cmd_setting(:custom_timeout)
310
- timeout = cmd_setting(:custom_timeout)
311
- else
312
- timeout = 30 # fallback
313
- end
314
- end
315
- end
316
- ```
317
-
318
- ### Configuration Conflicts
319
-
320
- ```ruby
321
- # Parent class configuration
322
- class BaseTask < CMDx::Task
323
- cmd_settings!(task_halt: "failed", tags: ["base"])
324
- end
325
-
326
- # Child class inherits and overrides
327
- class SpecialTask < BaseTask
328
- cmd_settings!(task_halt: ["failed", "skipped"]) # Overrides parent
329
- # tags: ["base"] inherited from parent
330
-
331
- def call
332
- halt_statuses = cmd_setting(:task_halt) #=> ["failed", "skipped"]
333
- inherited_tags = cmd_setting(:tags) #=> ["base"]
334
- end
335
- end
336
- ```
337
-
338
- > [!IMPORTANT]
339
- > Settings inheritance follows Ruby's method resolution order. Child class settings always override parent class settings for the same key.
340
-
341
- ---
342
-
343
- - **Prev:** [Getting Started](getting_started.md)
344
- - **Next:** [Basics - Setup](basics/setup.md)