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.
Files changed (170) hide show
  1. checksums.yaml +4 -4
  2. data/.cursor/prompts/docs.md +9 -0
  3. data/.cursor/prompts/rspec.md +21 -0
  4. data/.cursor/prompts/yardoc.md +13 -0
  5. data/.rubocop.yml +2 -0
  6. data/CHANGELOG.md +29 -3
  7. data/README.md +2 -1
  8. data/docs/ai_prompts.md +269 -195
  9. data/docs/basics/call.md +126 -60
  10. data/docs/basics/chain.md +190 -160
  11. data/docs/basics/context.md +242 -154
  12. data/docs/basics/setup.md +302 -32
  13. data/docs/callbacks.md +382 -119
  14. data/docs/configuration.md +211 -49
  15. data/docs/deprecation.md +245 -0
  16. data/docs/getting_started.md +161 -39
  17. data/docs/internationalization.md +590 -70
  18. data/docs/interruptions/exceptions.md +135 -118
  19. data/docs/interruptions/faults.md +152 -127
  20. data/docs/interruptions/halt.md +134 -80
  21. data/docs/logging.md +183 -120
  22. data/docs/middlewares.md +165 -392
  23. data/docs/outcomes/result.md +140 -112
  24. data/docs/outcomes/states.md +134 -99
  25. data/docs/outcomes/statuses.md +204 -146
  26. data/docs/parameters/coercions.md +251 -289
  27. data/docs/parameters/defaults.md +224 -169
  28. data/docs/parameters/definitions.md +289 -141
  29. data/docs/parameters/namespacing.md +250 -161
  30. data/docs/parameters/validations.md +247 -159
  31. data/docs/testing.md +196 -203
  32. data/docs/workflows.md +146 -101
  33. data/lib/cmdx/.DS_Store +0 -0
  34. data/lib/cmdx/callback.rb +39 -55
  35. data/lib/cmdx/callback_registry.rb +80 -73
  36. data/lib/cmdx/chain.rb +65 -122
  37. data/lib/cmdx/chain_inspector.rb +23 -116
  38. data/lib/cmdx/chain_serializer.rb +34 -146
  39. data/lib/cmdx/coercion.rb +57 -0
  40. data/lib/cmdx/coercion_registry.rb +113 -0
  41. data/lib/cmdx/coercions/array.rb +18 -36
  42. data/lib/cmdx/coercions/big_decimal.rb +21 -33
  43. data/lib/cmdx/coercions/boolean.rb +21 -40
  44. data/lib/cmdx/coercions/complex.rb +18 -31
  45. data/lib/cmdx/coercions/date.rb +20 -39
  46. data/lib/cmdx/coercions/date_time.rb +22 -39
  47. data/lib/cmdx/coercions/float.rb +19 -32
  48. data/lib/cmdx/coercions/hash.rb +22 -41
  49. data/lib/cmdx/coercions/integer.rb +20 -33
  50. data/lib/cmdx/coercions/rational.rb +20 -32
  51. data/lib/cmdx/coercions/string.rb +23 -31
  52. data/lib/cmdx/coercions/time.rb +24 -40
  53. data/lib/cmdx/coercions/virtual.rb +14 -31
  54. data/lib/cmdx/configuration.rb +101 -162
  55. data/lib/cmdx/context.rb +34 -166
  56. data/lib/cmdx/core_ext/hash.rb +42 -67
  57. data/lib/cmdx/core_ext/module.rb +35 -79
  58. data/lib/cmdx/core_ext/object.rb +63 -98
  59. data/lib/cmdx/correlator.rb +59 -154
  60. data/lib/cmdx/error.rb +37 -202
  61. data/lib/cmdx/errors.rb +153 -216
  62. data/lib/cmdx/fault.rb +68 -150
  63. data/lib/cmdx/faults.rb +26 -137
  64. data/lib/cmdx/immutator.rb +22 -110
  65. data/lib/cmdx/lazy_struct.rb +110 -186
  66. data/lib/cmdx/log_formatters/json.rb +14 -40
  67. data/lib/cmdx/log_formatters/key_value.rb +14 -40
  68. data/lib/cmdx/log_formatters/line.rb +14 -48
  69. data/lib/cmdx/log_formatters/logstash.rb +14 -57
  70. data/lib/cmdx/log_formatters/pretty_json.rb +14 -50
  71. data/lib/cmdx/log_formatters/pretty_key_value.rb +13 -46
  72. data/lib/cmdx/log_formatters/pretty_line.rb +16 -54
  73. data/lib/cmdx/log_formatters/raw.rb +19 -49
  74. data/lib/cmdx/logger.rb +22 -79
  75. data/lib/cmdx/logger_ansi.rb +31 -72
  76. data/lib/cmdx/logger_serializer.rb +74 -103
  77. data/lib/cmdx/middleware.rb +56 -60
  78. data/lib/cmdx/middleware_registry.rb +82 -77
  79. data/lib/cmdx/middlewares/correlate.rb +41 -226
  80. data/lib/cmdx/middlewares/timeout.rb +46 -185
  81. data/lib/cmdx/parameter.rb +167 -183
  82. data/lib/cmdx/parameter_evaluator.rb +231 -0
  83. data/lib/cmdx/parameter_inspector.rb +37 -55
  84. data/lib/cmdx/parameter_registry.rb +65 -84
  85. data/lib/cmdx/parameter_serializer.rb +32 -76
  86. data/lib/cmdx/railtie.rb +24 -107
  87. data/lib/cmdx/result.rb +254 -259
  88. data/lib/cmdx/result_ansi.rb +28 -80
  89. data/lib/cmdx/result_inspector.rb +34 -70
  90. data/lib/cmdx/result_logger.rb +23 -77
  91. data/lib/cmdx/result_serializer.rb +59 -125
  92. data/lib/cmdx/rspec/matchers.rb +28 -0
  93. data/lib/cmdx/rspec/result_matchers/be_executed.rb +42 -0
  94. data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +94 -0
  95. data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +94 -0
  96. data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +59 -0
  97. data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +57 -0
  98. data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +87 -0
  99. data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +51 -0
  100. data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +58 -0
  101. data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +59 -0
  102. data/lib/cmdx/rspec/result_matchers/have_context.rb +86 -0
  103. data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +54 -0
  104. data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +52 -0
  105. data/lib/cmdx/rspec/result_matchers/have_metadata.rb +114 -0
  106. data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +66 -0
  107. data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +64 -0
  108. data/lib/cmdx/rspec/result_matchers/have_runtime.rb +78 -0
  109. data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +76 -0
  110. data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +62 -0
  111. data/lib/cmdx/rspec/task_matchers/have_callback.rb +85 -0
  112. data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +68 -0
  113. data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +92 -0
  114. data/lib/cmdx/rspec/task_matchers/have_middleware.rb +46 -0
  115. data/lib/cmdx/rspec/task_matchers/have_parameter.rb +181 -0
  116. data/lib/cmdx/task.rb +336 -427
  117. data/lib/cmdx/task_deprecator.rb +52 -0
  118. data/lib/cmdx/task_processor.rb +246 -0
  119. data/lib/cmdx/task_serializer.rb +34 -69
  120. data/lib/cmdx/utils/ansi_color.rb +13 -89
  121. data/lib/cmdx/utils/log_timestamp.rb +13 -42
  122. data/lib/cmdx/utils/monotonic_runtime.rb +11 -63
  123. data/lib/cmdx/utils/name_affix.rb +21 -71
  124. data/lib/cmdx/validator.rb +57 -0
  125. data/lib/cmdx/validator_registry.rb +108 -0
  126. data/lib/cmdx/validators/exclusion.rb +55 -94
  127. data/lib/cmdx/validators/format.rb +31 -85
  128. data/lib/cmdx/validators/inclusion.rb +65 -110
  129. data/lib/cmdx/validators/length.rb +117 -133
  130. data/lib/cmdx/validators/numeric.rb +123 -130
  131. data/lib/cmdx/validators/presence.rb +38 -79
  132. data/lib/cmdx/version.rb +1 -7
  133. data/lib/cmdx/workflow.rb +58 -330
  134. data/lib/cmdx.rb +1 -1
  135. data/lib/generators/cmdx/install_generator.rb +14 -31
  136. data/lib/generators/cmdx/task_generator.rb +39 -55
  137. data/lib/generators/cmdx/templates/install.rb +24 -6
  138. data/lib/generators/cmdx/workflow_generator.rb +41 -66
  139. data/lib/locales/ar.yml +0 -1
  140. data/lib/locales/cs.yml +0 -1
  141. data/lib/locales/da.yml +0 -1
  142. data/lib/locales/de.yml +0 -1
  143. data/lib/locales/el.yml +0 -1
  144. data/lib/locales/en.yml +0 -1
  145. data/lib/locales/es.yml +0 -1
  146. data/lib/locales/fi.yml +0 -1
  147. data/lib/locales/fr.yml +0 -1
  148. data/lib/locales/he.yml +0 -1
  149. data/lib/locales/hi.yml +0 -1
  150. data/lib/locales/it.yml +0 -1
  151. data/lib/locales/ja.yml +0 -1
  152. data/lib/locales/ko.yml +0 -1
  153. data/lib/locales/nl.yml +0 -1
  154. data/lib/locales/no.yml +0 -1
  155. data/lib/locales/pl.yml +0 -1
  156. data/lib/locales/pt.yml +0 -1
  157. data/lib/locales/ru.yml +0 -1
  158. data/lib/locales/sv.yml +0 -1
  159. data/lib/locales/th.yml +0 -1
  160. data/lib/locales/tr.yml +0 -1
  161. data/lib/locales/vi.yml +0 -1
  162. data/lib/locales/zh.yml +0 -1
  163. metadata +36 -8
  164. data/lib/cmdx/parameter_validator.rb +0 -81
  165. data/lib/cmdx/parameter_value.rb +0 -244
  166. data/lib/cmdx/parameters_inspector.rb +0 -72
  167. data/lib/cmdx/parameters_serializer.rb +0 -115
  168. data/lib/cmdx/rspec/result_matchers.rb +0 -917
  169. data/lib/cmdx/rspec/task_matchers.rb +0 -570
  170. data/lib/cmdx/validators/custom.rb +0 -102
