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/getting_started.md
CHANGED
@@ -1,47 +1,25 @@
|
|
1
1
|
# Getting Started
|
2
2
|
|
3
|
-
CMDx is a Ruby framework for building maintainable, observable business logic through composable command objects. Design robust workflows with automatic
|
3
|
+
CMDx is a Ruby framework for building maintainable, observable business logic through composable command objects. Design robust workflows with automatic attribute validation, structured error handling, comprehensive logging, and intelligent execution flow control.
|
4
4
|
|
5
5
|
## Table of Contents
|
6
6
|
|
7
|
-
- [TLDR](#tldr)
|
8
7
|
- [Installation](#installation)
|
9
|
-
- [
|
10
|
-
- [
|
11
|
-
- [
|
12
|
-
- [
|
13
|
-
- [
|
14
|
-
- [
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
#
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
class ProcessOrderTask < CMDx::Task
|
25
|
-
required :order_id, type: :integer
|
26
|
-
optional :send_email, type: :boolean, default: true
|
27
|
-
|
28
|
-
def call
|
29
|
-
context.order = Order.find(order_id)
|
30
|
-
fail!("Order canceled") if context.order.canceled?
|
31
|
-
skip!("Already processed") if context.order.completed?
|
32
|
-
|
33
|
-
context.order.update!(status: 'completed')
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
# Execution
|
38
|
-
result = ProcessOrderTask.call(order_id: 123) # Returns Result
|
39
|
-
result = ProcessOrderTask.call!(order_id: 123) # Raises on failure
|
40
|
-
|
41
|
-
# Check outcomes
|
42
|
-
result.success? && result.context.order # Access data
|
43
|
-
result.failed? && result.metadata[:reason] # Error details
|
44
|
-
```
|
8
|
+
- [Configuration Hierarchy](#configuration-hierarchy)
|
9
|
+
- [Global Configuration](#global-configuration)
|
10
|
+
- [Breakpoints](#breakpoints)
|
11
|
+
- [Logging](#logging)
|
12
|
+
- [Middlewares](#middlewares)
|
13
|
+
- [Callbacks](#callbacks)
|
14
|
+
- [Coercions](#coercions)
|
15
|
+
- [Validators](#validators)
|
16
|
+
- [Task Configuration](#task-configuration)
|
17
|
+
- [Settings](#settings)
|
18
|
+
- [Registrations](#registrations)
|
19
|
+
- [Configuration Management](#configuration-management)
|
20
|
+
- [Access](#access)
|
21
|
+
- [Resetting](#resetting)
|
22
|
+
- [Task Generator](#task-generator)
|
45
23
|
|
46
24
|
## Installation
|
47
25
|
|
@@ -57,236 +35,268 @@ For Rails applications, generate the configuration:
|
|
57
35
|
rails generate cmdx:install
|
58
36
|
```
|
59
37
|
|
60
|
-
|
61
|
-
> This creates `config/initializers/cmdx.rb` with default settings for logging, error handling, and middleware configuration.
|
38
|
+
This creates `config/initializers/cmdx.rb` file.
|
62
39
|
|
63
|
-
##
|
40
|
+
## Configuration Hierarchy
|
64
41
|
|
65
|
-
|
66
|
-
> Use **present tense verbs** for task names: `ProcessOrderTask`, `SendEmailTask`, `ValidatePaymentTask`
|
42
|
+
CMDx follows a two-tier configuration hierarchy:
|
67
43
|
|
68
|
-
|
69
|
-
|
70
|
-
required :order_id, type: :integer
|
71
|
-
optional :send_email, type: :boolean, default: true
|
72
|
-
|
73
|
-
def call
|
74
|
-
context.order = Order.find(order_id)
|
75
|
-
|
76
|
-
if context.order.canceled?
|
77
|
-
fail!(reason: "Order canceled", canceled_at: context.order.canceled_at)
|
78
|
-
elsif context.order.completed?
|
79
|
-
skip!(reason: "Already processed")
|
80
|
-
else
|
81
|
-
context.order.update!(status: 'completed', completed_at: Time.now)
|
82
|
-
EmailService.send_confirmation(context.order) if send_email
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
```
|
44
|
+
1. **Global Configuration**: Framework-wide defaults
|
45
|
+
2. **Task Settings**: Class-level overrides via `settings`
|
87
46
|
|
88
|
-
|
47
|
+
> [!IMPORTANT]
|
48
|
+
> Task-level settings take precedence over global configuration. Settings are inherited from superclasses and can be overridden in subclasses.
|
89
49
|
|
90
|
-
|
50
|
+
## Global Configuration
|
91
51
|
|
92
|
-
|
93
|
-
|
94
|
-
required :email, type: :string
|
95
|
-
required :age, type: :integer
|
96
|
-
required :active, type: :boolean, default: true
|
97
|
-
|
98
|
-
optional :metadata, type: :hash, default: {}
|
99
|
-
optional :tags, type: :array, default: []
|
100
|
-
|
101
|
-
def call
|
102
|
-
context.user = User.create!(
|
103
|
-
email: email,
|
104
|
-
age: age,
|
105
|
-
active: active,
|
106
|
-
metadata: metadata,
|
107
|
-
tags: tags
|
108
|
-
)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
```
|
52
|
+
Global configuration settings apply to all tasks inherited from `CMDx::Task`.
|
53
|
+
Globally these settings are initialized with sensible defaults.
|
112
54
|
|
113
|
-
|
55
|
+
### Breakpoints
|
114
56
|
|
115
|
-
|
57
|
+
Breakpoints control when `execute!` raises faults.
|
116
58
|
|
117
59
|
```ruby
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
result = ProcessOrderTask.call!(order_id: 123, send_email: false)
|
60
|
+
CMDx.configure do |config|
|
61
|
+
config.task_breakpoints = "skipped"
|
62
|
+
config.workflow_breakpoints = ["skipped", "failed"]
|
63
|
+
end
|
123
64
|
```
|
124
65
|
|
125
|
-
|
126
|
-
> Use `call` for conditional logic based on results, and `call!` for exception-based control flow where failures should halt execution.
|
66
|
+
### Logging
|
127
67
|
|
128
|
-
|
68
|
+
```ruby
|
69
|
+
CMDx.configure do |config|
|
70
|
+
config.logger = CustomLogger.new($stdout)
|
71
|
+
end
|
72
|
+
```
|
129
73
|
|
130
|
-
|
74
|
+
### Middlewares
|
131
75
|
|
132
76
|
```ruby
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
77
|
+
CMDx.configure do |config|
|
78
|
+
# Via callable (must respond to `call(task, options)`)
|
79
|
+
config.middlewares.register CMDx::Middlewares::Timeout
|
80
|
+
|
81
|
+
# Via proc or lambda
|
82
|
+
config.middlewares.register proc { |task, options|
|
83
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
84
|
+
result = yield
|
85
|
+
end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
86
|
+
Rails.logger.debug { "task completed in #{((end_time - start_time) * 1000).round(2)}ms" }
|
87
|
+
result
|
88
|
+
}
|
89
|
+
|
90
|
+
# With options
|
91
|
+
config.middlewares.register AuditTrailMiddleware, service_name: "document_processor"
|
92
|
+
|
93
|
+
# Remove middleware
|
94
|
+
config.middlewares.deregister CMDx::Middlewares::Timeout
|
95
|
+
end
|
138
96
|
```
|
139
97
|
|
140
|
-
|
98
|
+
> [!NOTE]
|
99
|
+
> Middlewares are executed in registration order. Each middleware wraps the next, creating an execution chain around task logic.
|
141
100
|
|
142
|
-
|
101
|
+
### Callbacks
|
143
102
|
|
144
103
|
```ruby
|
145
|
-
|
104
|
+
CMDx.configure do |config|
|
105
|
+
# Via method
|
106
|
+
config.callbacks.register :before_execution, :initialize_user_session
|
146
107
|
|
147
|
-
|
148
|
-
|
149
|
-
order = result.context.order
|
150
|
-
redirect_to order_path(order), notice: "Order processed successfully!"
|
108
|
+
# Via callable (must respond to `call(task)`)
|
109
|
+
config.callbacks.register :on_success, LogUserActivity
|
151
110
|
|
152
|
-
|
153
|
-
|
154
|
-
|
111
|
+
# Via proc or lambda
|
112
|
+
config.callbacks.register :on_complete, proc { |task|
|
113
|
+
execution_time = task.metadata[:runtime]
|
114
|
+
Metrics.timer("task.execution_time", execution_time, tags: ["task:#{task.class.name.underscore}"])
|
115
|
+
}
|
155
116
|
|
156
|
-
|
157
|
-
|
158
|
-
redirect_to orders_path, alert: "Processing failed: #{error_details}"
|
159
|
-
end
|
117
|
+
# With options
|
118
|
+
config.callbacks.register :on_failure, :send_alert_notification, if: :critical_task?
|
160
119
|
|
161
|
-
#
|
162
|
-
|
163
|
-
|
164
|
-
puts "Executed at: #{result.executed_at}"
|
120
|
+
# Remove callback
|
121
|
+
config.callbacks.deregister :on_success, LogUserActivity
|
122
|
+
end
|
165
123
|
```
|
166
124
|
|
167
|
-
###
|
125
|
+
### Coercions
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
CMDx.configure do |config|
|
129
|
+
# Via callable (must respond to `call(value, options)`)
|
130
|
+
config.coercions.register :currency, CurrencyCoercion
|
168
131
|
|
169
|
-
|
170
|
-
|
171
|
-
| `status` | Execution outcome | `'success'`, `'failed'`, `'skipped'` |
|
172
|
-
| `context` | Shared data object | `result.context.order` |
|
173
|
-
| `metadata` | Additional details | `result.metadata[:reason]` |
|
174
|
-
| `runtime` | Execution time (ms) | `result.runtime` |
|
175
|
-
| `id` | Unique task execution ID | `result.id` |
|
132
|
+
# Via method (must match signature `def coordinates_coercion(value, options)`)
|
133
|
+
config.coercions.register :coordinates, :coordinates_coercion
|
176
134
|
|
177
|
-
|
135
|
+
# Via proc or lambda
|
136
|
+
config.coercions.register :tag_list, proc { |value, options|
|
137
|
+
delimiter = options[:delimiter] || ','
|
138
|
+
max_tags = options[:max_tags] || 50
|
178
139
|
|
179
|
-
|
180
|
-
|
140
|
+
tags = value.to_s.split(delimiter).map(&:strip).reject(&:empty?)
|
141
|
+
tags.first(max_tags)
|
142
|
+
}
|
143
|
+
|
144
|
+
# Remove coercion
|
145
|
+
config.coercions.deregister :currency
|
146
|
+
end
|
147
|
+
```
|
148
|
+
|
149
|
+
### Validators
|
181
150
|
|
182
151
|
```ruby
|
183
|
-
|
184
|
-
|
185
|
-
|
152
|
+
CMDx.configure do |config|
|
153
|
+
# Via callable (must respond to `call(value, options)`)
|
154
|
+
config.validators.register :username, UsernameValidator
|
155
|
+
|
156
|
+
# Via method (must match signature `def url_validator(value, options)`)
|
157
|
+
config.validators.register :url, :url_validator
|
186
158
|
|
187
|
-
|
188
|
-
|
189
|
-
|
159
|
+
# Via proc or lambda
|
160
|
+
config.validators.register :access_token, proc { |value, options|
|
161
|
+
expected_prefix = options[:prefix] || "tok_"
|
162
|
+
minimum_length = options[:min_length] || 40
|
190
163
|
|
191
|
-
|
192
|
-
|
193
|
-
redirect_to order_path(123), alert: "Failed: #{error_details}"
|
164
|
+
value.start_with?(expected_prefix) && value.length >= minimum_length
|
165
|
+
}
|
194
166
|
|
195
|
-
|
196
|
-
|
167
|
+
# Remove validator
|
168
|
+
config.validators.deregister :username
|
197
169
|
end
|
198
170
|
```
|
199
171
|
|
200
|
-
|
172
|
+
## Task Configuration
|
201
173
|
|
202
|
-
|
203
|
-
- **`CMDx::Failed`** - Task failed due to business logic or validation errors
|
204
|
-
- **Standard exceptions** - Ruby/Rails exceptions (e.g., `ActiveRecord::RecordNotFound`)
|
174
|
+
### Settings
|
205
175
|
|
206
|
-
|
207
|
-
|
208
|
-
> [!TIP]
|
209
|
-
> Workflows orchestrate multiple tasks with automatic context sharing, error handling, and execution flow control.
|
176
|
+
Override global configuration for specific tasks using `settings`:
|
210
177
|
|
211
178
|
```ruby
|
212
|
-
class
|
213
|
-
|
214
|
-
|
179
|
+
class GenerateInvoice < CMDx::Task
|
180
|
+
settings(
|
181
|
+
# Global configuration overrides
|
182
|
+
task_breakpoints: ["failed"], # Breakpoint override
|
183
|
+
workflow_breakpoints: [], # Breakpoint override
|
184
|
+
logger: CustomLogger.new($stdout), # Custom logger
|
185
|
+
|
186
|
+
# Task configuration settings
|
187
|
+
breakpoints: ["failed"], # Contextual pointer for :task_breakpoints and :workflow_breakpoints
|
188
|
+
log_level: :info, # Log level override
|
189
|
+
log_formatter: CMDx::LogFormatters::Json.new # Log formatter override
|
190
|
+
tags: ["billing", "financial"], # Logging tags
|
191
|
+
deprecated: true # Task deprecations
|
192
|
+
)
|
193
|
+
|
194
|
+
def work
|
195
|
+
# Your logic here...
|
196
|
+
end
|
197
|
+
end
|
198
|
+
```
|
215
199
|
|
216
|
-
|
217
|
-
|
218
|
-
on_skipped :log_skip_reason
|
200
|
+
> [!TIP]
|
201
|
+
> Use task-level settings for tasks that require special handling, such as financial reporting, external API integrations, or critical system operations.
|
219
202
|
|
220
|
-
|
221
|
-
process ChargePaymentTask
|
222
|
-
process UpdateInventoryTask
|
223
|
-
process SendConfirmationTask, if: proc { context.payment_successful? }
|
224
|
-
process ExpressShippingTask, if: proc { priority == 'express' }
|
203
|
+
### Registrations
|
225
204
|
|
226
|
-
|
205
|
+
Register middlewares, callbacks, coercions, and validators on a specific task.
|
206
|
+
Deregister options that should not be available.
|
227
207
|
|
228
|
-
|
229
|
-
|
208
|
+
```ruby
|
209
|
+
class SendCampaignEmail < CMDx::Task
|
210
|
+
# Middlewares
|
211
|
+
register :middleware, CMDx::Middlewares::Timeout
|
212
|
+
deregister :middleware, AuditTrailMiddleware
|
213
|
+
|
214
|
+
# Callbacks
|
215
|
+
register :callback, :on_complete, proc { |task|
|
216
|
+
runtime = task.metadata[:runtime]
|
217
|
+
Analytics.track("email_campaign.sent", runtime, tags: ["task:#{task.class.name}"])
|
218
|
+
}
|
219
|
+
deregister :callback, :before_execution, :initialize_user_session
|
220
|
+
|
221
|
+
# Coercions
|
222
|
+
register :coercion, :currency, CurrencyCoercion
|
223
|
+
deregister :coercion, :coordinates
|
224
|
+
|
225
|
+
# Validators
|
226
|
+
register :validator, :username, :username_validator
|
227
|
+
deregister :validator, :url
|
228
|
+
|
229
|
+
def work
|
230
|
+
# Your logic here...
|
230
231
|
end
|
232
|
+
end
|
233
|
+
```
|
231
234
|
|
232
|
-
|
233
|
-
SupportNotifier.alert("Order workflow failed",
|
234
|
-
order_id: order_id,
|
235
|
-
error: result.metadata[:reason]
|
236
|
-
)
|
237
|
-
end
|
235
|
+
## Configuration Management
|
238
236
|
|
239
|
-
|
240
|
-
|
237
|
+
### Access
|
238
|
+
|
239
|
+
```ruby
|
240
|
+
# Global configuration access
|
241
|
+
CMDx.configuration.logger #=> <Logger instance>
|
242
|
+
CMDx.configuration.task_breakpoints #=> ["failed"]
|
243
|
+
CMDx.configuration.middlewares.registry #=> [<Middleware>, ...]
|
244
|
+
|
245
|
+
# Task configuration access
|
246
|
+
class ProcessUpload < CMDx::Task
|
247
|
+
settings(tags: ["files", "storage"])
|
248
|
+
|
249
|
+
def work
|
250
|
+
self.class.settings[:logger] #=> Global configuration value
|
251
|
+
self.class.settings[:tags] #=> Task configuration value => ["files", "storage"]
|
241
252
|
end
|
242
253
|
end
|
243
|
-
|
244
|
-
# Execute workflow
|
245
|
-
result = OrderProcessingWorkflow.call(order_id: 123, priority: 'express')
|
246
254
|
```
|
247
255
|
|
248
|
-
###
|
249
|
-
|
250
|
-
- **Automatic context sharing** - Tasks access shared `context` object
|
251
|
-
- **Conditional execution** - Use `:if` conditions for optional tasks
|
252
|
-
- **Lifecycle callbacks** - Hook into workflow execution phases
|
253
|
-
- **Error propagation** - Failed tasks halt workflow execution
|
254
|
-
- **Skip handling** - Graceful handling of skipped tasks
|
256
|
+
### Resetting
|
255
257
|
|
256
|
-
|
257
|
-
|
258
|
-
Generate properly structured tasks and workflows:
|
258
|
+
> [!WARNING]
|
259
|
+
> Resetting configuration affects the entire application. Use primarily in test environments or during application initialization.
|
259
260
|
|
260
261
|
```ruby
|
261
|
-
#
|
262
|
-
|
263
|
-
# Creates: app/cmds/process_order_task.rb
|
262
|
+
# Reset to framework defaults
|
263
|
+
CMDx.reset_configuration!
|
264
264
|
|
265
|
-
#
|
266
|
-
|
267
|
-
|
265
|
+
# Verify reset
|
266
|
+
CMDx.configuration.task_breakpoints #=> ["failed"] (default)
|
267
|
+
CMDx.configuration.middlewares.registry #=> Empty registry
|
268
268
|
|
269
|
-
#
|
270
|
-
|
269
|
+
# Commonly used in test setup (RSpec example)
|
270
|
+
RSpec.configure do |config|
|
271
|
+
config.before(:each) do
|
272
|
+
CMDx.reset_configuration!
|
273
|
+
end
|
274
|
+
end
|
271
275
|
```
|
272
276
|
|
273
|
-
|
274
|
-
> Generators automatically handle naming conventions and inherit from `ApplicationTask`/`ApplicationWorkflow` when available. Generated files include parameter definitions and basic structure.
|
277
|
+
## Task Generator
|
275
278
|
|
276
|
-
|
279
|
+
Generate new CMDx tasks quickly using the built-in generator:
|
277
280
|
|
278
|
-
```
|
279
|
-
|
280
|
-
|
281
|
-
required :order_id, type: :integer
|
281
|
+
```bash
|
282
|
+
rails generate cmdx:task ModerateBlogPost
|
283
|
+
```
|
282
284
|
|
283
|
-
|
284
|
-
|
285
|
+
This creates a new task file with the basic structure:
|
286
|
+
|
287
|
+
```ruby
|
288
|
+
# app/tasks/moderate_blog_post.rb
|
289
|
+
class ModerateBlogPost < CMDx::Task
|
290
|
+
def work
|
291
|
+
# Your logic here...
|
285
292
|
end
|
286
293
|
end
|
287
294
|
```
|
288
295
|
|
296
|
+
> [!TIP]
|
297
|
+
> Use **present tense verbs + noun** for task names, eg: `ModerateBlogPost`, `ScheduleAppointment`, `ValidateDocument`
|
298
|
+
|
289
299
|
---
|
290
300
|
|
291
|
-
- **
|
292
|
-
- **
|
301
|
+
- **Prev:** [Tips and Tricks](tips_and_tricks.md)
|
302
|
+
- **Next:** [Basics - Setup](basics/setup.md)
|