cmdx 1.9.0 → 1.10.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/.cursor/prompts/llms.md +3 -13
- data/.cursor/prompts/yardoc.md +1 -0
- data/CHANGELOG.md +16 -0
- data/LLM.md +436 -374
- data/README.md +7 -2
- data/docs/basics/setup.md +17 -0
- data/docs/callbacks.md +1 -1
- data/docs/getting_started.md +22 -2
- data/docs/index.md +13 -1
- data/docs/retries.md +121 -0
- data/docs/tips_and_tricks.md +2 -1
- data/examples/stoplight_circuit_breaker.md +36 -0
- data/lib/cmdx/attribute.rb +82 -1
- data/lib/cmdx/attribute_registry.rb +20 -0
- data/lib/cmdx/attribute_value.rb +25 -0
- data/lib/cmdx/callback_registry.rb +19 -0
- data/lib/cmdx/chain.rb +34 -1
- data/lib/cmdx/coercion_registry.rb +18 -0
- data/lib/cmdx/coercions/array.rb +2 -0
- data/lib/cmdx/coercions/big_decimal.rb +3 -0
- data/lib/cmdx/coercions/boolean.rb +5 -0
- data/lib/cmdx/coercions/complex.rb +2 -0
- data/lib/cmdx/coercions/date.rb +4 -0
- data/lib/cmdx/coercions/date_time.rb +5 -0
- data/lib/cmdx/coercions/float.rb +2 -0
- data/lib/cmdx/coercions/hash.rb +2 -0
- data/lib/cmdx/coercions/integer.rb +2 -0
- data/lib/cmdx/coercions/rational.rb +2 -0
- data/lib/cmdx/coercions/string.rb +2 -0
- data/lib/cmdx/coercions/symbol.rb +2 -0
- data/lib/cmdx/coercions/time.rb +5 -0
- data/lib/cmdx/configuration.rb +126 -3
- data/lib/cmdx/context.rb +36 -0
- data/lib/cmdx/deprecator.rb +3 -0
- data/lib/cmdx/errors.rb +22 -0
- data/lib/cmdx/executor.rb +71 -11
- data/lib/cmdx/faults.rb +14 -0
- data/lib/cmdx/identifier.rb +2 -0
- data/lib/cmdx/locale.rb +3 -0
- data/lib/cmdx/log_formatters/json.rb +2 -0
- data/lib/cmdx/log_formatters/key_value.rb +2 -0
- data/lib/cmdx/log_formatters/line.rb +2 -0
- data/lib/cmdx/log_formatters/logstash.rb +2 -0
- data/lib/cmdx/log_formatters/raw.rb +2 -0
- data/lib/cmdx/middleware_registry.rb +20 -0
- data/lib/cmdx/middlewares/correlate.rb +11 -0
- data/lib/cmdx/middlewares/runtime.rb +4 -0
- data/lib/cmdx/middlewares/timeout.rb +4 -0
- data/lib/cmdx/pipeline.rb +20 -1
- data/lib/cmdx/railtie.rb +4 -0
- data/lib/cmdx/result.rb +123 -1
- data/lib/cmdx/task.rb +91 -1
- data/lib/cmdx/utils/call.rb +2 -0
- data/lib/cmdx/utils/condition.rb +3 -0
- data/lib/cmdx/utils/format.rb +5 -0
- data/lib/cmdx/validator_registry.rb +18 -0
- data/lib/cmdx/validators/exclusion.rb +2 -0
- data/lib/cmdx/validators/format.rb +2 -0
- data/lib/cmdx/validators/inclusion.rb +2 -0
- data/lib/cmdx/validators/length.rb +14 -0
- data/lib/cmdx/validators/numeric.rb +14 -0
- data/lib/cmdx/validators/presence.rb +2 -0
- data/lib/cmdx/version.rb +4 -1
- data/lib/cmdx/workflow.rb +10 -0
- data/lib/cmdx.rb +8 -0
- data/lib/generators/cmdx/locale_generator.rb +0 -1
- data/mkdocs.yml +3 -1
- metadata +3 -1
data/LLM.md
CHANGED
|
@@ -1,40 +1,33 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
This file contains all the CMDx documentation consolidated from the docs directory.
|
|
1
|
+
# Getting Started
|
|
4
2
|
|
|
5
|
-
|
|
3
|
+
CMDx is a Ruby framework for building maintainable, observable business logic through composable command objects. It brings structure, consistency, and powerful developer tools to your business processes.
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
---
|
|
5
|
+
**Common challenges it solves:**
|
|
9
6
|
|
|
10
|
-
|
|
7
|
+
- Inconsistent service object patterns across your codebase
|
|
8
|
+
- Limited logging makes debugging a nightmare
|
|
9
|
+
- Fragile error handling erodes confidence
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
**What you get:**
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
- Consistent, standardized architecture
|
|
14
|
+
- Built-in flow control and error handling
|
|
15
|
+
- Composable, reusable workflows
|
|
16
|
+
- Comprehensive logging for observability
|
|
17
|
+
- Attribute validation with type coercions
|
|
18
|
+
- Sensible defaults and developer-friendly APIs
|
|
15
19
|
|
|
16
|
-
|
|
17
|
-
- Minimal or no logging, making debugging painful
|
|
18
|
-
- Fragile designs that erode developer confidence
|
|
20
|
+
## The CERO Pattern
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
CMDx embraces the Compose, Execute, React, Observe (CERO) pattern—a simple yet powerful approach to building reliable business logic.
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
- Provides flow control and error handling
|
|
24
|
-
- Supports composable, reusable workflows
|
|
25
|
-
- Includes detailed logging for observability
|
|
26
|
-
- Defines input attributes with fallback defaults
|
|
27
|
-
- Adds validations and type coercions
|
|
28
|
-
- Plus many other developer-friendly tools
|
|
24
|
+
🧩 **Compose** — Define small, focused tasks with typed attributes and validations
|
|
29
25
|
|
|
30
|
-
|
|
26
|
+
⚡ **Execute** — Run tasks with clear outcomes and pluggable behaviors
|
|
31
27
|
|
|
32
|
-
|
|
28
|
+
🔄 **React** — Adapt to outcomes by chaining follow-up tasks or handling faults
|
|
33
29
|
|
|
34
|
-
|
|
35
|
-
- **Execute** → Run tasks with clear outcomes, intentional halts, and pluggable behaviors via middlewares and callbacks.
|
|
36
|
-
- **React** → Adapt to outcomes by chaining follow-up tasks, handling faults, or shaping future flows.
|
|
37
|
-
- **Observe** → Capture immutable results, structured logs, and full execution chains for reliable tracing and insight.
|
|
30
|
+
🔍 **Observe** — Capture structured logs and execution chains for debugging
|
|
38
31
|
|
|
39
32
|
## Installation
|
|
40
33
|
|
|
@@ -54,45 +47,54 @@ This creates `config/initializers/cmdx.rb` file.
|
|
|
54
47
|
|
|
55
48
|
## Configuration Hierarchy
|
|
56
49
|
|
|
57
|
-
CMDx
|
|
50
|
+
CMDx uses a straightforward two-tier configuration system:
|
|
51
|
+
|
|
52
|
+
1. **Global Configuration** — Framework-wide defaults
|
|
53
|
+
2. **Task Settings** — Class-level overrides using `settings`
|
|
58
54
|
|
|
59
|
-
|
|
60
|
-
2. **Task Settings**: Class-level overrides via `settings`
|
|
55
|
+
!!! warning "Important"
|
|
61
56
|
|
|
62
|
-
|
|
63
|
-
> Task-level settings take precedence over global configuration. Settings are inherited from superclasses and can be overridden in subclasses.
|
|
57
|
+
Task settings take precedence over global config. Settings are inherited from parent classes and can be overridden in subclasses.
|
|
64
58
|
|
|
65
59
|
## Global Configuration
|
|
66
60
|
|
|
67
|
-
|
|
68
|
-
Globally these settings are initialized with sensible defaults.
|
|
61
|
+
Configure framework-wide defaults that apply to all tasks. These settings come with sensible defaults out of the box.
|
|
69
62
|
|
|
70
63
|
### Breakpoints
|
|
71
64
|
|
|
72
|
-
|
|
65
|
+
Control when `execute!` raises a `CMDx::Fault` based on task status.
|
|
73
66
|
|
|
74
67
|
```ruby
|
|
75
68
|
CMDx.configure do |config|
|
|
76
|
-
# String or Array[String]
|
|
77
|
-
config.task_breakpoints = "failed"
|
|
69
|
+
config.task_breakpoints = "failed" # String or Array[String]
|
|
78
70
|
end
|
|
79
71
|
```
|
|
80
72
|
|
|
81
|
-
|
|
73
|
+
For workflows, configure which statuses halt the execution pipeline:
|
|
82
74
|
|
|
83
75
|
```ruby
|
|
84
76
|
CMDx.configure do |config|
|
|
85
|
-
# String or Array[String]
|
|
86
77
|
config.workflow_breakpoints = ["skipped", "failed"]
|
|
87
78
|
end
|
|
88
79
|
```
|
|
89
80
|
|
|
81
|
+
### Rollback
|
|
82
|
+
|
|
83
|
+
Control when a `rollback` of task execution is called.
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
CMDx.configure do |config|
|
|
87
|
+
config.rollback_on = ["failed"] # String or Array[String]
|
|
88
|
+
end
|
|
89
|
+
```
|
|
90
|
+
|
|
90
91
|
### Backtraces
|
|
91
92
|
|
|
92
|
-
Enable backtraces
|
|
93
|
+
Enable detailed backtraces for non-fault exceptions to improve debugging. Optionally clean up stack traces to remove framework noise.
|
|
94
|
+
|
|
95
|
+
!!! note
|
|
93
96
|
|
|
94
|
-
|
|
95
|
-
> The `backtrace_cleaner` is set to `Rails.backtrace_cleaner.clean` in a Rails env by default.
|
|
97
|
+
In Rails environments, `backtrace_cleaner` defaults to `Rails.backtrace_cleaner.clean`.
|
|
96
98
|
|
|
97
99
|
```ruby
|
|
98
100
|
CMDx.configure do |config|
|
|
@@ -109,10 +111,11 @@ end
|
|
|
109
111
|
|
|
110
112
|
### Exception Handlers
|
|
111
113
|
|
|
112
|
-
|
|
114
|
+
Register handlers that run when non-fault exceptions occur.
|
|
115
|
+
|
|
116
|
+
!!! tip
|
|
113
117
|
|
|
114
|
-
|
|
115
|
-
> Use exception handlers to send errors to your APM of choice.
|
|
118
|
+
Use exception handlers to send errors to your APM of choice.
|
|
116
119
|
|
|
117
120
|
```ruby
|
|
118
121
|
CMDx.configure do |config|
|
|
@@ -136,7 +139,7 @@ end
|
|
|
136
139
|
|
|
137
140
|
### Middlewares
|
|
138
141
|
|
|
139
|
-
See the [
|
|
142
|
+
See the [Middlewares](https://github.com/drexed/cmdx/blob/main/docs/middlewares.md#declarations) docs for task level configurations.
|
|
140
143
|
|
|
141
144
|
```ruby
|
|
142
145
|
CMDx.configure do |config|
|
|
@@ -160,12 +163,13 @@ CMDx.configure do |config|
|
|
|
160
163
|
end
|
|
161
164
|
```
|
|
162
165
|
|
|
163
|
-
|
|
164
|
-
|
|
166
|
+
!!! note
|
|
167
|
+
|
|
168
|
+
Middlewares are executed in registration order. Each middleware wraps the next, creating an execution chain around task logic.
|
|
165
169
|
|
|
166
170
|
### Callbacks
|
|
167
171
|
|
|
168
|
-
See the [Callbacks](
|
|
172
|
+
See the [Callbacks](https://github.com/drexed/cmdx/blob/main/docs/callbacks.md#declarations) docs for task level configurations.
|
|
169
173
|
|
|
170
174
|
```ruby
|
|
171
175
|
CMDx.configure do |config|
|
|
@@ -191,7 +195,7 @@ end
|
|
|
191
195
|
|
|
192
196
|
### Coercions
|
|
193
197
|
|
|
194
|
-
See the [Attributes - Coercions](
|
|
198
|
+
See the [Attributes - Coercions](https://github.com/drexed/cmdx/blob/main/docs/attributes/coercions.md#declarations) docs for task level configurations.
|
|
195
199
|
|
|
196
200
|
```ruby
|
|
197
201
|
CMDx.configure do |config|
|
|
@@ -217,7 +221,7 @@ end
|
|
|
217
221
|
|
|
218
222
|
### Validators
|
|
219
223
|
|
|
220
|
-
See the [Attributes - Validations](
|
|
224
|
+
See the [Attributes - Validations](https://github.com/drexed/cmdx/blob/main/docs/attributes/validations.md#declarations) docs for task level configurations.
|
|
221
225
|
|
|
222
226
|
```ruby
|
|
223
227
|
CMDx.configure do |config|
|
|
@@ -264,7 +268,8 @@ class GenerateInvoice < CMDx::Task
|
|
|
264
268
|
deprecated: true, # Task deprecations
|
|
265
269
|
retries: 3, # Non-fault exception retries
|
|
266
270
|
retry_on: [External::ApiError], # List of exceptions to retry on
|
|
267
|
-
retry_jitter: 1
|
|
271
|
+
retry_jitter: 1, # Space between retry iteration, eg: current retry num + 1
|
|
272
|
+
rollback_on: ["failed", "skipped"], # Rollback on override
|
|
268
273
|
)
|
|
269
274
|
|
|
270
275
|
def work
|
|
@@ -273,13 +278,13 @@ class GenerateInvoice < CMDx::Task
|
|
|
273
278
|
end
|
|
274
279
|
```
|
|
275
280
|
|
|
276
|
-
|
|
277
|
-
|
|
281
|
+
!!! warning "Important"
|
|
282
|
+
|
|
283
|
+
Retries reuse the same context. By default, all `StandardError` exceptions (including faults) are retried unless you specify `retry_on` option for specific matches.
|
|
278
284
|
|
|
279
285
|
### Registrations
|
|
280
286
|
|
|
281
|
-
Register middlewares, callbacks, coercions, and validators
|
|
282
|
-
Deregister options that should not be available.
|
|
287
|
+
Register or deregister middlewares, callbacks, coercions, and validators for specific tasks:
|
|
283
288
|
|
|
284
289
|
```ruby
|
|
285
290
|
class SendCampaignEmail < CMDx::Task
|
|
@@ -331,8 +336,9 @@ end
|
|
|
331
336
|
|
|
332
337
|
### Resetting
|
|
333
338
|
|
|
334
|
-
|
|
335
|
-
|
|
339
|
+
!!! warning
|
|
340
|
+
|
|
341
|
+
Resetting affects your entire application. Use this primarily in test environments.
|
|
336
342
|
|
|
337
343
|
```ruby
|
|
338
344
|
# Reset to framework defaults
|
|
@@ -369,21 +375,26 @@ class ModerateBlogPost < CMDx::Task
|
|
|
369
375
|
end
|
|
370
376
|
```
|
|
371
377
|
|
|
372
|
-
|
|
373
|
-
> Use **present tense verbs + noun** for task names, eg: `ModerateBlogPost`, `ScheduleAppointment`, `ValidateDocument`
|
|
378
|
+
!!! tip
|
|
374
379
|
|
|
375
|
-
|
|
380
|
+
Use **present tense verbs + noun** for task names, eg: `ModerateBlogPost`, `ScheduleAppointment`, `ValidateDocument`
|
|
376
381
|
|
|
377
|
-
|
|
378
|
-
|
|
382
|
+
## Type safety
|
|
383
|
+
|
|
384
|
+
CMDx includes built-in RBS (Ruby Type Signature) inline annotations throughout the codebase, providing type information for static analysis and editor support.
|
|
385
|
+
|
|
386
|
+
- **Type checking** — Catch type errors before runtime using tools like Steep or TypeProf
|
|
387
|
+
- **Better IDE support** — Enhanced autocomplete, navigation, and inline documentation
|
|
388
|
+
- **Self-documenting code** — Clear method signatures and return types
|
|
389
|
+
- **Refactoring confidence** — Type-aware refactoring reduces bugs
|
|
379
390
|
|
|
380
391
|
# Basics - Setup
|
|
381
392
|
|
|
382
|
-
Tasks are the
|
|
393
|
+
Tasks are the heart of CMDx—self-contained units of business logic with built-in validation, error handling, and execution tracking.
|
|
383
394
|
|
|
384
395
|
## Structure
|
|
385
396
|
|
|
386
|
-
Tasks inherit from `CMDx::Task` and
|
|
397
|
+
Tasks need only two things: inherit from `CMDx::Task` and define a `work` method:
|
|
387
398
|
|
|
388
399
|
```ruby
|
|
389
400
|
class ValidateDocument < CMDx::Task
|
|
@@ -393,7 +404,7 @@ class ValidateDocument < CMDx::Task
|
|
|
393
404
|
end
|
|
394
405
|
```
|
|
395
406
|
|
|
396
|
-
|
|
407
|
+
Without a `work` method, execution raises `CMDx::UndefinedMethodError`.
|
|
397
408
|
|
|
398
409
|
```ruby
|
|
399
410
|
class IncompleteTask < CMDx::Task
|
|
@@ -403,10 +414,25 @@ end
|
|
|
403
414
|
IncompleteTask.execute #=> raises CMDx::UndefinedMethodError
|
|
404
415
|
```
|
|
405
416
|
|
|
417
|
+
## Rollback
|
|
418
|
+
|
|
419
|
+
Undo any operations linked to the given status, helping to restore a pristine state.
|
|
420
|
+
|
|
421
|
+
```ruby
|
|
422
|
+
class ValidateDocument < CMDx::Task
|
|
423
|
+
def work
|
|
424
|
+
# Your logic here...
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
def rollback
|
|
428
|
+
# Your undo logic...
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
```
|
|
432
|
+
|
|
406
433
|
## Inheritance
|
|
407
434
|
|
|
408
|
-
|
|
409
|
-
Create a base class to share common configuration across tasks:
|
|
435
|
+
Share configuration across tasks using inheritance:
|
|
410
436
|
|
|
411
437
|
```ruby
|
|
412
438
|
class ApplicationTask < CMDx::Task
|
|
@@ -432,10 +458,11 @@ end
|
|
|
432
458
|
|
|
433
459
|
## Lifecycle
|
|
434
460
|
|
|
435
|
-
Tasks follow a predictable
|
|
461
|
+
Tasks follow a predictable execution pattern:
|
|
462
|
+
|
|
463
|
+
!!! danger "Caution"
|
|
436
464
|
|
|
437
|
-
|
|
438
|
-
> Tasks are single-use objects. Once executed, they are frozen and cannot be executed again.
|
|
465
|
+
Tasks are single-use objects. Once executed, they're frozen and immutable.
|
|
439
466
|
|
|
440
467
|
| Stage | State | Status | Description |
|
|
441
468
|
|-------|-------|--------|-------------|
|
|
@@ -444,20 +471,15 @@ Tasks follow a predictable call pattern with specific states and statuses:
|
|
|
444
471
|
| **Execution** | `executing` | `success`/`failed`/`skipped` | `work` method runs |
|
|
445
472
|
| **Completion** | `executed` | `success`/`failed`/`skipped` | Result finalized |
|
|
446
473
|
| **Freezing** | `executed` | `success`/`failed`/`skipped` | Task becomes immutable |
|
|
447
|
-
|
|
448
|
-
---
|
|
449
|
-
|
|
450
|
-
url: https://github.com/drexed/cmdx/blob/main/docs/basics/execution.md
|
|
451
|
-
---
|
|
474
|
+
| **Rollback** | `executed` | `failed`/`skipped` | Work undone |
|
|
452
475
|
|
|
453
476
|
# Basics - Execution
|
|
454
477
|
|
|
455
|
-
|
|
478
|
+
CMDx offers two execution methods with different error handling approaches. Choose based on your needs: safe result handling or exception-based control flow.
|
|
456
479
|
|
|
457
|
-
## Methods
|
|
480
|
+
## Execution Methods
|
|
458
481
|
|
|
459
|
-
|
|
460
|
-
Create a new instance for subsequent executions.
|
|
482
|
+
Both methods return results, but handle failures differently:
|
|
461
483
|
|
|
462
484
|
| Method | Returns | Exceptions | Use Case |
|
|
463
485
|
|--------|---------|------------|----------|
|
|
@@ -466,10 +488,7 @@ Create a new instance for subsequent executions.
|
|
|
466
488
|
|
|
467
489
|
## Non-bang Execution
|
|
468
490
|
|
|
469
|
-
|
|
470
|
-
This is the preferred method for most use cases.
|
|
471
|
-
|
|
472
|
-
Any unhandled exceptions will be caught and returned as a task failure.
|
|
491
|
+
Always returns a `CMDx::Result`, never raises exceptions. Perfect for most use cases.
|
|
473
492
|
|
|
474
493
|
```ruby
|
|
475
494
|
result = CreateAccount.execute(email: "user@example.com")
|
|
@@ -487,23 +506,22 @@ result.status #=> "success"
|
|
|
487
506
|
|
|
488
507
|
## Bang Execution
|
|
489
508
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
It raises any unhandled non-fault exceptions caused during execution.
|
|
509
|
+
Raises `CMDx::Fault` exceptions on failure or skip. Returns results only on success.
|
|
493
510
|
|
|
494
511
|
| Exception | Raised When |
|
|
495
512
|
|-----------|-------------|
|
|
496
513
|
| `CMDx::FailFault` | Task execution fails |
|
|
497
514
|
| `CMDx::SkipFault` | Task execution is skipped |
|
|
498
515
|
|
|
499
|
-
|
|
500
|
-
|
|
516
|
+
!!! warning "Important"
|
|
517
|
+
|
|
518
|
+
Behavior depends on `task_breakpoints` or `workflow_breakpoints` config. Default: only failures raise exceptions.
|
|
501
519
|
|
|
502
520
|
```ruby
|
|
503
521
|
begin
|
|
504
522
|
result = CreateAccount.execute!(email: "user@example.com")
|
|
505
523
|
SendWelcomeEmail.execute(result.context)
|
|
506
|
-
rescue CMDx::
|
|
524
|
+
rescue CMDx::FailFault => e
|
|
507
525
|
ScheduleAccountRetryJob.perform_later(e.result.context.email)
|
|
508
526
|
rescue CMDx::SkipFault => e
|
|
509
527
|
Rails.logger.info("Account creation skipped: #{e.result.reason}")
|
|
@@ -525,7 +543,7 @@ task.id #=> "abc123..." (unique task ID)
|
|
|
525
543
|
task.context.email #=> "user@example.com"
|
|
526
544
|
task.context.send_welcome #=> true
|
|
527
545
|
task.result.state #=> "initialized"
|
|
528
|
-
result.status
|
|
546
|
+
task.result.status #=> "success"
|
|
529
547
|
|
|
530
548
|
# Manual execution
|
|
531
549
|
task.execute
|
|
@@ -550,19 +568,15 @@ result.chain #=> Task execution chain
|
|
|
550
568
|
# Context and metadata
|
|
551
569
|
result.context #=> Context with all task data
|
|
552
570
|
result.metadata #=> Hash with execution metadata
|
|
553
|
-
|
|
554
|
-
---
|
|
555
|
-
|
|
556
|
-
url: https://github.com/drexed/cmdx/blob/main/docs/basics/context.md
|
|
557
|
-
---
|
|
571
|
+
```
|
|
558
572
|
|
|
559
573
|
# Basics - Context
|
|
560
574
|
|
|
561
|
-
|
|
575
|
+
Context is your data container for inputs, intermediate values, and outputs. It makes sharing data between tasks effortless.
|
|
562
576
|
|
|
563
577
|
## Assigning Data
|
|
564
578
|
|
|
565
|
-
Context
|
|
579
|
+
Context automatically captures all task inputs, normalizing keys to symbols:
|
|
566
580
|
|
|
567
581
|
```ruby
|
|
568
582
|
# Direct execution
|
|
@@ -572,12 +586,13 @@ CalculateShipping.execute(weight: 2.5, destination: "CA")
|
|
|
572
586
|
CalculateShipping.new(weight: 2.5, "destination" => "CA")
|
|
573
587
|
```
|
|
574
588
|
|
|
575
|
-
|
|
576
|
-
|
|
589
|
+
!!! warning "Important"
|
|
590
|
+
|
|
591
|
+
String keys convert to symbols automatically. Prefer symbols for consistency.
|
|
577
592
|
|
|
578
593
|
## Accessing Data
|
|
579
594
|
|
|
580
|
-
|
|
595
|
+
Access context data using method notation, hash keys, or safe accessors:
|
|
581
596
|
|
|
582
597
|
```ruby
|
|
583
598
|
class CalculateShipping < CMDx::Task
|
|
@@ -600,8 +615,9 @@ class CalculateShipping < CMDx::Task
|
|
|
600
615
|
end
|
|
601
616
|
```
|
|
602
617
|
|
|
603
|
-
|
|
604
|
-
|
|
618
|
+
!!! warning "Important"
|
|
619
|
+
|
|
620
|
+
Undefined attributes return `nil` instead of raising errors—perfect for optional data.
|
|
605
621
|
|
|
606
622
|
## Modifying Context
|
|
607
623
|
|
|
@@ -642,12 +658,13 @@ class CalculateShipping < CMDx::Task
|
|
|
642
658
|
end
|
|
643
659
|
```
|
|
644
660
|
|
|
645
|
-
|
|
646
|
-
|
|
661
|
+
!!! tip
|
|
662
|
+
|
|
663
|
+
Use context for both input values and intermediate results. This creates natural data flow through your task execution pipeline.
|
|
647
664
|
|
|
648
665
|
## Data Sharing
|
|
649
666
|
|
|
650
|
-
|
|
667
|
+
Share context across tasks for seamless data flow:
|
|
651
668
|
|
|
652
669
|
```ruby
|
|
653
670
|
# During execution
|
|
@@ -675,21 +692,17 @@ result = CalculateShipping.execute(destination: "New York, NY")
|
|
|
675
692
|
CreateShippingLabel.execute(result)
|
|
676
693
|
```
|
|
677
694
|
|
|
678
|
-
---
|
|
679
|
-
|
|
680
|
-
url: https://github.com/drexed/cmdx/blob/main/docs/basics/chain.md
|
|
681
|
-
---
|
|
682
|
-
|
|
683
695
|
# Basics - Chain
|
|
684
696
|
|
|
685
|
-
Chains automatically
|
|
697
|
+
Chains automatically track related task executions within a thread. Think of them as execution traces that help you understand what happened and in what order.
|
|
686
698
|
|
|
687
699
|
## Management
|
|
688
700
|
|
|
689
|
-
Each thread maintains its own chain
|
|
701
|
+
Each thread maintains its own isolated chain using thread-local storage.
|
|
690
702
|
|
|
691
|
-
|
|
692
|
-
|
|
703
|
+
!!! warning
|
|
704
|
+
|
|
705
|
+
Chains are thread-local. Don't share chain references across threads—it causes race conditions.
|
|
693
706
|
|
|
694
707
|
```ruby
|
|
695
708
|
# Thread A
|
|
@@ -711,10 +724,11 @@ CMDx::Chain.clear #=> Clears current thread's chain
|
|
|
711
724
|
|
|
712
725
|
## Links
|
|
713
726
|
|
|
714
|
-
|
|
727
|
+
Tasks automatically create or join the current thread's chain:
|
|
728
|
+
|
|
729
|
+
!!! warning "Important"
|
|
715
730
|
|
|
716
|
-
|
|
717
|
-
> Chain creation is automatic and transparent. You don't need to manually manage chain lifecycle.
|
|
731
|
+
Chain management is automatic—no manual lifecycle handling needed.
|
|
718
732
|
|
|
719
733
|
```ruby
|
|
720
734
|
class ImportDataset < CMDx::Task
|
|
@@ -737,7 +751,7 @@ end
|
|
|
737
751
|
|
|
738
752
|
## Inheritance
|
|
739
753
|
|
|
740
|
-
|
|
754
|
+
Subtasks automatically inherit the current thread's chain, building a unified execution trail:
|
|
741
755
|
|
|
742
756
|
```ruby
|
|
743
757
|
class ImportDataset < CMDx::Task
|
|
@@ -762,10 +776,11 @@ chain.results.map { |r| r.task.class }
|
|
|
762
776
|
|
|
763
777
|
## Structure
|
|
764
778
|
|
|
765
|
-
Chains
|
|
779
|
+
Chains expose comprehensive execution information:
|
|
780
|
+
|
|
781
|
+
!!! warning "Important"
|
|
766
782
|
|
|
767
|
-
|
|
768
|
-
> Chain state always reflects the first (outer-most) task result, not individual subtask outcomes. Subtasks maintain their own success/failure states.
|
|
783
|
+
Chain state reflects the first (outermost) task result. Subtasks maintain their own states.
|
|
769
784
|
|
|
770
785
|
```ruby
|
|
771
786
|
result = ImportDataset.execute(dataset_id: 456)
|
|
@@ -786,21 +801,17 @@ chain.results.each_with_index do |result, index|
|
|
|
786
801
|
end
|
|
787
802
|
```
|
|
788
803
|
|
|
789
|
-
---
|
|
790
|
-
|
|
791
|
-
url: https://github.com/drexed/cmdx/blob/main/docs/interruptions/halt.md
|
|
792
|
-
---
|
|
793
|
-
|
|
794
804
|
# Interruptions - Halt
|
|
795
805
|
|
|
796
|
-
|
|
806
|
+
Stop task execution intentionally using `skip!` or `fail!`. Both methods signal clear intent about why execution stopped.
|
|
797
807
|
|
|
798
808
|
## Skipping
|
|
799
809
|
|
|
800
|
-
`skip!`
|
|
810
|
+
Use `skip!` when the task doesn't need to run. It's a no-op, not an error.
|
|
811
|
+
|
|
812
|
+
!!! warning "Important"
|
|
801
813
|
|
|
802
|
-
|
|
803
|
-
> Skipping is a no-op, not a failure or error and are considered successful outcomes.
|
|
814
|
+
Skipped tasks are considered "good" outcomes—they succeeded by doing nothing.
|
|
804
815
|
|
|
805
816
|
```ruby
|
|
806
817
|
class ProcessInventory < CMDx::Task
|
|
@@ -835,7 +846,7 @@ result.reason #=> "Warehouse closed"
|
|
|
835
846
|
|
|
836
847
|
## Failing
|
|
837
848
|
|
|
838
|
-
`fail!`
|
|
849
|
+
Use `fail!` when the task can't complete successfully. It signals controlled, intentional failure:
|
|
839
850
|
|
|
840
851
|
```ruby
|
|
841
852
|
class ProcessRefund < CMDx::Task
|
|
@@ -870,7 +881,7 @@ result.reason #=> "Refund period has expired"
|
|
|
870
881
|
|
|
871
882
|
## Metadata Enrichment
|
|
872
883
|
|
|
873
|
-
|
|
884
|
+
Enrich halt calls with metadata for better debugging and error handling:
|
|
874
885
|
|
|
875
886
|
```ruby
|
|
876
887
|
class ProcessRenewal < CMDx::Task
|
|
@@ -970,7 +981,7 @@ end
|
|
|
970
981
|
|
|
971
982
|
## Best Practices
|
|
972
983
|
|
|
973
|
-
Always
|
|
984
|
+
Always provide a reason for better debugging and clearer exception messages:
|
|
974
985
|
|
|
975
986
|
```ruby
|
|
976
987
|
# Good: Clear, specific reason
|
|
@@ -988,10 +999,11 @@ fail! #=> "Unspecified"
|
|
|
988
999
|
|
|
989
1000
|
## Manual Errors
|
|
990
1001
|
|
|
991
|
-
|
|
1002
|
+
For rare cases, manually add errors before halting:
|
|
992
1003
|
|
|
993
|
-
|
|
994
|
-
|
|
1004
|
+
!!! warning "Important"
|
|
1005
|
+
|
|
1006
|
+
Manual errors don't stop execution—you still need to call `fail!` or `skip!`.
|
|
995
1007
|
|
|
996
1008
|
```ruby
|
|
997
1009
|
class ProcessRenewal < CMDx::Task
|
|
@@ -1006,14 +1018,9 @@ class ProcessRenewal < CMDx::Task
|
|
|
1006
1018
|
end
|
|
1007
1019
|
```
|
|
1008
1020
|
|
|
1009
|
-
---
|
|
1010
|
-
|
|
1011
|
-
url: https://github.com/drexed/cmdx/blob/main/docs/interruptions/faults.md
|
|
1012
|
-
---
|
|
1013
|
-
|
|
1014
1021
|
# Interruptions - Faults
|
|
1015
1022
|
|
|
1016
|
-
Faults are
|
|
1023
|
+
Faults are exceptions raised by `execute!` when tasks halt. They carry rich context about execution state, enabling sophisticated error handling patterns.
|
|
1017
1024
|
|
|
1018
1025
|
## Fault Types
|
|
1019
1026
|
|
|
@@ -1023,8 +1030,9 @@ Faults are exception mechanisms that halt task execution via `skip!` and `fail!`
|
|
|
1023
1030
|
| `CMDx::SkipFault` | `skip!` method | Optional processing, early returns |
|
|
1024
1031
|
| `CMDx::FailFault` | `fail!` method | Validation errors, processing failures |
|
|
1025
1032
|
|
|
1026
|
-
|
|
1027
|
-
|
|
1033
|
+
!!! warning "Important"
|
|
1034
|
+
|
|
1035
|
+
All faults inherit from `CMDx::Fault` and expose result, task, context, and chain data.
|
|
1028
1036
|
|
|
1029
1037
|
## Fault Handling
|
|
1030
1038
|
|
|
@@ -1045,7 +1053,7 @@ end
|
|
|
1045
1053
|
|
|
1046
1054
|
## Data Access
|
|
1047
1055
|
|
|
1048
|
-
|
|
1056
|
+
Access rich execution data from fault exceptions:
|
|
1049
1057
|
|
|
1050
1058
|
```ruby
|
|
1051
1059
|
begin
|
|
@@ -1074,7 +1082,7 @@ end
|
|
|
1074
1082
|
|
|
1075
1083
|
### Task-Specific Matching
|
|
1076
1084
|
|
|
1077
|
-
|
|
1085
|
+
Handle faults only from specific tasks using `for?`:
|
|
1078
1086
|
|
|
1079
1087
|
```ruby
|
|
1080
1088
|
begin
|
|
@@ -1104,7 +1112,7 @@ end
|
|
|
1104
1112
|
|
|
1105
1113
|
## Fault Propagation
|
|
1106
1114
|
|
|
1107
|
-
|
|
1115
|
+
Propagate failures with `throw!` to preserve context and maintain the error chain:
|
|
1108
1116
|
|
|
1109
1117
|
### Basic Propagation
|
|
1110
1118
|
|
|
@@ -1151,7 +1159,7 @@ end
|
|
|
1151
1159
|
|
|
1152
1160
|
## Chain Analysis
|
|
1153
1161
|
|
|
1154
|
-
|
|
1162
|
+
Trace fault origins and propagation through the execution chain:
|
|
1155
1163
|
|
|
1156
1164
|
```ruby
|
|
1157
1165
|
result = DocumentWorkflow.execute(invalid_data)
|
|
@@ -1180,23 +1188,19 @@ if result.failed?
|
|
|
1180
1188
|
end
|
|
1181
1189
|
```
|
|
1182
1190
|
|
|
1183
|
-
---
|
|
1184
|
-
|
|
1185
|
-
url: https://github.com/drexed/cmdx/blob/main/docs/interruptions/exceptions.md
|
|
1186
|
-
---
|
|
1187
|
-
|
|
1188
1191
|
# Interruptions - Exceptions
|
|
1189
1192
|
|
|
1190
|
-
|
|
1193
|
+
Exception handling differs between `execute` and `execute!`. Choose the method that matches your error handling strategy.
|
|
1191
1194
|
|
|
1192
1195
|
## Exception Handling
|
|
1193
1196
|
|
|
1194
|
-
|
|
1195
|
-
|
|
1197
|
+
!!! warning "Important"
|
|
1198
|
+
|
|
1199
|
+
Prefer `skip!` and `fail!` over raising exceptions—they signal intent more clearly.
|
|
1196
1200
|
|
|
1197
1201
|
### Non-bang execution
|
|
1198
1202
|
|
|
1199
|
-
|
|
1203
|
+
Captures all exceptions and returns them as failed results:
|
|
1200
1204
|
|
|
1201
1205
|
```ruby
|
|
1202
1206
|
class CompressDocument < CMDx::Task
|
|
@@ -1214,12 +1218,13 @@ result.reason #=> "[ActiveRecord::NotFoundError] record not found"
|
|
|
1214
1218
|
result.cause #=> <ActiveRecord::NotFoundError>
|
|
1215
1219
|
```
|
|
1216
1220
|
|
|
1217
|
-
|
|
1218
|
-
|
|
1221
|
+
!!! note
|
|
1222
|
+
|
|
1223
|
+
Use `exception_handler` with `execute` to send exceptions to APM tools before they become failed results.
|
|
1219
1224
|
|
|
1220
1225
|
### Bang execution
|
|
1221
1226
|
|
|
1222
|
-
|
|
1227
|
+
Lets exceptions propagate naturally for standard Ruby error handling:
|
|
1223
1228
|
|
|
1224
1229
|
```ruby
|
|
1225
1230
|
class CompressDocument < CMDx::Task
|
|
@@ -1236,21 +1241,17 @@ rescue ActiveRecord::NotFoundError => e
|
|
|
1236
1241
|
end
|
|
1237
1242
|
```
|
|
1238
1243
|
|
|
1239
|
-
---
|
|
1240
|
-
|
|
1241
|
-
url: https://github.com/drexed/cmdx/blob/main/docs/outcomes/result.md
|
|
1242
|
-
---
|
|
1243
|
-
|
|
1244
1244
|
# Outcomes - Result
|
|
1245
1245
|
|
|
1246
|
-
|
|
1246
|
+
Results are your window into task execution. They expose everything: outcome, state, timing, context, and metadata.
|
|
1247
1247
|
|
|
1248
1248
|
## Result Attributes
|
|
1249
1249
|
|
|
1250
|
-
|
|
1250
|
+
Access essential execution information:
|
|
1251
|
+
|
|
1252
|
+
!!! warning "Important"
|
|
1251
1253
|
|
|
1252
|
-
|
|
1253
|
-
> Result objects are immutable after task execution completes and reflect the final state.
|
|
1254
|
+
Results are immutable after execution completes.
|
|
1254
1255
|
|
|
1255
1256
|
```ruby
|
|
1256
1257
|
result = BuildApplication.execute(version: "1.2.3")
|
|
@@ -1272,7 +1273,7 @@ result.metadata #=> { error_code: "BUILD_TOOL.NOT_FOUND" }
|
|
|
1272
1273
|
|
|
1273
1274
|
## Lifecycle Information
|
|
1274
1275
|
|
|
1275
|
-
|
|
1276
|
+
Check execution state and status with predicate methods:
|
|
1276
1277
|
|
|
1277
1278
|
```ruby
|
|
1278
1279
|
result = BuildApplication.execute(version: "1.2.3")
|
|
@@ -1294,7 +1295,7 @@ result.bad? #=> false (skipped or failed)
|
|
|
1294
1295
|
|
|
1295
1296
|
## Outcome Analysis
|
|
1296
1297
|
|
|
1297
|
-
|
|
1298
|
+
Get a unified outcome string combining state and status:
|
|
1298
1299
|
|
|
1299
1300
|
```ruby
|
|
1300
1301
|
result = BuildApplication.execute(version: "1.2.3")
|
|
@@ -1304,7 +1305,7 @@ result.outcome #=> "success" (state and status)
|
|
|
1304
1305
|
|
|
1305
1306
|
## Chain Analysis
|
|
1306
1307
|
|
|
1307
|
-
|
|
1308
|
+
Trace fault origins and propagation:
|
|
1308
1309
|
|
|
1309
1310
|
```ruby
|
|
1310
1311
|
result = DeploymentWorkflow.execute(app_name: "webapp")
|
|
@@ -1345,7 +1346,7 @@ result.chain.results[result.index] == result #=> true
|
|
|
1345
1346
|
|
|
1346
1347
|
## Block Yield
|
|
1347
1348
|
|
|
1348
|
-
|
|
1349
|
+
Execute code with direct result access:
|
|
1349
1350
|
|
|
1350
1351
|
```ruby
|
|
1351
1352
|
BuildApplication.execute(version: "1.2.3") do |result|
|
|
@@ -1361,7 +1362,7 @@ end
|
|
|
1361
1362
|
|
|
1362
1363
|
## Handlers
|
|
1363
1364
|
|
|
1364
|
-
|
|
1365
|
+
Handle outcomes with functional-style methods. Handlers return the result for chaining:
|
|
1365
1366
|
|
|
1366
1367
|
```ruby
|
|
1367
1368
|
result = BuildApplication.execute(version: "1.2.3")
|
|
@@ -1385,10 +1386,11 @@ result
|
|
|
1385
1386
|
|
|
1386
1387
|
## Pattern Matching
|
|
1387
1388
|
|
|
1388
|
-
|
|
1389
|
+
Use Ruby 3.0+ pattern matching for elegant outcome handling:
|
|
1390
|
+
|
|
1391
|
+
!!! warning "Important"
|
|
1389
1392
|
|
|
1390
|
-
|
|
1391
|
-
> Pattern matching requires Ruby 3.0+
|
|
1393
|
+
Pattern matching works with both array and hash deconstruction.
|
|
1392
1394
|
|
|
1393
1395
|
### Array Pattern
|
|
1394
1396
|
|
|
@@ -1433,17 +1435,9 @@ in { runtime: time } if time > performance_threshold
|
|
|
1433
1435
|
end
|
|
1434
1436
|
```
|
|
1435
1437
|
|
|
1436
|
-
---
|
|
1437
|
-
|
|
1438
|
-
url: https://github.com/drexed/cmdx/blob/main/docs/outcomes/states.md
|
|
1439
|
-
---
|
|
1440
|
-
|
|
1441
1438
|
# Outcomes - States
|
|
1442
1439
|
|
|
1443
|
-
States
|
|
1444
|
-
the progress of tasks through their complete execution journey. States provide
|
|
1445
|
-
insight into where a task is in its lifecycle and enable lifecycle-based
|
|
1446
|
-
decision making and monitoring.
|
|
1440
|
+
States track where a task is in its execution lifecycle—from creation through completion or interruption.
|
|
1447
1441
|
|
|
1448
1442
|
## Definitions
|
|
1449
1443
|
|
|
@@ -1467,8 +1461,9 @@ State-Status combinations:
|
|
|
1467
1461
|
|
|
1468
1462
|
## Transitions
|
|
1469
1463
|
|
|
1470
|
-
|
|
1471
|
-
|
|
1464
|
+
!!! danger "Caution"
|
|
1465
|
+
|
|
1466
|
+
States are managed automatically—never modify them manually.
|
|
1472
1467
|
|
|
1473
1468
|
```ruby
|
|
1474
1469
|
# Valid state transition flow
|
|
@@ -1495,7 +1490,7 @@ result.executed? #=> true (complete OR interrupted)
|
|
|
1495
1490
|
|
|
1496
1491
|
## Handlers
|
|
1497
1492
|
|
|
1498
|
-
|
|
1493
|
+
Handle lifecycle events with state-based handlers. Use `handle_executed` for cleanup that runs regardless of outcome:
|
|
1499
1494
|
|
|
1500
1495
|
```ruby
|
|
1501
1496
|
result = ProcessVideoUpload.execute
|
|
@@ -1507,14 +1502,9 @@ result
|
|
|
1507
1502
|
.handle_executed { |result| log_upload_metrics(result) }
|
|
1508
1503
|
```
|
|
1509
1504
|
|
|
1510
|
-
---
|
|
1511
|
-
|
|
1512
|
-
url: https://github.com/drexed/cmdx/blob/main/docs/outcomes/statuses.md
|
|
1513
|
-
---
|
|
1514
|
-
|
|
1515
1505
|
# Outcomes - Statuses
|
|
1516
1506
|
|
|
1517
|
-
Statuses represent the business outcome
|
|
1507
|
+
Statuses represent the business outcome—did the task succeed, skip, or fail? This differs from state, which tracks the execution lifecycle.
|
|
1518
1508
|
|
|
1519
1509
|
## Definitions
|
|
1520
1510
|
|
|
@@ -1526,8 +1516,9 @@ Statuses represent the business outcome of task execution logic, indicating how
|
|
|
1526
1516
|
|
|
1527
1517
|
## Transitions
|
|
1528
1518
|
|
|
1529
|
-
|
|
1530
|
-
|
|
1519
|
+
!!! warning "Important"
|
|
1520
|
+
|
|
1521
|
+
Status transitions are final and unidirectional. Once skipped or failed, tasks can't return to success.
|
|
1531
1522
|
|
|
1532
1523
|
```ruby
|
|
1533
1524
|
# Valid status transitions
|
|
@@ -1560,7 +1551,7 @@ result.bad? #=> true if skipped OR failed (not success)
|
|
|
1560
1551
|
|
|
1561
1552
|
## Handlers
|
|
1562
1553
|
|
|
1563
|
-
|
|
1554
|
+
Branch business logic with status-based handlers. Use `handle_good` and `handle_bad` for success/skip vs failed outcomes:
|
|
1564
1555
|
|
|
1565
1556
|
```ruby
|
|
1566
1557
|
result = ProcessNotification.execute
|
|
@@ -1577,19 +1568,15 @@ result
|
|
|
1577
1568
|
.handle_bad { |result| track_delivery_failure(result) }
|
|
1578
1569
|
```
|
|
1579
1570
|
|
|
1580
|
-
---
|
|
1581
|
-
|
|
1582
|
-
url: https://github.com/drexed/cmdx/blob/main/docs/attributes/definitions.md
|
|
1583
|
-
---
|
|
1584
|
-
|
|
1585
1571
|
# Attributes - Definitions
|
|
1586
1572
|
|
|
1587
|
-
Attributes define
|
|
1573
|
+
Attributes define your task's interface with automatic validation, type coercion, and accessor generation. They're the contract between callers and your business logic.
|
|
1588
1574
|
|
|
1589
1575
|
## Declarations
|
|
1590
1576
|
|
|
1591
|
-
|
|
1592
|
-
|
|
1577
|
+
!!! tip
|
|
1578
|
+
|
|
1579
|
+
Prefer using the `required` and `optional` alias for `attributes` for brevity and to clearly signal intent.
|
|
1593
1580
|
|
|
1594
1581
|
### Optional
|
|
1595
1582
|
|
|
@@ -1658,7 +1645,7 @@ PublishArticle.execute(
|
|
|
1658
1645
|
|
|
1659
1646
|
## Sources
|
|
1660
1647
|
|
|
1661
|
-
Attributes
|
|
1648
|
+
Attributes read from any accessible object—not just context. Use sources to pull data from models, services, or any callable:
|
|
1662
1649
|
|
|
1663
1650
|
### Context
|
|
1664
1651
|
|
|
@@ -1738,10 +1725,11 @@ end
|
|
|
1738
1725
|
|
|
1739
1726
|
## Nesting
|
|
1740
1727
|
|
|
1741
|
-
|
|
1728
|
+
Build complex structures with nested attributes. Children inherit their parent as source and support all attribute options:
|
|
1742
1729
|
|
|
1743
|
-
|
|
1744
|
-
|
|
1730
|
+
!!! note
|
|
1731
|
+
|
|
1732
|
+
Nested attributes support all features: naming, coercions, validations, defaults, and more.
|
|
1745
1733
|
|
|
1746
1734
|
```ruby
|
|
1747
1735
|
class ConfigureServer < CMDx::Task
|
|
@@ -1794,15 +1782,17 @@ ConfigureServer.execute(
|
|
|
1794
1782
|
)
|
|
1795
1783
|
```
|
|
1796
1784
|
|
|
1797
|
-
|
|
1798
|
-
|
|
1785
|
+
!!! warning "Important"
|
|
1786
|
+
|
|
1787
|
+
Child requirements only apply when the parent is provided—perfect for optional structures.
|
|
1799
1788
|
|
|
1800
1789
|
## Error Handling
|
|
1801
1790
|
|
|
1802
|
-
|
|
1791
|
+
Validation failures provide detailed, structured error messages:
|
|
1792
|
+
|
|
1793
|
+
!!! note
|
|
1803
1794
|
|
|
1804
|
-
|
|
1805
|
-
> Nested attributes are only ever evaluated when the parent attribute is available and valid.
|
|
1795
|
+
Nested attributes are only validated when their parent is present and valid.
|
|
1806
1796
|
|
|
1807
1797
|
```ruby
|
|
1808
1798
|
class ConfigureServer < CMDx::Task
|
|
@@ -1852,17 +1842,13 @@ result.metadata #=> {
|
|
|
1852
1842
|
# }
|
|
1853
1843
|
```
|
|
1854
1844
|
|
|
1855
|
-
---
|
|
1856
|
-
|
|
1857
|
-
url: https://github.com/drexed/cmdx/blob/main/docs/attributes/naming.md
|
|
1858
|
-
---
|
|
1859
|
-
|
|
1860
1845
|
# Attributes - Naming
|
|
1861
1846
|
|
|
1862
|
-
|
|
1847
|
+
Customize accessor method names to avoid conflicts and improve clarity. Affixing changes only the generated methods—not the original attribute names.
|
|
1848
|
+
|
|
1849
|
+
!!! note
|
|
1863
1850
|
|
|
1864
|
-
|
|
1865
|
-
> Affixing modifies only the generated accessor method names within tasks.
|
|
1851
|
+
Use naming when attributes conflict with existing methods or need better clarity in your code.
|
|
1866
1852
|
|
|
1867
1853
|
## Prefix
|
|
1868
1854
|
|
|
@@ -1925,16 +1911,11 @@ end
|
|
|
1925
1911
|
ScheduleMaintenance.execute(scheduled_at: DateTime.new(2024, 12, 15, 2, 0, 0))
|
|
1926
1912
|
```
|
|
1927
1913
|
|
|
1928
|
-
---
|
|
1929
|
-
|
|
1930
|
-
url: https://github.com/drexed/cmdx/blob/main/docs/attributes/coercions.md
|
|
1931
|
-
---
|
|
1932
|
-
|
|
1933
1914
|
# Attributes - Coercions
|
|
1934
1915
|
|
|
1935
|
-
|
|
1916
|
+
Automatically convert inputs to expected types. Coercions handle everything from simple string-to-integer conversions to JSON parsing.
|
|
1936
1917
|
|
|
1937
|
-
|
|
1918
|
+
See [Global Configuration](https://github.com/drexed/cmdx/blob/main/docs/getting_started.md#coercions) for custom coercion setup.
|
|
1938
1919
|
|
|
1939
1920
|
## Usage
|
|
1940
1921
|
|
|
@@ -1965,8 +1946,9 @@ ParseMetrics.execute(
|
|
|
1965
1946
|
)
|
|
1966
1947
|
```
|
|
1967
1948
|
|
|
1968
|
-
|
|
1969
|
-
|
|
1949
|
+
!!! tip
|
|
1950
|
+
|
|
1951
|
+
Specify multiple coercion types for attributes that could be a variety of value formats. CMDx attempts each type in order until one succeeds.
|
|
1970
1952
|
|
|
1971
1953
|
## Built-in Coercions
|
|
1972
1954
|
|
|
@@ -1988,8 +1970,9 @@ ParseMetrics.execute(
|
|
|
1988
1970
|
|
|
1989
1971
|
## Declarations
|
|
1990
1972
|
|
|
1991
|
-
|
|
1992
|
-
|
|
1973
|
+
!!! warning "Important"
|
|
1974
|
+
|
|
1975
|
+
Custom coercions must raise `CMDx::CoercionError` with a descriptive message.
|
|
1993
1976
|
|
|
1994
1977
|
### Proc or Lambda
|
|
1995
1978
|
|
|
@@ -2039,10 +2022,11 @@ end
|
|
|
2039
2022
|
|
|
2040
2023
|
## Removals
|
|
2041
2024
|
|
|
2042
|
-
Remove
|
|
2025
|
+
Remove unwanted coercions:
|
|
2026
|
+
|
|
2027
|
+
!!! warning
|
|
2043
2028
|
|
|
2044
|
-
|
|
2045
|
-
> Only one removal operation is allowed per `deregister` call. Multiple removals require separate calls.
|
|
2029
|
+
Each `deregister` call removes one coercion. Use multiple calls for batch removals.
|
|
2046
2030
|
|
|
2047
2031
|
```ruby
|
|
2048
2032
|
class TransformCoordinates < CMDx::Task
|
|
@@ -2083,16 +2067,11 @@ result.metadata #=> {
|
|
|
2083
2067
|
# }
|
|
2084
2068
|
```
|
|
2085
2069
|
|
|
2086
|
-
---
|
|
2087
|
-
|
|
2088
|
-
url: https://github.com/drexed/cmdx/blob/main/docs/attributes/validations.md
|
|
2089
|
-
---
|
|
2090
|
-
|
|
2091
2070
|
# Attributes - Validations
|
|
2092
2071
|
|
|
2093
|
-
|
|
2072
|
+
Ensure inputs meet requirements before execution. Validations run after coercions, giving you declarative data integrity checks.
|
|
2094
2073
|
|
|
2095
|
-
|
|
2074
|
+
See [Global Configuration](https://github.com/drexed/cmdx/blob/main/docs/getting_started.md#validators) for custom validator setup.
|
|
2096
2075
|
|
|
2097
2076
|
## Usage
|
|
2098
2077
|
|
|
@@ -2128,8 +2107,9 @@ ProcessSubscription.execute(
|
|
|
2128
2107
|
)
|
|
2129
2108
|
```
|
|
2130
2109
|
|
|
2131
|
-
|
|
2132
|
-
|
|
2110
|
+
!!! tip
|
|
2111
|
+
|
|
2112
|
+
Validations run after coercions, so you can validate the final coerced values rather than raw input.
|
|
2133
2113
|
|
|
2134
2114
|
## Built-in Validators
|
|
2135
2115
|
|
|
@@ -2284,8 +2264,9 @@ end
|
|
|
2284
2264
|
|
|
2285
2265
|
## Declarations
|
|
2286
2266
|
|
|
2287
|
-
|
|
2288
|
-
|
|
2267
|
+
!!! warning "Important"
|
|
2268
|
+
|
|
2269
|
+
Custom validators must raise `CMDx::ValidationError` with a descriptive message.
|
|
2289
2270
|
|
|
2290
2271
|
### Proc or Lambda
|
|
2291
2272
|
|
|
@@ -2331,10 +2312,11 @@ end
|
|
|
2331
2312
|
|
|
2332
2313
|
## Removals
|
|
2333
2314
|
|
|
2334
|
-
Remove
|
|
2315
|
+
Remove unwanted validators:
|
|
2335
2316
|
|
|
2336
|
-
|
|
2337
|
-
|
|
2317
|
+
!!! warning
|
|
2318
|
+
|
|
2319
|
+
Each `deregister` call removes one validator. Use multiple calls for batch removals.
|
|
2338
2320
|
|
|
2339
2321
|
```ruby
|
|
2340
2322
|
class SetupApplication < CMDx::Task
|
|
@@ -2344,7 +2326,7 @@ end
|
|
|
2344
2326
|
|
|
2345
2327
|
## Error Handling
|
|
2346
2328
|
|
|
2347
|
-
Validation failures provide detailed
|
|
2329
|
+
Validation failures provide detailed, structured error messages:
|
|
2348
2330
|
|
|
2349
2331
|
```ruby
|
|
2350
2332
|
class CreateProject < CMDx::Task
|
|
@@ -2381,18 +2363,13 @@ result.metadata #=> {
|
|
|
2381
2363
|
# }
|
|
2382
2364
|
```
|
|
2383
2365
|
|
|
2384
|
-
---
|
|
2385
|
-
|
|
2386
|
-
url: https://github.com/drexed/cmdx/blob/main/docs/attributes/defaults.md
|
|
2387
|
-
---
|
|
2388
|
-
|
|
2389
2366
|
# Attributes - Defaults
|
|
2390
2367
|
|
|
2391
|
-
|
|
2368
|
+
Provide fallback values for optional attributes. Defaults kick in when values aren't provided or are `nil`.
|
|
2392
2369
|
|
|
2393
2370
|
## Declarations
|
|
2394
2371
|
|
|
2395
|
-
Defaults
|
|
2372
|
+
Defaults work seamlessly with coercions, validations, and nested attributes:
|
|
2396
2373
|
|
|
2397
2374
|
### Static Values
|
|
2398
2375
|
|
|
@@ -2452,7 +2429,7 @@ end
|
|
|
2452
2429
|
|
|
2453
2430
|
## Coercions and Validations
|
|
2454
2431
|
|
|
2455
|
-
Defaults
|
|
2432
|
+
Defaults follow the same coercion and validation rules as provided values:
|
|
2456
2433
|
|
|
2457
2434
|
```ruby
|
|
2458
2435
|
class ScheduleBackup < CMDx::Task
|
|
@@ -2464,14 +2441,9 @@ class ScheduleBackup < CMDx::Task
|
|
|
2464
2441
|
end
|
|
2465
2442
|
```
|
|
2466
2443
|
|
|
2467
|
-
---
|
|
2468
|
-
|
|
2469
|
-
url: https://github.com/drexed/cmdx/blob/main/docs/attributes/transformations.md
|
|
2470
|
-
---
|
|
2471
|
-
|
|
2472
2444
|
# Attributes - Transformations
|
|
2473
2445
|
|
|
2474
|
-
|
|
2446
|
+
Modify attribute values after coercion but before validation. Perfect for normalization, formatting, and data cleanup.
|
|
2475
2447
|
|
|
2476
2448
|
## Declarations
|
|
2477
2449
|
|
|
@@ -2521,7 +2493,7 @@ end
|
|
|
2521
2493
|
|
|
2522
2494
|
## Validations
|
|
2523
2495
|
|
|
2524
|
-
|
|
2496
|
+
Validations run on transformed values, ensuring data consistency:
|
|
2525
2497
|
|
|
2526
2498
|
```ruby
|
|
2527
2499
|
class ScheduleBackup < CMDx::Task
|
|
@@ -2533,39 +2505,30 @@ class ScheduleBackup < CMDx::Task
|
|
|
2533
2505
|
end
|
|
2534
2506
|
```
|
|
2535
2507
|
|
|
2536
|
-
---
|
|
2537
|
-
|
|
2538
|
-
- **Prev:** [Attributes - Defaults](defaults.md)
|
|
2539
|
-
- **Next:** [Callbacks](../callbacks.md)
|
|
2540
|
-
|
|
2541
|
-
---
|
|
2542
|
-
|
|
2543
|
-
url: https://github.com/drexed/cmdx/blob/main/docs/callbacks.md
|
|
2544
|
-
---
|
|
2545
|
-
|
|
2546
2508
|
# Callbacks
|
|
2547
2509
|
|
|
2548
|
-
|
|
2510
|
+
Run custom logic at specific points during task execution. Callbacks have full access to task context and results, making them perfect for logging, notifications, cleanup, and more.
|
|
2549
2511
|
|
|
2550
|
-
|
|
2512
|
+
See [Global Configuration](https://github.com/drexed/cmdx/blob/main/docs/getting_started.md#callbacks) for framework-wide callback setup.
|
|
2551
2513
|
|
|
2552
|
-
|
|
2553
|
-
|
|
2514
|
+
!!! warning "Important"
|
|
2515
|
+
|
|
2516
|
+
Callbacks execute in declaration order (FIFO). Multiple callbacks of the same type run sequentially.
|
|
2554
2517
|
|
|
2555
2518
|
## Available Callbacks
|
|
2556
2519
|
|
|
2557
|
-
Callbacks execute in
|
|
2520
|
+
Callbacks execute in a predictable lifecycle order:
|
|
2558
2521
|
|
|
2559
2522
|
```ruby
|
|
2560
2523
|
1. before_validation # Pre-validation setup
|
|
2561
|
-
2. before_execution #
|
|
2524
|
+
2. before_execution # Prepare for execution
|
|
2562
2525
|
|
|
2563
|
-
# --- Task#work
|
|
2526
|
+
# --- Task#work executes ---
|
|
2564
2527
|
|
|
2565
|
-
3. on_[complete|interrupted] #
|
|
2566
|
-
4. on_executed #
|
|
2567
|
-
5. on_[success|skipped|failed] #
|
|
2568
|
-
6. on_[good|bad] #
|
|
2528
|
+
3. on_[complete|interrupted] # State-based (execution lifecycle)
|
|
2529
|
+
4. on_executed # Always runs after work completes
|
|
2530
|
+
5. on_[success|skipped|failed] # Status-based (business outcome)
|
|
2531
|
+
6. on_[good|bad] # Outcome-based (success/skip vs fail)
|
|
2569
2532
|
```
|
|
2570
2533
|
|
|
2571
2534
|
## Declarations
|
|
@@ -2617,7 +2580,7 @@ end
|
|
|
2617
2580
|
|
|
2618
2581
|
### Class or Module
|
|
2619
2582
|
|
|
2620
|
-
Implement reusable callback logic in dedicated classes:
|
|
2583
|
+
Implement reusable callback logic in dedicated modules and classes:
|
|
2621
2584
|
|
|
2622
2585
|
```ruby
|
|
2623
2586
|
class BookingConfirmationCallback
|
|
@@ -2684,10 +2647,11 @@ end
|
|
|
2684
2647
|
|
|
2685
2648
|
## Callback Removal
|
|
2686
2649
|
|
|
2687
|
-
Remove callbacks
|
|
2650
|
+
Remove unwanted callbacks dynamically:
|
|
2651
|
+
|
|
2652
|
+
!!! warning "Important"
|
|
2688
2653
|
|
|
2689
|
-
|
|
2690
|
-
> Only one removal operation is allowed per `deregister` call. Multiple removals require separate calls.
|
|
2654
|
+
Each `deregister` call removes one callback. Use multiple calls for batch removals.
|
|
2691
2655
|
|
|
2692
2656
|
```ruby
|
|
2693
2657
|
class ProcessBooking < CMDx::Task
|
|
@@ -2699,23 +2663,19 @@ class ProcessBooking < CMDx::Task
|
|
|
2699
2663
|
end
|
|
2700
2664
|
```
|
|
2701
2665
|
|
|
2702
|
-
---
|
|
2703
|
-
|
|
2704
|
-
url: https://github.com/drexed/cmdx/blob/main/docs/middlewares.md
|
|
2705
|
-
---
|
|
2706
|
-
|
|
2707
2666
|
# Middlewares
|
|
2708
2667
|
|
|
2709
|
-
|
|
2668
|
+
Wrap task execution with middleware for cross-cutting concerns like authentication, caching, timeouts, and monitoring. Think Rack middleware, but for your business logic.
|
|
2710
2669
|
|
|
2711
|
-
|
|
2670
|
+
See [Global Configuration](https://github.com/drexed/cmdx/blob/main/docs/getting_started.md#middlewares) for framework-wide setup.
|
|
2712
2671
|
|
|
2713
|
-
## Order
|
|
2672
|
+
## Execution Order
|
|
2714
2673
|
|
|
2715
|
-
Middleware
|
|
2674
|
+
Middleware wraps task execution in layers, like an onion:
|
|
2716
2675
|
|
|
2717
|
-
|
|
2718
|
-
|
|
2676
|
+
!!! note
|
|
2677
|
+
|
|
2678
|
+
First registered = outermost wrapper. They execute in registration order.
|
|
2719
2679
|
|
|
2720
2680
|
```ruby
|
|
2721
2681
|
class ProcessCampaign < CMDx::Task
|
|
@@ -2791,10 +2751,11 @@ end
|
|
|
2791
2751
|
|
|
2792
2752
|
## Removals
|
|
2793
2753
|
|
|
2794
|
-
|
|
2754
|
+
Remove class or module-based middleware globally or per-task:
|
|
2755
|
+
|
|
2756
|
+
!!! warning
|
|
2795
2757
|
|
|
2796
|
-
|
|
2797
|
-
> Only one removal operation is allowed per `deregister` call. Multiple removals require separate calls.
|
|
2758
|
+
Each `deregister` call removes one middleware. Use multiple calls for batch removals.
|
|
2798
2759
|
|
|
2799
2760
|
```ruby
|
|
2800
2761
|
class ProcessCampaign < CMDx::Task
|
|
@@ -2807,7 +2768,7 @@ end
|
|
|
2807
2768
|
|
|
2808
2769
|
### Timeout
|
|
2809
2770
|
|
|
2810
|
-
|
|
2771
|
+
Prevent tasks from running too long:
|
|
2811
2772
|
|
|
2812
2773
|
```ruby
|
|
2813
2774
|
class ProcessReport < CMDx::Task
|
|
@@ -2843,7 +2804,7 @@ result.metadata #=> { limit: 3 }
|
|
|
2843
2804
|
|
|
2844
2805
|
### Correlate
|
|
2845
2806
|
|
|
2846
|
-
|
|
2807
|
+
Add correlation IDs for distributed tracing and request tracking:
|
|
2847
2808
|
|
|
2848
2809
|
```ruby
|
|
2849
2810
|
class ProcessExport < CMDx::Task
|
|
@@ -2873,8 +2834,7 @@ result.metadata #=> { correlation_id: "550e8400-e29b-41d4-a716-446655440000" }
|
|
|
2873
2834
|
|
|
2874
2835
|
### Runtime
|
|
2875
2836
|
|
|
2876
|
-
|
|
2877
|
-
The calculation uses a monotonic clock and the time is returned in milliseconds.
|
|
2837
|
+
Track task execution time in milliseconds using a monotonic clock:
|
|
2878
2838
|
|
|
2879
2839
|
```ruby
|
|
2880
2840
|
class PerformanceMonitoringCheck
|
|
@@ -2895,18 +2855,13 @@ result = ProcessExport.execute
|
|
|
2895
2855
|
result.metadata #=> { runtime: 1247 } (ms)
|
|
2896
2856
|
```
|
|
2897
2857
|
|
|
2898
|
-
---
|
|
2899
|
-
|
|
2900
|
-
url: https://github.com/drexed/cmdx/blob/main/docs/logging.md
|
|
2901
|
-
---
|
|
2902
|
-
|
|
2903
2858
|
# Logging
|
|
2904
2859
|
|
|
2905
|
-
CMDx
|
|
2860
|
+
CMDx automatically logs every task execution with structured data, making debugging and monitoring effortless. Choose from multiple formatters to match your logging infrastructure.
|
|
2906
2861
|
|
|
2907
2862
|
## Formatters
|
|
2908
2863
|
|
|
2909
|
-
|
|
2864
|
+
Choose the format that works best for your logging system:
|
|
2910
2865
|
|
|
2911
2866
|
| Formatter | Use Case | Output Style |
|
|
2912
2867
|
|-----------|----------|--------------|
|
|
@@ -2936,12 +2891,13 @@ E, [2022-07-17T18:43:15.000000 #3784] ERROR -- BillingWorkflow:
|
|
|
2936
2891
|
index=3 chain_id="018c2b95-b764-7615-a924-cc5b910ed1e5" type="Task" class="BillingWorkflow" state="interrupted" status="failed" caused_failure={index: 2, class: "CalculateTax", status: "failed"} threw_failure={index: 1, class: "ValidateCustomer", status: "failed"}
|
|
2937
2892
|
```
|
|
2938
2893
|
|
|
2939
|
-
|
|
2940
|
-
|
|
2894
|
+
!!! tip
|
|
2895
|
+
|
|
2896
|
+
Use logging as a low-level event stream to track all tasks in a request. Combine with correlation for powerful distributed tracing.
|
|
2941
2897
|
|
|
2942
2898
|
## Structure
|
|
2943
2899
|
|
|
2944
|
-
|
|
2900
|
+
Every log entry includes rich metadata. Available fields depend on execution context and outcome.
|
|
2945
2901
|
|
|
2946
2902
|
### Core Fields
|
|
2947
2903
|
|
|
@@ -2982,7 +2938,7 @@ All log entries include comprehensive execution metadata. Field availability dep
|
|
|
2982
2938
|
|
|
2983
2939
|
## Usage
|
|
2984
2940
|
|
|
2985
|
-
|
|
2941
|
+
Access the framework logger directly within tasks:
|
|
2986
2942
|
|
|
2987
2943
|
```ruby
|
|
2988
2944
|
class ProcessSubscription < CMDx::Task
|
|
@@ -2994,18 +2950,13 @@ class ProcessSubscription < CMDx::Task
|
|
|
2994
2950
|
end
|
|
2995
2951
|
```
|
|
2996
2952
|
|
|
2997
|
-
---
|
|
2998
|
-
|
|
2999
|
-
url: https://github.com/drexed/cmdx/blob/main/docs/internationalization.md
|
|
3000
|
-
---
|
|
3001
|
-
|
|
3002
2953
|
# Internationalization (i18n)
|
|
3003
2954
|
|
|
3004
|
-
CMDx
|
|
2955
|
+
CMDx supports 90+ languages out of the box for all error messages, validations, coercions, and faults. Error messages automatically adapt to the current `I18n.locale`, making it easy to build applications for global audiences.
|
|
3005
2956
|
|
|
3006
|
-
##
|
|
2957
|
+
## Usage
|
|
3007
2958
|
|
|
3008
|
-
|
|
2959
|
+
All error messages are automatically localized based on your current locale:
|
|
3009
2960
|
|
|
3010
2961
|
```ruby
|
|
3011
2962
|
class ProcessQuote < CMDx::Task
|
|
@@ -3024,11 +2975,11 @@ end
|
|
|
3024
2975
|
|
|
3025
2976
|
## Configuration
|
|
3026
2977
|
|
|
3027
|
-
|
|
2978
|
+
CMDx uses the `I18n` gem for localization. In Rails, locales load automatically.
|
|
3028
2979
|
|
|
3029
|
-
###
|
|
2980
|
+
### Copy Locale Files
|
|
3030
2981
|
|
|
3031
|
-
|
|
2982
|
+
Copy locale files to your Rails application's `config/locales` directory:
|
|
3032
2983
|
|
|
3033
2984
|
```bash
|
|
3034
2985
|
rails generate cmdx:locale [LOCALE]
|
|
@@ -3126,23 +3077,141 @@ rails generate cmdx:locale fr
|
|
|
3126
3077
|
- zh-TW - Chinese (Traditional)
|
|
3127
3078
|
- zh-YUE - Chinese (Yue)
|
|
3128
3079
|
|
|
3129
|
-
|
|
3080
|
+
# Retries
|
|
3081
|
+
|
|
3082
|
+
CMDx provides automatic retry functionality for tasks that encounter transient failures. This is essential for handling temporary issues like network timeouts, rate limits, or database locks without manual intervention.
|
|
3130
3083
|
|
|
3131
|
-
|
|
3132
|
-
|
|
3084
|
+
## Basic Usage
|
|
3085
|
+
|
|
3086
|
+
Configure retries upto n attempts without any delay.
|
|
3087
|
+
|
|
3088
|
+
```ruby
|
|
3089
|
+
class FetchExternalData < CMDx::Task
|
|
3090
|
+
settings retries: 3
|
|
3091
|
+
|
|
3092
|
+
def work
|
|
3093
|
+
response = HTTParty.get("https://api.example.com/data")
|
|
3094
|
+
context.data = response.parsed_response
|
|
3095
|
+
end
|
|
3096
|
+
end
|
|
3097
|
+
```
|
|
3098
|
+
|
|
3099
|
+
When an exception occurs during execution, CMDx automatically retries up to the configured limit.
|
|
3100
|
+
|
|
3101
|
+
## Selective Retries
|
|
3102
|
+
|
|
3103
|
+
By default, CMDx retries on `StandardError` and its subclasses. Narrow this to specific exception types:
|
|
3104
|
+
|
|
3105
|
+
```ruby
|
|
3106
|
+
class ProcessPayment < CMDx::Task
|
|
3107
|
+
settings retries: 5, retry_on: [Stripe::RateLimitError, Net::ReadTimeout]
|
|
3108
|
+
|
|
3109
|
+
def work
|
|
3110
|
+
# Your logic here...
|
|
3111
|
+
end
|
|
3112
|
+
end
|
|
3113
|
+
```
|
|
3114
|
+
|
|
3115
|
+
!!! warning "Important"
|
|
3116
|
+
|
|
3117
|
+
Only exceptions matching the `retry_on` configuration will trigger retries. Uncaught exceptions immediately fail the task.
|
|
3118
|
+
|
|
3119
|
+
## Retry Jitter
|
|
3120
|
+
|
|
3121
|
+
Add delays between retry attempts to avoid overwhelming external services or to implement exponential backoff strategies.
|
|
3122
|
+
|
|
3123
|
+
### Fixed Value
|
|
3124
|
+
|
|
3125
|
+
Use a numeric value to calculate linear delay (`jitter * current_retry`):
|
|
3126
|
+
|
|
3127
|
+
```ruby
|
|
3128
|
+
class ImportRecords < CMDx::Task
|
|
3129
|
+
settings retries: 3, retry_jitter: 0.5
|
|
3130
|
+
|
|
3131
|
+
def work
|
|
3132
|
+
# Delays: 0s, 0.5s (retry 1), 1.0s (retry 2), 1.5s (retry 3)
|
|
3133
|
+
context.records = ExternalAPI.fetch_records
|
|
3134
|
+
end
|
|
3135
|
+
end
|
|
3136
|
+
```
|
|
3137
|
+
|
|
3138
|
+
### Symbol References
|
|
3139
|
+
|
|
3140
|
+
Define an instance method for custom delay logic:
|
|
3141
|
+
|
|
3142
|
+
```ruby
|
|
3143
|
+
class SyncInventory < CMDx::Task
|
|
3144
|
+
settings retries: 5, retry_jitter: :exponential_backoff
|
|
3145
|
+
|
|
3146
|
+
def work
|
|
3147
|
+
context.inventory = InventoryAPI.sync
|
|
3148
|
+
end
|
|
3149
|
+
|
|
3150
|
+
private
|
|
3151
|
+
|
|
3152
|
+
def exponential_backoff(current_retry)
|
|
3153
|
+
2 ** current_retry # 2s, 4s, 8s, 16s, 32s
|
|
3154
|
+
end
|
|
3155
|
+
end
|
|
3156
|
+
```
|
|
3157
|
+
|
|
3158
|
+
### Proc or Lambda
|
|
3159
|
+
|
|
3160
|
+
Pass a proc for inline delay calculations:
|
|
3161
|
+
|
|
3162
|
+
```ruby
|
|
3163
|
+
class PollJobStatus < CMDx::Task
|
|
3164
|
+
# Proc
|
|
3165
|
+
settings retries: 10, retry_jitter: proc { |retry_count| [retry_count * 0.5, 5.0].min }
|
|
3166
|
+
|
|
3167
|
+
# Lambda
|
|
3168
|
+
settings retries: 10, retry_jitter: ->(retry_count) { [retry_count * 0.5, 5.0].min }
|
|
3169
|
+
|
|
3170
|
+
def work
|
|
3171
|
+
# Delays: 0.5s, 1.0s, 1.5s, 2.0s, 2.5s, 3.0s, 3.5s, 4.0s, 4.5s, 5.0s (capped)
|
|
3172
|
+
context.status = JobAPI.check_status(context.job_id)
|
|
3173
|
+
end
|
|
3174
|
+
end
|
|
3175
|
+
```
|
|
3176
|
+
|
|
3177
|
+
### Class or Module
|
|
3178
|
+
|
|
3179
|
+
Implement reusable delay logic in dedicated modules and classes:
|
|
3180
|
+
|
|
3181
|
+
```ruby
|
|
3182
|
+
class ExponentialBackoff
|
|
3183
|
+
def call(task, retry_count)
|
|
3184
|
+
base_delay = task.context.base_delay || 1.0
|
|
3185
|
+
[base_delay * (2 ** retry_count), 60.0].min
|
|
3186
|
+
end
|
|
3187
|
+
end
|
|
3188
|
+
|
|
3189
|
+
class FetchUserProfile < CMDx::Task
|
|
3190
|
+
# Class or Module
|
|
3191
|
+
settings retries: 4, retry_jitter: ExponentialBackoff
|
|
3192
|
+
|
|
3193
|
+
# Instance
|
|
3194
|
+
settings retries: 4, retry_jitter: ExponentialBackoff.new
|
|
3195
|
+
|
|
3196
|
+
def work
|
|
3197
|
+
# Your logic here...
|
|
3198
|
+
end
|
|
3199
|
+
end
|
|
3200
|
+
```
|
|
3133
3201
|
|
|
3134
3202
|
# Task Deprecation
|
|
3135
3203
|
|
|
3136
|
-
|
|
3204
|
+
Manage legacy tasks gracefully with built-in deprecation support. Choose how to handle deprecated tasks—log warnings for awareness, issue Ruby warnings for development, or prevent execution entirely.
|
|
3137
3205
|
|
|
3138
3206
|
## Modes
|
|
3139
3207
|
|
|
3140
3208
|
### Raise
|
|
3141
3209
|
|
|
3142
|
-
|
|
3210
|
+
Prevent task execution completely. Perfect for tasks that must no longer run.
|
|
3211
|
+
|
|
3212
|
+
!!! warning
|
|
3143
3213
|
|
|
3144
|
-
|
|
3145
|
-
> Use `:raise` mode carefully in production environments as it will break existing workflows immediately.
|
|
3214
|
+
Use `:raise` mode carefully—it will break existing workflows immediately.
|
|
3146
3215
|
|
|
3147
3216
|
```ruby
|
|
3148
3217
|
class ProcessObsoleteAPI < CMDx::Task
|
|
@@ -3159,7 +3228,7 @@ result = ProcessObsoleteAPI.execute
|
|
|
3159
3228
|
|
|
3160
3229
|
### Log
|
|
3161
3230
|
|
|
3162
|
-
|
|
3231
|
+
Allow execution while tracking deprecation in logs. Ideal for gradual migrations.
|
|
3163
3232
|
|
|
3164
3233
|
```ruby
|
|
3165
3234
|
class ProcessLegacyFormat < CMDx::Task
|
|
@@ -3182,7 +3251,7 @@ result.successful? #=> true
|
|
|
3182
3251
|
|
|
3183
3252
|
### Warn
|
|
3184
3253
|
|
|
3185
|
-
|
|
3254
|
+
Issue Ruby warnings visible during development and testing. Keeps production logs clean while alerting developers.
|
|
3186
3255
|
|
|
3187
3256
|
```ruby
|
|
3188
3257
|
class ProcessOldData < CMDx::Task
|
|
@@ -3276,21 +3345,17 @@ class OutdatedConnector < CMDx::Task
|
|
|
3276
3345
|
end
|
|
3277
3346
|
```
|
|
3278
3347
|
|
|
3279
|
-
---
|
|
3280
|
-
|
|
3281
|
-
url: https://github.com/drexed/cmdx/blob/main/docs/workflows.md
|
|
3282
|
-
---
|
|
3283
|
-
|
|
3284
3348
|
# Workflows
|
|
3285
3349
|
|
|
3286
|
-
|
|
3350
|
+
Compose multiple tasks into powerful, sequential pipelines. Workflows provide a declarative way to build complex business processes with conditional execution, shared context, and flexible error handling.
|
|
3287
3351
|
|
|
3288
3352
|
## Declarations
|
|
3289
3353
|
|
|
3290
|
-
Tasks
|
|
3354
|
+
Tasks run in declaration order (FIFO), sharing a common context across the pipeline.
|
|
3355
|
+
|
|
3356
|
+
!!! warning
|
|
3291
3357
|
|
|
3292
|
-
|
|
3293
|
-
> Do **NOT** define a `work` method in workflow tasks. The included module automatically provides the execution logic.
|
|
3358
|
+
Don't define a `work` method in workflows—the module handles execution automatically.
|
|
3294
3359
|
|
|
3295
3360
|
### Task
|
|
3296
3361
|
|
|
@@ -3305,15 +3370,17 @@ class OnboardingWorkflow < CMDx::Task
|
|
|
3305
3370
|
end
|
|
3306
3371
|
```
|
|
3307
3372
|
|
|
3308
|
-
|
|
3309
|
-
|
|
3373
|
+
!!! tip
|
|
3374
|
+
|
|
3375
|
+
Execute tasks in parallel via the [cmdx-parallel](https://github.com/drexed/cmdx-parallel) gem.
|
|
3310
3376
|
|
|
3311
3377
|
### Group
|
|
3312
3378
|
|
|
3313
|
-
Group related tasks
|
|
3379
|
+
Group related tasks to share configuration:
|
|
3314
3380
|
|
|
3315
|
-
|
|
3316
|
-
|
|
3381
|
+
!!! warning "Important"
|
|
3382
|
+
|
|
3383
|
+
Settings and conditionals apply to all tasks in the group.
|
|
3317
3384
|
|
|
3318
3385
|
```ruby
|
|
3319
3386
|
class ContentModerationWorkflow < CMDx::Task
|
|
@@ -3376,9 +3443,7 @@ end
|
|
|
3376
3443
|
|
|
3377
3444
|
## Halt Behavior
|
|
3378
3445
|
|
|
3379
|
-
By default skipped tasks
|
|
3380
|
-
This is configurable via global and task level breakpoint settings. Task and group configurations
|
|
3381
|
-
can be used together within a workflow.
|
|
3446
|
+
By default, skipped tasks don't stop the workflow—they're treated as no-ops. Configure breakpoints globally or per-task to customize this behavior.
|
|
3382
3447
|
|
|
3383
3448
|
```ruby
|
|
3384
3449
|
class AnalyticsWorkflow < CMDx::Task
|
|
@@ -3434,7 +3499,7 @@ end
|
|
|
3434
3499
|
|
|
3435
3500
|
## Nested Workflows
|
|
3436
3501
|
|
|
3437
|
-
|
|
3502
|
+
Build hierarchical workflows by composing workflows within workflows:
|
|
3438
3503
|
|
|
3439
3504
|
```ruby
|
|
3440
3505
|
class EmailPreparationWorkflow < CMDx::Task
|
|
@@ -3461,10 +3526,11 @@ end
|
|
|
3461
3526
|
|
|
3462
3527
|
## Parallel Execution
|
|
3463
3528
|
|
|
3464
|
-
|
|
3529
|
+
Run tasks concurrently using the [Parallel](https://github.com/grosser/parallel) gem. It automatically uses all available processors for maximum throughput.
|
|
3530
|
+
|
|
3531
|
+
!!! warning
|
|
3465
3532
|
|
|
3466
|
-
|
|
3467
|
-
> Context cannot be modified during parallel execution. Ensure that all required data is preloaded into the context before parallelization begins.
|
|
3533
|
+
Context is read-only during parallel execution. Load all required data beforehand.
|
|
3468
3534
|
|
|
3469
3535
|
```ruby
|
|
3470
3536
|
class SendWelcomeNotifications < CMDx::Task
|
|
@@ -3502,17 +3568,13 @@ class SendNotifications < CMDx::Task
|
|
|
3502
3568
|
end
|
|
3503
3569
|
```
|
|
3504
3570
|
|
|
3505
|
-
|
|
3506
|
-
> Use **present tense verbs + pluralized noun** for workflow task names, eg: `SendNotifications`, `DownloadFiles`, `ValidateDocuments`
|
|
3507
|
-
|
|
3508
|
-
---
|
|
3571
|
+
!!! tip
|
|
3509
3572
|
|
|
3510
|
-
|
|
3511
|
-
---
|
|
3573
|
+
Use **present tense verbs + pluralized noun** for workflow task names, eg: `SendNotifications`, `DownloadFiles`, `ValidateDocuments`
|
|
3512
3574
|
|
|
3513
3575
|
# Tips and Tricks
|
|
3514
3576
|
|
|
3515
|
-
|
|
3577
|
+
Best practices, patterns, and techniques to build maintainable CMDx applications.
|
|
3516
3578
|
|
|
3517
3579
|
## Project Organization
|
|
3518
3580
|
|
|
@@ -3556,7 +3618,7 @@ class TokenGeneration < CMDx::Task; end # ❌ Avoid
|
|
|
3556
3618
|
|
|
3557
3619
|
### Story Telling
|
|
3558
3620
|
|
|
3559
|
-
|
|
3621
|
+
Break down complex logic into descriptive methods that read like a narrative:
|
|
3560
3622
|
|
|
3561
3623
|
```ruby
|
|
3562
3624
|
class ProcessOrder < CMDx::Task
|
|
@@ -3588,7 +3650,7 @@ end
|
|
|
3588
3650
|
|
|
3589
3651
|
### Style Guide
|
|
3590
3652
|
|
|
3591
|
-
Follow
|
|
3653
|
+
Follow this order for consistent, readable tasks:
|
|
3592
3654
|
|
|
3593
3655
|
```ruby
|
|
3594
3656
|
class ExportReport < CMDx::Task
|
|
@@ -3631,7 +3693,7 @@ end
|
|
|
3631
3693
|
|
|
3632
3694
|
## Attribute Options
|
|
3633
3695
|
|
|
3634
|
-
Use
|
|
3696
|
+
Use `with_options` to reduce duplication:
|
|
3635
3697
|
|
|
3636
3698
|
```ruby
|
|
3637
3699
|
class ConfigureCompany < CMDx::Task
|
|
@@ -3657,9 +3719,9 @@ class ConfigureCompany < CMDx::Task
|
|
|
3657
3719
|
end
|
|
3658
3720
|
```
|
|
3659
3721
|
|
|
3660
|
-
##
|
|
3722
|
+
## More Examples
|
|
3661
3723
|
|
|
3662
3724
|
- [Active Record Query Tagging](https://github.com/drexed/cmdx/blob/main/examples/active_record_query_tagging.md)
|
|
3663
3725
|
- [Paper Trail Whatdunnit](https://github.com/drexed/cmdx/blob/main/examples/paper_trail_whatdunnit.md)
|
|
3726
|
+
- [Stoplight Circuit Breaker](https://github.com/drexed/cmdx/blob/main/examples/stoplight_circuit_breaker.md)
|
|
3664
3727
|
|
|
3665
|
-
---
|