cmdx 1.1.2 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/CHANGELOG.md +5 -133
- 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 -67
- 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 -58
- 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/workflows.md
CHANGED
@@ -1,374 +1,190 @@
|
|
1
|
-
#
|
1
|
+
# Workflows
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
Workflows inherit from Task, gaining all task capabilities including callbacks, parameter validation, result tracking, and configuration. The key difference is that workflows coordinate other tasks rather than implementing business logic directly.
|
3
|
+
Workflow orchestrates sequential execution of multiple tasks in a linear pipeline. Workflows provide a declarative DSL for composing complex business logic from individual task components, with support for conditional execution, context propagation, and configurable halt behavior.
|
6
4
|
|
7
5
|
## Table of Contents
|
8
6
|
|
9
|
-
- [
|
10
|
-
- [
|
11
|
-
- [
|
12
|
-
- [
|
13
|
-
- [Conditional Execution](#conditional-execution)
|
7
|
+
- [Declarations](#declarations)
|
8
|
+
- [Task](#task)
|
9
|
+
- [Group](#group)
|
10
|
+
- [Conditionals](#conditionals)
|
14
11
|
- [Halt Behavior](#halt-behavior)
|
15
|
-
- [
|
16
|
-
- [
|
17
|
-
- [Group-Level Configuration](#group-level-configuration)
|
18
|
-
- [Available Result Statuses](#available-result-statuses)
|
19
|
-
- [Process Method Options](#process-method-options)
|
20
|
-
- [Error Handling](#error-handling)
|
12
|
+
- [Task Configuration](#task-configuration)
|
13
|
+
- [Group Configuration](#group-configuration)
|
21
14
|
- [Nested Workflows](#nested-workflows)
|
22
|
-
- [Task Settings Integration](#task-settings-integration)
|
23
|
-
- [Generator](#generator)
|
24
|
-
|
25
|
-
## TLDR
|
26
|
-
|
27
|
-
```ruby
|
28
|
-
# Basic workflow - sequential task execution
|
29
|
-
class OrderWorkflow < CMDx::Workflow
|
30
|
-
process ValidateOrderTask # Step 1
|
31
|
-
process CalculateTaxTask # Step 2
|
32
|
-
process ChargePaymentTask # Step 3
|
33
|
-
end
|
34
15
|
|
35
|
-
|
36
|
-
process SendEmailTask, if: proc { context.notify_user? }
|
37
|
-
process SkipableTask, unless: :should_skip?
|
16
|
+
## Declarations
|
38
17
|
|
39
|
-
|
40
|
-
process CriticalTask, workflow_halt: [CMDx::Result::FAILED, CMDx::Result::SKIPPED]
|
41
|
-
process OptionalTask, workflow_halt: [] # Never halt
|
18
|
+
Tasks execute in declaration order (FIFO). The workflow context propagates to each task, allowing access to data from previous executions.
|
42
19
|
|
43
|
-
|
44
|
-
|
45
|
-
result.context.tax_amount # Set by CalculateTaxTask
|
46
|
-
result.context.payment_id # Set by ChargePaymentTask
|
47
|
-
```
|
48
|
-
|
49
|
-
## Basic Usage
|
20
|
+
> [!IMPORTANT]
|
21
|
+
> Do **NOT** define a `work` method in workflow tasks. The included module automatically provides the execution logic.
|
50
22
|
|
51
|
-
|
52
|
-
> Do **NOT** define a `call` method in workflow classes. The workflow automatically provides execution logic.
|
23
|
+
### Task
|
53
24
|
|
54
25
|
```ruby
|
55
|
-
class
|
56
|
-
|
57
|
-
process CalculateTaxTask
|
58
|
-
process ChargePaymentTask
|
59
|
-
process FulfillOrderTask
|
60
|
-
end
|
26
|
+
class OnboardingWorkflow < CMDx::Task
|
27
|
+
include CMDx::Workflow
|
61
28
|
|
62
|
-
|
63
|
-
|
29
|
+
task CreateUserProfile
|
30
|
+
task SetupAccountPreferences
|
64
31
|
|
65
|
-
|
66
|
-
redirect_to order_path(result.context.order)
|
67
|
-
elsif result.failed?
|
68
|
-
handle_error(result.metadata[:reason])
|
32
|
+
tasks SendWelcomeEmail, SendWelcomeSms, CreateDashboard
|
69
33
|
end
|
70
34
|
```
|
71
35
|
|
72
|
-
|
36
|
+
### Group
|
73
37
|
|
74
|
-
|
38
|
+
Group related tasks for better organization and shared configuration:
|
39
|
+
|
40
|
+
> [!IMPORTANT]
|
41
|
+
> Settings and conditionals for a group apply to all tasks within that group.
|
75
42
|
|
76
43
|
```ruby
|
77
|
-
class
|
78
|
-
|
79
|
-
process PrepareNotificationTask
|
44
|
+
class ContentModerationWorkflow < CMDx::Task
|
45
|
+
include CMDx::Workflow
|
80
46
|
|
81
|
-
#
|
82
|
-
|
47
|
+
# Screening phase
|
48
|
+
tasks ScanForProfanity, CheckForSpam, ValidateImages, breakpoints: ["skipped"]
|
83
49
|
|
84
|
-
#
|
85
|
-
|
86
|
-
process SendSlackTask, unless: :slack_disabled?
|
50
|
+
# Review phase
|
51
|
+
tasks ApplyFilters, ScoreContent, FlagSuspicious
|
87
52
|
|
88
|
-
|
89
|
-
|
90
|
-
def slack_disabled?
|
91
|
-
!context.user.slack_enabled?
|
92
|
-
end
|
53
|
+
# Decision phase
|
54
|
+
tasks PublishContent, QueueForReview, NotifyModerators
|
93
55
|
end
|
94
56
|
```
|
95
57
|
|
96
|
-
|
97
|
-
> Tasks execute in declaration order (FIFO). Use grouping to apply the same options to multiple tasks.
|
98
|
-
|
99
|
-
## Context Propagation
|
58
|
+
### Conditionals
|
100
59
|
|
101
|
-
|
60
|
+
Conditionals support multiple syntaxes for flexible execution control:
|
102
61
|
|
103
62
|
```ruby
|
104
|
-
class
|
105
|
-
|
106
|
-
|
107
|
-
|
63
|
+
class ContentAccessCheck
|
64
|
+
def call(task)
|
65
|
+
task.context.user.can?(:publish_content)
|
66
|
+
end
|
108
67
|
end
|
109
68
|
|
110
|
-
|
111
|
-
|
112
|
-
result.context.validation_errors # From ValidateOrderTask
|
113
|
-
result.context.tax_amount # From CalculateTaxTask
|
114
|
-
result.context.payment_id # From ChargePaymentTask
|
115
|
-
```
|
116
|
-
|
117
|
-
## Conditional Execution
|
69
|
+
class OnboardingWorkflow < CMDx::Task
|
70
|
+
include CMDx::Workflow
|
118
71
|
|
119
|
-
|
72
|
+
# If and/or Unless
|
73
|
+
task SendWelcomeEmail, if: :email_configured?, unless: :email_disabled?
|
120
74
|
|
121
|
-
|
122
|
-
|
123
|
-
process ValidateUserTask
|
75
|
+
# Proc
|
76
|
+
task SendWelcomeEmail, if: ->(workflow) { Rails.env.production? && workflow.class.name.include?("Premium") }
|
124
77
|
|
125
|
-
#
|
126
|
-
|
78
|
+
# Lambda
|
79
|
+
task SendWelcomeEmail, if: proc { |workflow| workflow.context.features_enabled? }
|
127
80
|
|
128
|
-
#
|
129
|
-
|
81
|
+
# Class or Module
|
82
|
+
task SendWelcomeEmail, unless: ContentAccessCheck
|
130
83
|
|
131
|
-
#
|
132
|
-
|
84
|
+
# Instance
|
85
|
+
task SendWelcomeEmail, if: ContentAccessCheck.new
|
133
86
|
|
134
|
-
#
|
135
|
-
|
136
|
-
context.user.active? &&
|
137
|
-
context.feature_enabled?(:offers) &&
|
138
|
-
business_hours?
|
139
|
-
}
|
87
|
+
# Conditional applies to all tasks of this declaration group
|
88
|
+
tasks SendWelcomeEmail, CreateDashboard, SetupTutorial, if: :email_configured?
|
140
89
|
|
141
90
|
private
|
142
91
|
|
143
|
-
def
|
144
|
-
|
92
|
+
def email_configured?
|
93
|
+
context.user.email_address.present?
|
145
94
|
end
|
146
95
|
|
147
|
-
def
|
148
|
-
|
96
|
+
def email_disabled?
|
97
|
+
context.user.communication_preference == :disabled
|
149
98
|
end
|
150
99
|
end
|
151
100
|
```
|
152
101
|
|
153
|
-
> [!NOTE]
|
154
|
-
> Conditions are evaluated in the workflow instance context. Skipped tasks return `SKIPPED` status but don't halt execution by default.
|
155
|
-
|
156
102
|
## Halt Behavior
|
157
103
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
By default, workflows halt on `FAILED` status but continue on `SKIPPED`:
|
104
|
+
By default skipped tasks are considered no-op executions and does not stop workflow execution.
|
105
|
+
This is configurable via global and task level breakpoint settings. Task and group configurations
|
106
|
+
can be used together within a workflow.
|
163
107
|
|
164
108
|
```ruby
|
165
|
-
class
|
166
|
-
|
167
|
-
|
168
|
-
|
109
|
+
class AnalyticsWorkflow < CMDx::Task
|
110
|
+
include CMDx::Workflow
|
111
|
+
|
112
|
+
task CollectMetrics # If fails → workflow stops
|
113
|
+
task FilterOutliers # If skipped → workflow continues
|
114
|
+
task GenerateDashboard # Only runs if no failures occurred
|
169
115
|
end
|
170
116
|
```
|
171
117
|
|
172
|
-
###
|
118
|
+
### Task Configuration
|
173
119
|
|
174
120
|
Configure halt behavior for the entire workflow:
|
175
121
|
|
176
122
|
```ruby
|
177
|
-
class
|
123
|
+
class SecurityWorkflow < CMDx::Task
|
124
|
+
include CMDx::Workflow
|
125
|
+
|
178
126
|
# Halt on both failed and skipped results
|
179
|
-
|
127
|
+
settings(workflow_breakpoints: ["skipped", "failed"])
|
180
128
|
|
181
|
-
|
182
|
-
|
129
|
+
task PerformSecurityScan
|
130
|
+
task ValidateSecurityRules
|
183
131
|
end
|
184
132
|
|
185
|
-
class
|
133
|
+
class OptionalTasksWorkflow < CMDx::Task
|
134
|
+
include CMDx::Workflow
|
135
|
+
|
186
136
|
# Never halt, always continue
|
187
|
-
|
137
|
+
settings(breakpoints: [])
|
188
138
|
|
189
|
-
|
190
|
-
|
191
|
-
|
139
|
+
task TryBackupData
|
140
|
+
task TryCleanupLogs
|
141
|
+
task TryOptimizeCache
|
192
142
|
end
|
193
143
|
```
|
194
144
|
|
195
|
-
### Group
|
145
|
+
### Group Configuration
|
196
146
|
|
197
147
|
Different task groups can have different halt behavior:
|
198
148
|
|
199
149
|
```ruby
|
200
|
-
class
|
201
|
-
|
202
|
-
process CreateUserTask, ValidateUserTask,
|
203
|
-
workflow_halt: [CMDx::Result::FAILED, CMDx::Result::SKIPPED]
|
204
|
-
|
205
|
-
# Optional tasks - never halt
|
206
|
-
process SendWelcomeEmailTask, CreateProfileTask,
|
207
|
-
workflow_halt: []
|
208
|
-
|
209
|
-
# Default behavior for remaining tasks
|
210
|
-
process NotifyAdminTask, LogUserCreationTask
|
211
|
-
end
|
212
|
-
```
|
213
|
-
|
214
|
-
### Available Result Statuses
|
215
|
-
|
216
|
-
Use these statuses in `workflow_halt` arrays:
|
217
|
-
|
218
|
-
| Status | Description |
|
219
|
-
|--------|-------------|
|
220
|
-
| `CMDx::Result::SUCCESS` | Task completed successfully |
|
221
|
-
| `CMDx::Result::SKIPPED` | Task was skipped intentionally |
|
222
|
-
| `CMDx::Result::FAILED` | Task failed due to error or validation |
|
223
|
-
|
224
|
-
## Process Method Options
|
225
|
-
|
226
|
-
The `process` method supports these options:
|
227
|
-
|
228
|
-
| Option | Description | Example |
|
229
|
-
|--------|-------------|---------|
|
230
|
-
| `:if` | Execute task if condition is true | `if: proc { context.enabled? }` |
|
231
|
-
| `:unless` | Execute task if condition is false | `unless: :should_skip?` |
|
232
|
-
| `:workflow_halt` | Which statuses should halt execution | `workflow_halt: [CMDx::Result::FAILED]` |
|
233
|
-
|
234
|
-
Conditions can be procs, lambdas, symbols, or strings referencing instance methods.
|
235
|
-
|
236
|
-
## Error Handling
|
237
|
-
|
238
|
-
> [!WARNING]
|
239
|
-
> Workflow failures provide detailed information about which task failed and why, enabling precise error handling and debugging.
|
240
|
-
|
241
|
-
```ruby
|
242
|
-
class OrderWorkflow < CMDx::Workflow
|
243
|
-
process ValidateOrderTask
|
244
|
-
process CalculateTaxTask
|
245
|
-
process ChargePaymentTask
|
246
|
-
end
|
247
|
-
|
248
|
-
result = OrderWorkflow.call(order: invalid_order)
|
249
|
-
|
250
|
-
if result.failed?
|
251
|
-
result.metadata
|
252
|
-
# {
|
253
|
-
# reason: "ValidateOrderTask failed: Order ID is required",
|
254
|
-
# failed_task: "ValidateOrderTask",
|
255
|
-
# task_index: 0,
|
256
|
-
# executed_tasks: ["ValidateOrderTask"],
|
257
|
-
# skipped_tasks: [],
|
258
|
-
# context_at_failure: { order: {...} }
|
259
|
-
# }
|
260
|
-
end
|
261
|
-
```
|
262
|
-
|
263
|
-
### Common Error Scenarios
|
264
|
-
|
265
|
-
```ruby
|
266
|
-
# Task raises exception
|
267
|
-
class ProcessDataWorkflow < CMDx::Workflow
|
268
|
-
process ValidateDataTask # Raises validation error
|
269
|
-
process TransformDataTask # Never executes
|
270
|
-
end
|
150
|
+
class SubscriptionWorkflow < CMDx::Task
|
151
|
+
include CMDx::Workflow
|
271
152
|
|
272
|
-
|
273
|
-
result.failed? # → true
|
274
|
-
result.metadata[:reason] # → "ValidateDataTask failed: Data cannot be nil"
|
153
|
+
task CreateSubscription, ValidatePayment, workflow_breakpoints: ["skipped", "failed"]
|
275
154
|
|
276
|
-
#
|
277
|
-
|
278
|
-
process RequiredTask, workflow_halt: [CMDx::Result::SKIPPED]
|
279
|
-
process OptionalTask, if: proc { false } # Always skipped
|
280
|
-
process FinalTask # Never executes
|
155
|
+
# Never halt, always continue
|
156
|
+
task SendConfirmationEmail, UpdateBilling, breakpoints: []
|
281
157
|
end
|
282
|
-
|
283
|
-
result = StrictWorkflow.call
|
284
|
-
result.failed? # → true (halted on skipped task)
|
285
158
|
```
|
286
159
|
|
287
|
-
> [!TIP]
|
288
|
-
> Use specific halt configurations to implement different failure strategies: strict validation, best-effort processing, or fault-tolerant pipelines.
|
289
|
-
|
290
160
|
## Nested Workflows
|
291
161
|
|
292
|
-
Workflows can
|
162
|
+
Workflows can task other workflows for hierarchical composition:
|
293
163
|
|
294
164
|
```ruby
|
295
|
-
class
|
296
|
-
|
297
|
-
process SanitizeDataTask
|
298
|
-
end
|
299
|
-
|
300
|
-
class DataProcessingWorkflow < CMDx::Workflow
|
301
|
-
process TransformDataTask
|
302
|
-
process ApplyBusinessLogicTask
|
303
|
-
end
|
165
|
+
class EmailPreparationWorkflow < CMDx::Task
|
166
|
+
include CMDx::Workflow
|
304
167
|
|
305
|
-
|
306
|
-
|
307
|
-
process DataProcessingWorkflow, if: proc { context.pre_processing_successful? }
|
308
|
-
process GenerateReportTask
|
168
|
+
task ValidateRecipients
|
169
|
+
task CompileTemplate
|
309
170
|
end
|
310
|
-
```
|
311
|
-
|
312
|
-
> [!NOTE]
|
313
|
-
> Nested workflows share the same context object, enabling seamless data flow across workflow boundaries.
|
314
|
-
|
315
|
-
## Task Settings Integration
|
316
|
-
|
317
|
-
Workflows support all task capabilities including parameters, callbacks, and configuration:
|
318
|
-
|
319
|
-
```ruby
|
320
|
-
class PaymentWorkflow < CMDx::Workflow
|
321
|
-
# Parameter validation
|
322
|
-
required :order_id, type: :integer
|
323
|
-
optional :notify_user, type: :boolean, default: true
|
324
|
-
|
325
|
-
# Workflow settings
|
326
|
-
cmd_settings!(
|
327
|
-
workflow_halt: [CMDx::Result::FAILED],
|
328
|
-
log_level: :debug,
|
329
|
-
tags: [:critical, :payment]
|
330
|
-
)
|
331
|
-
|
332
|
-
# Callbacks
|
333
|
-
before_execution :setup_context
|
334
|
-
after_execution :cleanup_resources
|
335
|
-
|
336
|
-
process ValidateOrderTask
|
337
|
-
process ProcessPaymentTask
|
338
|
-
process NotifyUserTask, if: proc { context.notify_user }
|
339
|
-
|
340
|
-
private
|
341
171
|
|
342
|
-
|
343
|
-
|
344
|
-
end
|
172
|
+
class EmailDeliveryWorkflow < CMDx::Task
|
173
|
+
include CMDx::Workflow
|
345
174
|
|
346
|
-
|
347
|
-
context.temp_files&.each(&:delete)
|
348
|
-
end
|
175
|
+
tasks SendEmails, TrackDeliveries
|
349
176
|
end
|
350
|
-
```
|
351
177
|
|
352
|
-
|
178
|
+
class CompleteEmailWorkflow < CMDx::Task
|
179
|
+
include CMDx::Workflow
|
353
180
|
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
rails g cmdx:workflow ProcessOrder
|
358
|
-
```
|
359
|
-
|
360
|
-
Creates `app/commands/process_order_workflow.rb`:
|
361
|
-
|
362
|
-
```ruby
|
363
|
-
class ProcessOrderWorkflow < ApplicationWorkflow
|
364
|
-
process # TODO: Add your tasks here
|
181
|
+
task EmailPreparationWorkflow
|
182
|
+
task EmailDeliveryWorkflow, if: proc { context.preparation_successful? }
|
183
|
+
task GenerateDeliveryReport
|
365
184
|
end
|
366
185
|
```
|
367
186
|
|
368
|
-
> [!NOTE]
|
369
|
-
> The generator creates workflow files in `app/commands/`, inherits from `ApplicationWorkflow` if available (otherwise `CMDx::Workflow`), and handles proper naming conventions.
|
370
|
-
|
371
187
|
---
|
372
188
|
|
373
|
-
- **Prev:** [
|
374
|
-
- **Next:** [
|
189
|
+
- **Prev:** [Deprecation](deprecation.md)
|
190
|
+
- **Next:** [Tips and Tricks](tips_and_tricks.md)
|
data/lib/cmdx/.DS_Store
CHANGED
Binary file
|