cmdx 1.0.1 → 1.1.1
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/docs.md +9 -0
- data/.cursor/prompts/rspec.md +21 -0
- data/.cursor/prompts/yardoc.md +13 -0
- data/.rubocop.yml +2 -0
- data/CHANGELOG.md +29 -3
- data/README.md +2 -1
- data/docs/ai_prompts.md +269 -195
- data/docs/basics/call.md +126 -60
- data/docs/basics/chain.md +190 -160
- data/docs/basics/context.md +242 -154
- data/docs/basics/setup.md +302 -32
- data/docs/callbacks.md +382 -119
- data/docs/configuration.md +211 -49
- data/docs/deprecation.md +245 -0
- data/docs/getting_started.md +161 -39
- data/docs/internationalization.md +590 -70
- data/docs/interruptions/exceptions.md +135 -118
- data/docs/interruptions/faults.md +152 -127
- data/docs/interruptions/halt.md +134 -80
- data/docs/logging.md +183 -120
- data/docs/middlewares.md +165 -392
- data/docs/outcomes/result.md +140 -112
- data/docs/outcomes/states.md +134 -99
- data/docs/outcomes/statuses.md +204 -146
- data/docs/parameters/coercions.md +251 -289
- data/docs/parameters/defaults.md +224 -169
- data/docs/parameters/definitions.md +289 -141
- data/docs/parameters/namespacing.md +250 -161
- data/docs/parameters/validations.md +247 -159
- data/docs/testing.md +196 -203
- data/docs/workflows.md +146 -101
- data/lib/cmdx/.DS_Store +0 -0
- data/lib/cmdx/callback.rb +39 -55
- data/lib/cmdx/callback_registry.rb +80 -73
- data/lib/cmdx/chain.rb +65 -122
- data/lib/cmdx/chain_inspector.rb +23 -116
- data/lib/cmdx/chain_serializer.rb +34 -146
- data/lib/cmdx/coercion.rb +57 -0
- data/lib/cmdx/coercion_registry.rb +113 -0
- data/lib/cmdx/coercions/array.rb +18 -36
- data/lib/cmdx/coercions/big_decimal.rb +21 -33
- data/lib/cmdx/coercions/boolean.rb +21 -40
- data/lib/cmdx/coercions/complex.rb +18 -31
- data/lib/cmdx/coercions/date.rb +20 -39
- data/lib/cmdx/coercions/date_time.rb +22 -39
- data/lib/cmdx/coercions/float.rb +19 -32
- data/lib/cmdx/coercions/hash.rb +22 -41
- data/lib/cmdx/coercions/integer.rb +20 -33
- data/lib/cmdx/coercions/rational.rb +20 -32
- data/lib/cmdx/coercions/string.rb +23 -31
- data/lib/cmdx/coercions/time.rb +24 -40
- data/lib/cmdx/coercions/virtual.rb +14 -31
- data/lib/cmdx/configuration.rb +101 -162
- data/lib/cmdx/context.rb +34 -166
- data/lib/cmdx/core_ext/hash.rb +42 -67
- data/lib/cmdx/core_ext/module.rb +35 -79
- data/lib/cmdx/core_ext/object.rb +63 -98
- data/lib/cmdx/correlator.rb +59 -154
- data/lib/cmdx/error.rb +37 -202
- data/lib/cmdx/errors.rb +153 -216
- data/lib/cmdx/fault.rb +68 -150
- data/lib/cmdx/faults.rb +26 -137
- data/lib/cmdx/immutator.rb +22 -110
- data/lib/cmdx/lazy_struct.rb +110 -186
- data/lib/cmdx/log_formatters/json.rb +14 -40
- data/lib/cmdx/log_formatters/key_value.rb +14 -40
- data/lib/cmdx/log_formatters/line.rb +14 -48
- data/lib/cmdx/log_formatters/logstash.rb +14 -57
- data/lib/cmdx/log_formatters/pretty_json.rb +14 -50
- data/lib/cmdx/log_formatters/pretty_key_value.rb +13 -46
- data/lib/cmdx/log_formatters/pretty_line.rb +16 -54
- data/lib/cmdx/log_formatters/raw.rb +19 -49
- data/lib/cmdx/logger.rb +22 -79
- data/lib/cmdx/logger_ansi.rb +31 -72
- data/lib/cmdx/logger_serializer.rb +74 -103
- data/lib/cmdx/middleware.rb +56 -60
- data/lib/cmdx/middleware_registry.rb +82 -77
- data/lib/cmdx/middlewares/correlate.rb +41 -226
- data/lib/cmdx/middlewares/timeout.rb +46 -185
- data/lib/cmdx/parameter.rb +167 -183
- data/lib/cmdx/parameter_evaluator.rb +231 -0
- data/lib/cmdx/parameter_inspector.rb +37 -55
- data/lib/cmdx/parameter_registry.rb +65 -84
- data/lib/cmdx/parameter_serializer.rb +32 -76
- data/lib/cmdx/railtie.rb +24 -107
- data/lib/cmdx/result.rb +254 -259
- data/lib/cmdx/result_ansi.rb +28 -80
- data/lib/cmdx/result_inspector.rb +34 -70
- data/lib/cmdx/result_logger.rb +23 -77
- data/lib/cmdx/result_serializer.rb +59 -125
- data/lib/cmdx/rspec/matchers.rb +28 -0
- data/lib/cmdx/rspec/result_matchers/be_executed.rb +42 -0
- data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +94 -0
- data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +94 -0
- data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +59 -0
- data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +57 -0
- data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +87 -0
- data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +51 -0
- data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +58 -0
- data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +59 -0
- data/lib/cmdx/rspec/result_matchers/have_context.rb +86 -0
- data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +54 -0
- data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +52 -0
- data/lib/cmdx/rspec/result_matchers/have_metadata.rb +114 -0
- data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +66 -0
- data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +64 -0
- data/lib/cmdx/rspec/result_matchers/have_runtime.rb +78 -0
- data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +76 -0
- data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +62 -0
- data/lib/cmdx/rspec/task_matchers/have_callback.rb +85 -0
- data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +68 -0
- data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +92 -0
- data/lib/cmdx/rspec/task_matchers/have_middleware.rb +46 -0
- data/lib/cmdx/rspec/task_matchers/have_parameter.rb +181 -0
- data/lib/cmdx/task.rb +336 -427
- data/lib/cmdx/task_deprecator.rb +52 -0
- data/lib/cmdx/task_processor.rb +246 -0
- data/lib/cmdx/task_serializer.rb +34 -69
- data/lib/cmdx/utils/ansi_color.rb +13 -89
- data/lib/cmdx/utils/log_timestamp.rb +13 -42
- data/lib/cmdx/utils/monotonic_runtime.rb +11 -63
- data/lib/cmdx/utils/name_affix.rb +21 -71
- data/lib/cmdx/validator.rb +57 -0
- data/lib/cmdx/validator_registry.rb +108 -0
- data/lib/cmdx/validators/exclusion.rb +55 -94
- data/lib/cmdx/validators/format.rb +31 -85
- data/lib/cmdx/validators/inclusion.rb +65 -110
- data/lib/cmdx/validators/length.rb +117 -133
- data/lib/cmdx/validators/numeric.rb +123 -130
- data/lib/cmdx/validators/presence.rb +38 -79
- data/lib/cmdx/version.rb +1 -7
- data/lib/cmdx/workflow.rb +58 -330
- data/lib/cmdx.rb +1 -1
- data/lib/generators/cmdx/install_generator.rb +14 -31
- data/lib/generators/cmdx/task_generator.rb +39 -55
- data/lib/generators/cmdx/templates/install.rb +24 -6
- data/lib/generators/cmdx/workflow_generator.rb +41 -66
- data/lib/locales/ar.yml +0 -1
- data/lib/locales/cs.yml +0 -1
- data/lib/locales/da.yml +0 -1
- data/lib/locales/de.yml +0 -1
- data/lib/locales/el.yml +0 -1
- data/lib/locales/en.yml +0 -1
- data/lib/locales/es.yml +0 -1
- data/lib/locales/fi.yml +0 -1
- data/lib/locales/fr.yml +0 -1
- data/lib/locales/he.yml +0 -1
- data/lib/locales/hi.yml +0 -1
- data/lib/locales/it.yml +0 -1
- data/lib/locales/ja.yml +0 -1
- data/lib/locales/ko.yml +0 -1
- data/lib/locales/nl.yml +0 -1
- data/lib/locales/no.yml +0 -1
- data/lib/locales/pl.yml +0 -1
- data/lib/locales/pt.yml +0 -1
- data/lib/locales/ru.yml +0 -1
- data/lib/locales/sv.yml +0 -1
- data/lib/locales/th.yml +0 -1
- data/lib/locales/tr.yml +0 -1
- data/lib/locales/vi.yml +0 -1
- data/lib/locales/zh.yml +0 -1
- metadata +36 -8
- data/lib/cmdx/parameter_validator.rb +0 -81
- data/lib/cmdx/parameter_value.rb +0 -244
- data/lib/cmdx/parameters_inspector.rb +0 -72
- data/lib/cmdx/parameters_serializer.rb +0 -115
- data/lib/cmdx/rspec/result_matchers.rb +0 -917
- data/lib/cmdx/rspec/task_matchers.rb +0 -570
- data/lib/cmdx/validators/custom.rb +0 -102
data/docs/configuration.md
CHANGED
@@ -10,28 +10,46 @@ CMDx provides a flexible configuration system that allows customization at both
|
|
10
10
|
- [Configuration Options](#configuration-options)
|
11
11
|
- [Global Middlewares](#global-middlewares)
|
12
12
|
- [Global Callbacks](#global-callbacks)
|
13
|
+
- [Global Coercions](#global-coercions)
|
14
|
+
- [Global Validators](#global-validators)
|
13
15
|
- [Task Settings](#task-settings)
|
14
16
|
- [Available Task Settings](#available-task-settings)
|
15
17
|
- [Workflow Configuration](#workflow-configuration)
|
16
18
|
- [Configuration Management](#configuration-management)
|
17
19
|
- [Accessing Configuration](#accessing-configuration)
|
18
20
|
- [Resetting Configuration](#resetting-configuration)
|
21
|
+
- [Error Handling](#error-handling)
|
19
22
|
|
20
23
|
## TLDR
|
21
24
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
```ruby
|
26
|
+
# Generate configuration file
|
27
|
+
rails g cmdx:install
|
28
|
+
|
29
|
+
# Global configuration
|
30
|
+
CMDx.configure do |config|
|
31
|
+
config.task_halt = ["failed", "skipped"] # Multiple halt statuses
|
32
|
+
config.logger = Rails.logger # Custom logger
|
33
|
+
config.middlewares.use TimeoutMiddleware # Global middleware
|
34
|
+
config.callbacks.register :on_failure, :log # Global callback
|
35
|
+
end
|
36
|
+
|
37
|
+
# Task-level overrides
|
38
|
+
class PaymentTask < CMDx::Task
|
39
|
+
cmd_settings!(task_halt: "failed", tags: ["payments"])
|
40
|
+
|
41
|
+
def call
|
42
|
+
halt_on = cmd_setting(:task_halt) # Access settings
|
43
|
+
end
|
44
|
+
end
|
45
|
+
```
|
28
46
|
|
29
47
|
## Configuration Hierarchy
|
30
48
|
|
31
49
|
CMDx follows a three-tier configuration hierarchy:
|
32
50
|
|
33
51
|
1. **Global Configuration**: Framework-wide defaults
|
34
|
-
2. **Task Settings**: Class-level overrides via `
|
52
|
+
2. **Task Settings**: Class-level overrides via `cmd_settings!`
|
35
53
|
3. **Runtime Parameters**: Instance-specific overrides during execution
|
36
54
|
|
37
55
|
> [!IMPORTANT]
|
@@ -45,7 +63,7 @@ Generate a configuration file using the Rails generator:
|
|
45
63
|
rails g cmdx:install
|
46
64
|
```
|
47
65
|
|
48
|
-
This creates `config/initializers/cmdx.rb` with
|
66
|
+
This creates `config/initializers/cmdx.rb` with sensible defaults.
|
49
67
|
|
50
68
|
### Configuration Options
|
51
69
|
|
@@ -56,65 +74,117 @@ This creates `config/initializers/cmdx.rb` with default settings.
|
|
56
74
|
| `logger` | Logger | Line formatter | Logger instance for task execution logging |
|
57
75
|
| `middlewares` | MiddlewareRegistry | Empty registry | Global middleware registry applied to all tasks |
|
58
76
|
| `callbacks` | CallbackRegistry | Empty registry | Global callback registry applied to all tasks |
|
77
|
+
| `coercions` | CoercionRegistry | Built-in coercions | Global coercion registry for custom parameter types |
|
78
|
+
| `validators` | ValidatorRegistry | Built-in validators | Global validator registry for parameter validation |
|
59
79
|
|
60
80
|
### Global Middlewares
|
61
81
|
|
62
|
-
Configure middlewares that automatically apply to all tasks
|
82
|
+
Configure middlewares that automatically apply to all tasks:
|
63
83
|
|
64
84
|
```ruby
|
65
85
|
CMDx.configure do |config|
|
66
|
-
#
|
86
|
+
# Simple middleware registration
|
67
87
|
config.middlewares.use CMDx::Middlewares::Timeout
|
68
88
|
|
69
|
-
#
|
89
|
+
# Middleware with configuration
|
70
90
|
config.middlewares.use CMDx::Middlewares::Timeout, seconds: 30
|
71
91
|
|
72
|
-
#
|
73
|
-
config.middlewares.use
|
92
|
+
# Multiple middlewares
|
93
|
+
config.middlewares.use AuthenticationMiddleware
|
94
|
+
config.middlewares.use LoggingMiddleware, level: :debug
|
95
|
+
config.middlewares.use MetricsMiddleware, namespace: "app.tasks"
|
74
96
|
end
|
75
97
|
```
|
76
98
|
|
99
|
+
> [!NOTE]
|
100
|
+
> Middlewares are executed in registration order. Each middleware wraps the next, creating an execution chain around task logic.
|
101
|
+
|
77
102
|
### Global Callbacks
|
78
103
|
|
79
|
-
Configure callbacks that automatically apply to all tasks
|
104
|
+
Configure callbacks that automatically apply to all tasks:
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
CMDx.configure do |config|
|
108
|
+
# Method callbacks
|
109
|
+
config.callbacks.register :before_execution, :setup_request_context
|
110
|
+
config.callbacks.register :after_execution, :cleanup_temp_files
|
111
|
+
|
112
|
+
# Conditional callbacks
|
113
|
+
config.callbacks.register :on_failure, :notify_admin, if: :production?
|
114
|
+
config.callbacks.register :on_success, :update_metrics, unless: :test?
|
115
|
+
|
116
|
+
# Proc callbacks with context
|
117
|
+
config.callbacks.register :on_complete, proc { |task, type|
|
118
|
+
duration = task.metadata[:runtime]
|
119
|
+
StatsD.histogram("task.duration", duration, tags: ["class:#{task.class.name}"])
|
120
|
+
}
|
121
|
+
end
|
122
|
+
```
|
123
|
+
|
124
|
+
### Global Coercions
|
125
|
+
|
126
|
+
Configure custom coercions for domain-specific types:
|
80
127
|
|
81
128
|
```ruby
|
82
129
|
CMDx.configure do |config|
|
83
|
-
#
|
84
|
-
config.
|
85
|
-
config.
|
130
|
+
# Simple coercion classes
|
131
|
+
config.coercions.register :money, MoneyCoercion
|
132
|
+
config.coercions.register :email, EmailCoercion
|
133
|
+
|
134
|
+
# Complex coercions with options
|
135
|
+
config.coercions.register :csv_array, proc { |value, options|
|
136
|
+
separator = options[:separator] || ','
|
137
|
+
max_items = options[:max_items] || 100
|
138
|
+
|
139
|
+
items = value.to_s.split(separator).map(&:strip).reject(&:empty?)
|
140
|
+
items.first(max_items)
|
141
|
+
}
|
142
|
+
end
|
143
|
+
```
|
144
|
+
|
145
|
+
### Global Validators
|
86
146
|
|
87
|
-
|
88
|
-
config.callbacks.register :on_success, NotificationCallback.new([:slack])
|
89
|
-
config.callbacks.register :on_failure, AlertCallback.new(severity: :critical)
|
147
|
+
Configure custom validators for parameter validation:
|
90
148
|
|
91
|
-
|
92
|
-
|
93
|
-
|
149
|
+
```ruby
|
150
|
+
CMDx.configure do |config|
|
151
|
+
# Validator classes
|
152
|
+
config.validators.register :email, EmailValidator
|
153
|
+
config.validators.register :phone, PhoneValidator
|
154
|
+
|
155
|
+
# Proc validators with options
|
156
|
+
config.validators.register :api_key, proc { |value, options|
|
157
|
+
required_prefix = options.dig(:api_key, :prefix) || "sk_"
|
158
|
+
min_length = options.dig(:api_key, :min_length) || 32
|
94
159
|
|
95
|
-
|
96
|
-
config.callbacks.register :on_complete, proc { |task, callback_type|
|
97
|
-
Metrics.increment("task.#{task.class.name.underscore}.completed")
|
160
|
+
value.start_with?(required_prefix) && value.length >= min_length
|
98
161
|
}
|
99
162
|
end
|
100
163
|
```
|
101
164
|
|
102
165
|
## Task Settings
|
103
166
|
|
104
|
-
Override global configuration for specific tasks
|
167
|
+
Override global configuration for specific tasks using `cmd_settings!`:
|
105
168
|
|
106
169
|
```ruby
|
107
170
|
class ProcessPaymentTask < CMDx::Task
|
108
|
-
|
109
|
-
task_halt: ["failed"],
|
110
|
-
tags: ["payments", "critical"],
|
111
|
-
logger:
|
112
|
-
log_level: :info,
|
113
|
-
log_formatter: CMDx::LogFormatters::Json.new
|
171
|
+
cmd_settings!(
|
172
|
+
task_halt: ["failed"], # Only halt on failures
|
173
|
+
tags: ["payments", "critical"], # Logging tags
|
174
|
+
logger: PaymentLogger.new, # Custom logger
|
175
|
+
log_level: :info, # Log level override
|
176
|
+
log_formatter: CMDx::LogFormatters::Json.new # JSON formatting
|
114
177
|
)
|
115
178
|
|
116
179
|
def call
|
117
|
-
#
|
180
|
+
# Payment processing logic
|
181
|
+
charge_customer(amount, payment_method)
|
182
|
+
end
|
183
|
+
|
184
|
+
private
|
185
|
+
|
186
|
+
def charge_customer(amount, method)
|
187
|
+
# Implementation details
|
118
188
|
end
|
119
189
|
end
|
120
190
|
```
|
@@ -130,18 +200,39 @@ end
|
|
130
200
|
| `log_level` | Symbol | Log level (`:debug`, `:info`, `:warn`, `:error`, `:fatal`) |
|
131
201
|
| `log_formatter` | LogFormatter | Custom log formatter |
|
132
202
|
|
203
|
+
> [!TIP]
|
204
|
+
> Use task-level settings for tasks that require special handling, such as payment processing, external API calls, or critical system operations.
|
205
|
+
|
133
206
|
### Workflow Configuration
|
134
207
|
|
135
|
-
Configure halt behavior for workflows:
|
208
|
+
Configure halt behavior and logging for workflows:
|
136
209
|
|
137
210
|
```ruby
|
138
211
|
class OrderProcessingWorkflow < CMDx::Workflow
|
139
|
-
#
|
140
|
-
|
212
|
+
# Halt on any non-success status
|
213
|
+
cmd_settings!(
|
214
|
+
workflow_halt: ["failed", "skipped"],
|
215
|
+
tags: ["orders", "e-commerce"],
|
216
|
+
log_level: :info
|
217
|
+
)
|
141
218
|
|
142
219
|
process ValidateOrderTask
|
143
220
|
process ChargePaymentTask
|
144
|
-
process
|
221
|
+
process UpdateInventoryTask
|
222
|
+
process SendConfirmationTask
|
223
|
+
end
|
224
|
+
|
225
|
+
class DataMigrationWorkflow < CMDx::Workflow
|
226
|
+
# Continue on skipped tasks, halt only on failures
|
227
|
+
cmd_settings!(
|
228
|
+
workflow_halt: "failed",
|
229
|
+
tags: ["migration", "maintenance"]
|
230
|
+
)
|
231
|
+
|
232
|
+
process BackupDataTask
|
233
|
+
process MigrateUsersTask
|
234
|
+
process MigrateOrdersTask
|
235
|
+
process ValidateDataTask
|
145
236
|
end
|
146
237
|
```
|
147
238
|
|
@@ -150,32 +241,103 @@ end
|
|
150
241
|
### Accessing Configuration
|
151
242
|
|
152
243
|
```ruby
|
153
|
-
# Global configuration
|
154
|
-
CMDx.configuration.logger
|
155
|
-
CMDx.configuration.task_halt
|
156
|
-
CMDx.configuration.middlewares
|
157
|
-
CMDx.configuration.callbacks
|
244
|
+
# Global configuration access
|
245
|
+
CMDx.configuration.logger #=> <Logger instance>
|
246
|
+
CMDx.configuration.task_halt #=> "failed"
|
247
|
+
CMDx.configuration.middlewares.middlewares #=> [<Middleware>, ...]
|
248
|
+
CMDx.configuration.callbacks.callbacks #=> {before_execution: [...], ...}
|
158
249
|
|
159
250
|
# Task-specific settings
|
160
|
-
class
|
161
|
-
|
251
|
+
class DataProcessingTask < CMDx::Task
|
252
|
+
cmd_settings!(
|
253
|
+
tags: ["data", "analytics"],
|
254
|
+
task_halt: ["failed", "skipped"]
|
255
|
+
)
|
162
256
|
|
163
257
|
def call
|
164
|
-
|
165
|
-
|
258
|
+
# Access current task settings
|
259
|
+
log_tags = cmd_setting(:tags) #=> ["data", "analytics"]
|
260
|
+
halt_on = cmd_setting(:task_halt) #=> ["failed", "skipped"]
|
261
|
+
logger_instance = cmd_setting(:logger) #=> Inherited from global
|
166
262
|
end
|
167
263
|
end
|
168
264
|
```
|
169
265
|
|
170
266
|
### Resetting Configuration
|
171
267
|
|
172
|
-
|
268
|
+
> [!WARNING]
|
269
|
+
> Resetting configuration affects the entire application. Use primarily in test environments or during application initialization.
|
173
270
|
|
174
271
|
```ruby
|
272
|
+
# Reset to framework defaults
|
175
273
|
CMDx.reset_configuration!
|
176
|
-
|
274
|
+
|
275
|
+
# Verify reset
|
276
|
+
CMDx.configuration.task_halt #=> "failed" (default)
|
277
|
+
CMDx.configuration.middlewares #=> Empty registry
|
278
|
+
CMDx.configuration.callbacks #=> Empty registry
|
279
|
+
|
280
|
+
# Commonly used in test setup
|
281
|
+
RSpec.configure do |config|
|
282
|
+
config.before(:each) do
|
283
|
+
CMDx.reset_configuration!
|
284
|
+
end
|
285
|
+
end
|
177
286
|
```
|
178
287
|
|
288
|
+
## Error Handling
|
289
|
+
|
290
|
+
### Configuration Validation
|
291
|
+
|
292
|
+
```ruby
|
293
|
+
# Invalid configuration types
|
294
|
+
CMDx.configure do |config|
|
295
|
+
config.task_halt = :invalid_type # Error: must be String or Array
|
296
|
+
config.logger = "not_a_logger" # Error: must respond to logging methods
|
297
|
+
end
|
298
|
+
```
|
299
|
+
|
300
|
+
### Missing Settings Access
|
301
|
+
|
302
|
+
```ruby
|
303
|
+
class ExampleTask < CMDx::Task
|
304
|
+
def call
|
305
|
+
# Accessing non-existent setting
|
306
|
+
value = cmd_setting(:non_existent_setting) #=> nil (returns nil for undefined)
|
307
|
+
|
308
|
+
# Check if setting exists
|
309
|
+
if cmd_setting(:custom_timeout)
|
310
|
+
timeout = cmd_setting(:custom_timeout)
|
311
|
+
else
|
312
|
+
timeout = 30 # fallback
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
```
|
317
|
+
|
318
|
+
### Configuration Conflicts
|
319
|
+
|
320
|
+
```ruby
|
321
|
+
# Parent class configuration
|
322
|
+
class BaseTask < CMDx::Task
|
323
|
+
cmd_settings!(task_halt: "failed", tags: ["base"])
|
324
|
+
end
|
325
|
+
|
326
|
+
# Child class inherits and overrides
|
327
|
+
class SpecialTask < BaseTask
|
328
|
+
cmd_settings!(task_halt: ["failed", "skipped"]) # Overrides parent
|
329
|
+
# tags: ["base"] inherited from parent
|
330
|
+
|
331
|
+
def call
|
332
|
+
halt_statuses = cmd_setting(:task_halt) #=> ["failed", "skipped"]
|
333
|
+
inherited_tags = cmd_setting(:tags) #=> ["base"]
|
334
|
+
end
|
335
|
+
end
|
336
|
+
```
|
337
|
+
|
338
|
+
> [!IMPORTANT]
|
339
|
+
> Settings inheritance follows Ruby's method resolution order. Child class settings always override parent class settings for the same key.
|
340
|
+
|
179
341
|
---
|
180
342
|
|
181
343
|
- **Prev:** [Getting Started](getting_started.md)
|
data/docs/deprecation.md
ADDED
@@ -0,0 +1,245 @@
|
|
1
|
+
# Task Deprecation
|
2
|
+
|
3
|
+
Task deprecation provides a systematic approach to managing legacy tasks in CMDx applications. The deprecation system enables controlled migration paths by issuing warnings, logging messages, or preventing execution of deprecated tasks entirely, helping teams maintain code quality while providing clear upgrade paths.
|
4
|
+
|
5
|
+
## Table of Contents
|
6
|
+
|
7
|
+
- [TLDR](#tldr)
|
8
|
+
- [Deprecation Fundamentals](#deprecation-fundamentals)
|
9
|
+
- [Deprecation Modes](#deprecation-modes)
|
10
|
+
- [Configuration Examples](#configuration-examples)
|
11
|
+
- [Migration Strategies](#migration-strategies)
|
12
|
+
- [Error Handling](#error-handling)
|
13
|
+
- [Best Practices](#best-practices)
|
14
|
+
|
15
|
+
## TLDR
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
# Prevent task execution completely
|
19
|
+
class LegacyTask < CMDx::Task
|
20
|
+
cmd_setting!(deprecated: :error)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Log deprecation warnings
|
24
|
+
class OldTask < CMDx::Task
|
25
|
+
cmd_setting!(deprecated: :log)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Issue Ruby warnings
|
29
|
+
class ObsoleteTask < CMDx::Task
|
30
|
+
cmd_setting!(deprecated: :warning)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Usage triggers appropriate deprecation handling
|
34
|
+
LegacyTask.call # → raises DeprecationError
|
35
|
+
OldTask.call # → logs warning via task.logger
|
36
|
+
ObsoleteTask.call # → issues Ruby warning
|
37
|
+
```
|
38
|
+
|
39
|
+
## Deprecation Fundamentals
|
40
|
+
|
41
|
+
> [!NOTE]
|
42
|
+
> Task deprecation is configured using the `cmd_setting!` declaration and processed automatically by CMDx before task execution. The deprecation system integrates seamlessly with existing logging and error handling infrastructure.
|
43
|
+
|
44
|
+
### How It Works
|
45
|
+
|
46
|
+
1. **Configuration**: Tasks declare deprecation mode using `cmd_setting!(deprecated: mode)`
|
47
|
+
2. **Processing**: CMDx automatically calls `TaskDeprecator.call(task)` during task lifecycle
|
48
|
+
3. **Action**: Appropriate deprecation handling occurs based on configured mode
|
49
|
+
4. **Execution**: Task proceeds normally (unless `:error` mode prevents it)
|
50
|
+
|
51
|
+
### Available Modes
|
52
|
+
|
53
|
+
| Mode | Behavior | Use Case |
|
54
|
+
|------|----------|----------|
|
55
|
+
| `:error` | Raises `DeprecationError` | Hard deprecation, prevent execution |
|
56
|
+
| `:log` | Logs warning via `task.logger.warn` | Soft deprecation, track usage |
|
57
|
+
| `:warning` | Issues Ruby warning | Development alerts |
|
58
|
+
| `true` | Same as `:log` | Legacy boolean support |
|
59
|
+
| `nil/false` | No deprecation handling | Default behavior |
|
60
|
+
|
61
|
+
## Deprecation Modes
|
62
|
+
|
63
|
+
### Error Mode (Hard Deprecation)
|
64
|
+
|
65
|
+
> [!WARNING]
|
66
|
+
> Error mode prevents task execution entirely. Use this for tasks that should no longer be used under any circumstances.
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
class ProcessLegacyPaymentTask < CMDx::Task
|
70
|
+
cmd_setting!(deprecated: :error)
|
71
|
+
|
72
|
+
def call
|
73
|
+
# This code will never execute
|
74
|
+
charge_customer(amount)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Attempting to use deprecated task
|
79
|
+
result = ProcessLegacyPaymentTask.call(amount: 100)
|
80
|
+
# → raises CMDx::DeprecationError: "ProcessLegacyPaymentTask usage prohibited"
|
81
|
+
```
|
82
|
+
|
83
|
+
### Log Mode (Soft Deprecation)
|
84
|
+
|
85
|
+
> [!TIP]
|
86
|
+
> Log mode allows continued usage while tracking deprecation warnings. Perfect for gradual migration scenarios where immediate replacement isn't feasible.
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
class ProcessOldPaymentTask < CMDx::Task
|
90
|
+
cmd_setting!(deprecated: :log)
|
91
|
+
|
92
|
+
def call
|
93
|
+
# Task executes normally but logs deprecation warning
|
94
|
+
charge_customer(amount)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Task executes with logged warning
|
99
|
+
result = ProcessOldPaymentTask.call(amount: 100)
|
100
|
+
result.successful? # → true
|
101
|
+
|
102
|
+
# Check logs for deprecation warning:
|
103
|
+
# WARN -- : DEPRECATED: migrate to replacement or discontinue use
|
104
|
+
```
|
105
|
+
|
106
|
+
### Warning Mode (Development Alerts)
|
107
|
+
|
108
|
+
> [!NOTE]
|
109
|
+
> Warning mode issues Ruby warnings visible in development and testing environments. Useful for alerting developers without affecting production logging.
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
class ProcessObsoletePaymentTask < CMDx::Task
|
113
|
+
cmd_setting!(deprecated: :warning)
|
114
|
+
|
115
|
+
def call
|
116
|
+
# Task executes with Ruby warning
|
117
|
+
charge_customer(amount)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Task executes with Ruby warning
|
122
|
+
result = ProcessObsoletePaymentTask.call(amount: 100)
|
123
|
+
# stderr: [ProcessObsoletePaymentTask] DEPRECATED: migrate to replacement or discontinue use
|
124
|
+
|
125
|
+
result.successful? # → true
|
126
|
+
```
|
127
|
+
|
128
|
+
## Configuration Examples
|
129
|
+
|
130
|
+
### Environment-Specific Deprecation
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
class ExperimentalFeatureTask < CMDx::Task
|
134
|
+
# Different deprecation behavior per environment
|
135
|
+
cmd_setting!(
|
136
|
+
deprecated: Rails.env.production? ? :error : :warning
|
137
|
+
)
|
138
|
+
|
139
|
+
def call
|
140
|
+
enable_experimental_feature
|
141
|
+
end
|
142
|
+
end
|
143
|
+
```
|
144
|
+
|
145
|
+
### Conditional Deprecation
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
class LegacyIntegrationTask < CMDx::Task
|
149
|
+
# Deprecate only for specific conditions
|
150
|
+
cmd_setting!(
|
151
|
+
deprecated: -> { ENV['NEW_API_ENABLED'] == 'true' ? :log : nil }
|
152
|
+
)
|
153
|
+
|
154
|
+
def call
|
155
|
+
call_legacy_api
|
156
|
+
end
|
157
|
+
end
|
158
|
+
```
|
159
|
+
|
160
|
+
## Migration Strategies
|
161
|
+
|
162
|
+
> [!IMPORTANT]
|
163
|
+
> When deprecating tasks, always provide clear migration paths and replacement implementations to minimize disruption.
|
164
|
+
|
165
|
+
### Graceful Fallback
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
class NotificationTask < CMDx::Task
|
169
|
+
cmd_setting!(deprecated: :log)
|
170
|
+
|
171
|
+
def call
|
172
|
+
# Provide fallback while encouraging migration
|
173
|
+
logger.warn "Consider migrating to NotificationServiceV2"
|
174
|
+
|
175
|
+
# Delegate to new service but maintain compatibility
|
176
|
+
NotificationServiceV2.send_notification(
|
177
|
+
recipient: recipient,
|
178
|
+
message: message,
|
179
|
+
delivery_method: :legacy
|
180
|
+
)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
```
|
184
|
+
|
185
|
+
## Error Handling
|
186
|
+
|
187
|
+
### Catching Deprecation Errors
|
188
|
+
|
189
|
+
```ruby
|
190
|
+
begin
|
191
|
+
result = LegacyTask.call(params)
|
192
|
+
rescue CMDx::DeprecationError => e
|
193
|
+
# Handle deprecation gracefully
|
194
|
+
Rails.logger.error "Attempted to use deprecated task: #{e.message}"
|
195
|
+
|
196
|
+
# Use replacement task instead
|
197
|
+
result = ReplacementTask.call(params)
|
198
|
+
end
|
199
|
+
|
200
|
+
if result.successful?
|
201
|
+
# Process successful result
|
202
|
+
else
|
203
|
+
# Handle task failure
|
204
|
+
end
|
205
|
+
```
|
206
|
+
|
207
|
+
## Best Practices
|
208
|
+
|
209
|
+
### Documentation and Communication
|
210
|
+
|
211
|
+
> [!TIP]
|
212
|
+
> Always document deprecation reasons, timelines, and migration paths. Clear communication prevents confusion and reduces support burden.
|
213
|
+
|
214
|
+
```ruby
|
215
|
+
class LegacyReportTask < CMDx::Task
|
216
|
+
# Document deprecation clearly
|
217
|
+
cmd_setting!(deprecated: :log)
|
218
|
+
|
219
|
+
# Class-level documentation
|
220
|
+
# @deprecated Use ReportGeneratorV2Task instead
|
221
|
+
# @see ReportGeneratorV2Task
|
222
|
+
# @note This task will be removed in v2.0.0
|
223
|
+
# @since 1.5.0 marked as deprecated
|
224
|
+
|
225
|
+
def call
|
226
|
+
# Add inline documentation
|
227
|
+
logger.warn <<~DEPRECATION
|
228
|
+
LegacyReportTask is deprecated and will be removed in v2.0.0.
|
229
|
+
Please migrate to ReportGeneratorV2Task which provides:
|
230
|
+
- Better performance
|
231
|
+
- Enhanced error handling
|
232
|
+
- More flexible output formats
|
233
|
+
|
234
|
+
Migration guide: https://docs.example.com/migration/reports
|
235
|
+
DEPRECATION
|
236
|
+
|
237
|
+
generate_legacy_report
|
238
|
+
end
|
239
|
+
end
|
240
|
+
```
|
241
|
+
|
242
|
+
---
|
243
|
+
|
244
|
+
- **Prev:** [Testing](testing.md)
|
245
|
+
- **Next:** [AI Prompts](ai_prompts.md)
|