@@ -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
- - **Hierarchy** - Global → Task Settings → Runtime (each level overrides previous)
23
- - **Global config** - Framework-wide defaults via `CMDx.configure`
24
- - **Task settings** - Class-level overrides using `task_settings!`
25
- - **Key options** - `task_halt`, `workflow_halt`, `logger`, `middlewares`, `callbacks`
26
- - **Generator** - Use `rails g cmdx:install` to create configuration file
27
- - **Inheritance** - Settings are inherited from parent classes
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 `task_settings!`
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 default settings.
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 in your application:
82
+ Configure middlewares that automatically apply to all tasks:
63
83
 
64
84
  ```ruby
65
85
  CMDx.configure do |config|
66
- # Add middlewares without arguments
86
+ # Simple middleware registration
67
87
  config.middlewares.use CMDx::Middlewares::Timeout
68
88
 
69
- # Add middlewares with arguments
89
+ # Middleware with configuration
70
90
  config.middlewares.use CMDx::Middlewares::Timeout, seconds: 30
71
91
 
72
- # Add middleware instances
73
- config.middlewares.use CMDx::Middlewares::Timeout.new(seconds: 30)
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 in your application:
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
- # Add method callbacks
84
- config.callbacks.register :before_execution, :log_task_start
85
- config.callbacks.register :after_execution, :log_task_end
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
- # Add callback instances
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
- # Add conditional callbacks
92
- config.callbacks.register :on_failure, :page_admin, if: :production?
93
- config.callbacks.register :before_validation, :skip_validation, unless: :validate_params?
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
- # Add proc callbacks
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 or workflows using `task_settings!`:
167
+ Override global configuration for specific tasks using `cmd_settings!`:
105
168
 
106
169
  ```ruby
107
170
  class ProcessPaymentTask < CMDx::Task
108
- task_settings!(
109
- task_halt: ["failed"], # Only halt on failures
110
- tags: ["payments", "critical"], # Add logging tags
111
- logger: Rails.logger, # Use Rails logger
112
- log_level: :info, # Set log level
113
- log_formatter: CMDx::LogFormatters::Json.new # JSON formatter
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
- # Process payment logic
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
- # Strict workflow - halt on any failure
140
- task_settings!(workflow_halt: ["failed", "skipped"])
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 FulfillOrderTask
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 #=> <Logger instance>
155
- CMDx.configuration.task_halt #=> "failed"
156
- CMDx.configuration.middlewares #=> <MiddlewareRegistry instance>
157
- CMDx.configuration.callbacks #=> <CallbackRegistry instance>
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 AnalyzeDataTask < CMDx::Task
161
- task_settings!(tags: ["analytics"])
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
- tags = task_setting(:tags) # Gets ["analytics"]
165
- halt_statuses = task_setting(:task_halt) # Gets global default
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
- Reset configuration to defaults (useful for testing):
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
- CMDx.configuration.task_halt #=> "failed" (default)
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)
@@ -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)