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.
- checksums.yaml +4 -4
- data/.DS_Store +0 -0
- data/.cursor/prompts/docs.md +4 -1
- data/.cursor/prompts/llms.md +20 -0
- data/.cursor/prompts/rspec.md +4 -1
- data/.cursor/prompts/yardoc.md +3 -2
- data/.cursor/rules/cursor-instructions.mdc +56 -1
- data/.irbrc +6 -0
- data/.rubocop.yml +29 -18
- data/.ruby-version +1 -1
- data/CHANGELOG.md +6 -128
- data/LLM.md +3317 -0
- data/README.md +68 -44
- data/docs/attributes/coercions.md +162 -0
- data/docs/attributes/defaults.md +90 -0
- data/docs/attributes/definitions.md +281 -0
- data/docs/attributes/naming.md +78 -0
- data/docs/attributes/validations.md +309 -0
- data/docs/basics/chain.md +56 -249
- data/docs/basics/context.md +56 -289
- data/docs/basics/execution.md +114 -0
- data/docs/basics/setup.md +37 -334
- data/docs/callbacks.md +89 -467
- data/docs/deprecation.md +91 -174
- data/docs/getting_started.md +212 -202
- data/docs/internationalization.md +11 -647
- data/docs/interruptions/exceptions.md +23 -198
- data/docs/interruptions/faults.md +71 -151
- data/docs/interruptions/halt.md +109 -186
- data/docs/logging.md +44 -256
- data/docs/middlewares.md +113 -426
- data/docs/outcomes/result.md +81 -228
- data/docs/outcomes/states.md +33 -221
- data/docs/outcomes/statuses.md +21 -311
- data/docs/tips_and_tricks.md +120 -70
- data/docs/workflows.md +99 -283
- data/lib/cmdx/.DS_Store +0 -0
- data/lib/cmdx/attribute.rb +229 -0
- data/lib/cmdx/attribute_registry.rb +94 -0
- data/lib/cmdx/attribute_value.rb +193 -0
- data/lib/cmdx/callback_registry.rb +69 -77
- data/lib/cmdx/chain.rb +56 -73
- data/lib/cmdx/coercion_registry.rb +52 -68
- data/lib/cmdx/coercions/array.rb +19 -18
- data/lib/cmdx/coercions/big_decimal.rb +20 -24
- data/lib/cmdx/coercions/boolean.rb +26 -25
- data/lib/cmdx/coercions/complex.rb +21 -22
- data/lib/cmdx/coercions/date.rb +25 -23
- data/lib/cmdx/coercions/date_time.rb +24 -25
- data/lib/cmdx/coercions/float.rb +25 -22
- data/lib/cmdx/coercions/hash.rb +31 -32
- data/lib/cmdx/coercions/integer.rb +30 -24
- data/lib/cmdx/coercions/rational.rb +29 -24
- data/lib/cmdx/coercions/string.rb +19 -22
- data/lib/cmdx/coercions/symbol.rb +37 -0
- data/lib/cmdx/coercions/time.rb +26 -25
- data/lib/cmdx/configuration.rb +49 -108
- data/lib/cmdx/context.rb +222 -44
- data/lib/cmdx/deprecator.rb +61 -0
- data/lib/cmdx/errors.rb +42 -252
- data/lib/cmdx/exceptions.rb +39 -0
- data/lib/cmdx/faults.rb +78 -39
- data/lib/cmdx/freezer.rb +51 -0
- data/lib/cmdx/identifier.rb +30 -0
- data/lib/cmdx/locale.rb +52 -0
- data/lib/cmdx/log_formatters/json.rb +21 -22
- data/lib/cmdx/log_formatters/key_value.rb +20 -22
- data/lib/cmdx/log_formatters/line.rb +15 -22
- data/lib/cmdx/log_formatters/logstash.rb +22 -23
- data/lib/cmdx/log_formatters/raw.rb +16 -22
- data/lib/cmdx/middleware_registry.rb +70 -74
- data/lib/cmdx/middlewares/correlate.rb +90 -54
- data/lib/cmdx/middlewares/runtime.rb +58 -0
- data/lib/cmdx/middlewares/timeout.rb +48 -68
- data/lib/cmdx/railtie.rb +12 -45
- data/lib/cmdx/result.rb +229 -314
- data/lib/cmdx/task.rb +194 -366
- data/lib/cmdx/utils/call.rb +49 -0
- data/lib/cmdx/utils/condition.rb +71 -0
- data/lib/cmdx/utils/format.rb +61 -0
- data/lib/cmdx/validator_registry.rb +63 -72
- data/lib/cmdx/validators/exclusion.rb +38 -67
- data/lib/cmdx/validators/format.rb +48 -49
- data/lib/cmdx/validators/inclusion.rb +43 -74
- data/lib/cmdx/validators/length.rb +91 -154
- data/lib/cmdx/validators/numeric.rb +87 -162
- data/lib/cmdx/validators/presence.rb +37 -50
- data/lib/cmdx/version.rb +1 -1
- data/lib/cmdx/worker.rb +178 -0
- data/lib/cmdx/workflow.rb +85 -81
- data/lib/cmdx.rb +19 -13
- data/lib/generators/cmdx/install_generator.rb +14 -13
- data/lib/generators/cmdx/task_generator.rb +25 -50
- data/lib/generators/cmdx/templates/install.rb +11 -46
- data/lib/generators/cmdx/templates/task.rb.tt +3 -2
- data/lib/locales/en.yml +18 -4
- data/src/cmdx-logo.png +0 -0
- metadata +32 -116
- data/docs/ai_prompts.md +0 -393
- data/docs/basics/call.md +0 -317
- data/docs/configuration.md +0 -344
- data/docs/parameters/coercions.md +0 -396
- data/docs/parameters/defaults.md +0 -335
- data/docs/parameters/definitions.md +0 -446
- data/docs/parameters/namespacing.md +0 -378
- data/docs/parameters/validations.md +0 -405
- data/docs/testing.md +0 -553
- data/lib/cmdx/callback.rb +0 -53
- data/lib/cmdx/chain_inspector.rb +0 -56
- data/lib/cmdx/chain_serializer.rb +0 -63
- data/lib/cmdx/coercion.rb +0 -57
- data/lib/cmdx/coercions/virtual.rb +0 -29
- data/lib/cmdx/core_ext/hash.rb +0 -83
- data/lib/cmdx/core_ext/module.rb +0 -98
- data/lib/cmdx/core_ext/object.rb +0 -125
- data/lib/cmdx/correlator.rb +0 -122
- data/lib/cmdx/error.rb +0 -60
- data/lib/cmdx/fault.rb +0 -140
- data/lib/cmdx/immutator.rb +0 -52
- data/lib/cmdx/lazy_struct.rb +0 -246
- data/lib/cmdx/log_formatters/pretty_json.rb +0 -40
- data/lib/cmdx/log_formatters/pretty_key_value.rb +0 -38
- data/lib/cmdx/log_formatters/pretty_line.rb +0 -41
- data/lib/cmdx/logger.rb +0 -49
- data/lib/cmdx/logger_ansi.rb +0 -68
- data/lib/cmdx/logger_serializer.rb +0 -116
- data/lib/cmdx/middleware.rb +0 -70
- data/lib/cmdx/parameter.rb +0 -312
- data/lib/cmdx/parameter_evaluator.rb +0 -231
- data/lib/cmdx/parameter_inspector.rb +0 -66
- data/lib/cmdx/parameter_registry.rb +0 -106
- data/lib/cmdx/parameter_serializer.rb +0 -59
- data/lib/cmdx/result_ansi.rb +0 -71
- data/lib/cmdx/result_inspector.rb +0 -71
- data/lib/cmdx/result_logger.rb +0 -59
- data/lib/cmdx/result_serializer.rb +0 -104
- data/lib/cmdx/rspec/matchers.rb +0 -28
- data/lib/cmdx/rspec/result_matchers/be_executed.rb +0 -42
- data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +0 -94
- data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +0 -94
- data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +0 -59
- data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +0 -57
- data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +0 -87
- data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +0 -51
- data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +0 -58
- data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +0 -59
- data/lib/cmdx/rspec/result_matchers/have_context.rb +0 -86
- data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +0 -54
- data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +0 -52
- data/lib/cmdx/rspec/result_matchers/have_metadata.rb +0 -114
- data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +0 -66
- data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +0 -64
- data/lib/cmdx/rspec/result_matchers/have_runtime.rb +0 -78
- data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +0 -76
- data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +0 -62
- data/lib/cmdx/rspec/task_matchers/have_callback.rb +0 -85
- data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +0 -68
- data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +0 -92
- data/lib/cmdx/rspec/task_matchers/have_middleware.rb +0 -46
- data/lib/cmdx/rspec/task_matchers/have_parameter.rb +0 -181
- data/lib/cmdx/task_deprecator.rb +0 -52
- data/lib/cmdx/task_processor.rb +0 -246
- data/lib/cmdx/task_serializer.rb +0 -57
- data/lib/cmdx/utils/ansi_color.rb +0 -73
- data/lib/cmdx/utils/log_timestamp.rb +0 -36
- data/lib/cmdx/utils/monotonic_runtime.rb +0 -34
- data/lib/cmdx/utils/name_affix.rb +0 -52
- data/lib/cmdx/validator.rb +0 -57
- data/lib/generators/cmdx/templates/workflow.rb.tt +0 -7
- data/lib/generators/cmdx/workflow_generator.rb +0 -84
- data/lib/locales/ar.yml +0 -35
- data/lib/locales/cs.yml +0 -35
- data/lib/locales/da.yml +0 -35
- data/lib/locales/de.yml +0 -35
- data/lib/locales/el.yml +0 -35
- data/lib/locales/es.yml +0 -35
- data/lib/locales/fi.yml +0 -35
- data/lib/locales/fr.yml +0 -35
- data/lib/locales/he.yml +0 -35
- data/lib/locales/hi.yml +0 -35
- data/lib/locales/it.yml +0 -35
- data/lib/locales/ja.yml +0 -35
- data/lib/locales/ko.yml +0 -35
- data/lib/locales/nl.yml +0 -35
- data/lib/locales/no.yml +0 -35
- data/lib/locales/pl.yml +0 -35
- data/lib/locales/pt.yml +0 -35
- data/lib/locales/ru.yml +0 -35
- data/lib/locales/sv.yml +0 -35
- data/lib/locales/th.yml +0 -35
- data/lib/locales/tr.yml +0 -35
- data/lib/locales/vi.yml +0 -35
- 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)
|
data/docs/configuration.md
DELETED
@@ -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)
|