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.
Files changed (193) hide show
  1. checksums.yaml +4 -4
  2. data/.DS_Store +0 -0
  3. data/.cursor/prompts/docs.md +4 -1
  4. data/.cursor/prompts/llms.md +20 -0
  5. data/.cursor/prompts/rspec.md +4 -1
  6. data/.cursor/prompts/yardoc.md +3 -2
  7. data/.cursor/rules/cursor-instructions.mdc +56 -1
  8. data/.irbrc +6 -0
  9. data/.rubocop.yml +29 -18
  10. data/.ruby-version +1 -1
  11. data/CHANGELOG.md +6 -128
  12. data/LLM.md +3317 -0
  13. data/README.md +68 -44
  14. data/docs/attributes/coercions.md +162 -0
  15. data/docs/attributes/defaults.md +90 -0
  16. data/docs/attributes/definitions.md +281 -0
  17. data/docs/attributes/naming.md +78 -0
  18. data/docs/attributes/validations.md +309 -0
  19. data/docs/basics/chain.md +56 -249
  20. data/docs/basics/context.md +56 -289
  21. data/docs/basics/execution.md +114 -0
  22. data/docs/basics/setup.md +37 -334
  23. data/docs/callbacks.md +89 -467
  24. data/docs/deprecation.md +91 -174
  25. data/docs/getting_started.md +212 -202
  26. data/docs/internationalization.md +11 -647
  27. data/docs/interruptions/exceptions.md +23 -198
  28. data/docs/interruptions/faults.md +71 -151
  29. data/docs/interruptions/halt.md +109 -186
  30. data/docs/logging.md +44 -256
  31. data/docs/middlewares.md +113 -426
  32. data/docs/outcomes/result.md +81 -228
  33. data/docs/outcomes/states.md +33 -221
  34. data/docs/outcomes/statuses.md +21 -311
  35. data/docs/tips_and_tricks.md +120 -70
  36. data/docs/workflows.md +99 -283
  37. data/lib/cmdx/.DS_Store +0 -0
  38. data/lib/cmdx/attribute.rb +229 -0
  39. data/lib/cmdx/attribute_registry.rb +94 -0
  40. data/lib/cmdx/attribute_value.rb +193 -0
  41. data/lib/cmdx/callback_registry.rb +69 -77
  42. data/lib/cmdx/chain.rb +56 -73
  43. data/lib/cmdx/coercion_registry.rb +52 -68
  44. data/lib/cmdx/coercions/array.rb +19 -18
  45. data/lib/cmdx/coercions/big_decimal.rb +20 -24
  46. data/lib/cmdx/coercions/boolean.rb +26 -25
  47. data/lib/cmdx/coercions/complex.rb +21 -22
  48. data/lib/cmdx/coercions/date.rb +25 -23
  49. data/lib/cmdx/coercions/date_time.rb +24 -25
  50. data/lib/cmdx/coercions/float.rb +25 -22
  51. data/lib/cmdx/coercions/hash.rb +31 -32
  52. data/lib/cmdx/coercions/integer.rb +30 -24
  53. data/lib/cmdx/coercions/rational.rb +29 -24
  54. data/lib/cmdx/coercions/string.rb +19 -22
  55. data/lib/cmdx/coercions/symbol.rb +37 -0
  56. data/lib/cmdx/coercions/time.rb +26 -25
  57. data/lib/cmdx/configuration.rb +49 -108
  58. data/lib/cmdx/context.rb +222 -44
  59. data/lib/cmdx/deprecator.rb +61 -0
  60. data/lib/cmdx/errors.rb +42 -252
  61. data/lib/cmdx/exceptions.rb +39 -0
  62. data/lib/cmdx/faults.rb +78 -39
  63. data/lib/cmdx/freezer.rb +51 -0
  64. data/lib/cmdx/identifier.rb +30 -0
  65. data/lib/cmdx/locale.rb +52 -0
  66. data/lib/cmdx/log_formatters/json.rb +21 -22
  67. data/lib/cmdx/log_formatters/key_value.rb +20 -22
  68. data/lib/cmdx/log_formatters/line.rb +15 -22
  69. data/lib/cmdx/log_formatters/logstash.rb +22 -23
  70. data/lib/cmdx/log_formatters/raw.rb +16 -22
  71. data/lib/cmdx/middleware_registry.rb +70 -74
  72. data/lib/cmdx/middlewares/correlate.rb +90 -54
  73. data/lib/cmdx/middlewares/runtime.rb +58 -0
  74. data/lib/cmdx/middlewares/timeout.rb +48 -68
  75. data/lib/cmdx/railtie.rb +12 -45
  76. data/lib/cmdx/result.rb +229 -314
  77. data/lib/cmdx/task.rb +194 -366
  78. data/lib/cmdx/utils/call.rb +49 -0
  79. data/lib/cmdx/utils/condition.rb +71 -0
  80. data/lib/cmdx/utils/format.rb +61 -0
  81. data/lib/cmdx/validator_registry.rb +63 -72
  82. data/lib/cmdx/validators/exclusion.rb +38 -67
  83. data/lib/cmdx/validators/format.rb +48 -49
  84. data/lib/cmdx/validators/inclusion.rb +43 -74
  85. data/lib/cmdx/validators/length.rb +91 -154
  86. data/lib/cmdx/validators/numeric.rb +87 -162
  87. data/lib/cmdx/validators/presence.rb +37 -50
  88. data/lib/cmdx/version.rb +1 -1
  89. data/lib/cmdx/worker.rb +178 -0
  90. data/lib/cmdx/workflow.rb +85 -81
  91. data/lib/cmdx.rb +19 -13
  92. data/lib/generators/cmdx/install_generator.rb +14 -13
  93. data/lib/generators/cmdx/task_generator.rb +25 -50
  94. data/lib/generators/cmdx/templates/install.rb +11 -46
  95. data/lib/generators/cmdx/templates/task.rb.tt +3 -2
  96. data/lib/locales/en.yml +18 -4
  97. data/src/cmdx-logo.png +0 -0
  98. metadata +32 -116
  99. data/docs/ai_prompts.md +0 -393
  100. data/docs/basics/call.md +0 -317
  101. data/docs/configuration.md +0 -344
  102. data/docs/parameters/coercions.md +0 -396
  103. data/docs/parameters/defaults.md +0 -335
  104. data/docs/parameters/definitions.md +0 -446
  105. data/docs/parameters/namespacing.md +0 -378
  106. data/docs/parameters/validations.md +0 -405
  107. data/docs/testing.md +0 -553
  108. data/lib/cmdx/callback.rb +0 -53
  109. data/lib/cmdx/chain_inspector.rb +0 -56
  110. data/lib/cmdx/chain_serializer.rb +0 -63
  111. data/lib/cmdx/coercion.rb +0 -57
  112. data/lib/cmdx/coercions/virtual.rb +0 -29
  113. data/lib/cmdx/core_ext/hash.rb +0 -83
  114. data/lib/cmdx/core_ext/module.rb +0 -98
  115. data/lib/cmdx/core_ext/object.rb +0 -125
  116. data/lib/cmdx/correlator.rb +0 -122
  117. data/lib/cmdx/error.rb +0 -60
  118. data/lib/cmdx/fault.rb +0 -140
  119. data/lib/cmdx/immutator.rb +0 -52
  120. data/lib/cmdx/lazy_struct.rb +0 -246
  121. data/lib/cmdx/log_formatters/pretty_json.rb +0 -40
  122. data/lib/cmdx/log_formatters/pretty_key_value.rb +0 -38
  123. data/lib/cmdx/log_formatters/pretty_line.rb +0 -41
  124. data/lib/cmdx/logger.rb +0 -49
  125. data/lib/cmdx/logger_ansi.rb +0 -68
  126. data/lib/cmdx/logger_serializer.rb +0 -116
  127. data/lib/cmdx/middleware.rb +0 -70
  128. data/lib/cmdx/parameter.rb +0 -312
  129. data/lib/cmdx/parameter_evaluator.rb +0 -231
  130. data/lib/cmdx/parameter_inspector.rb +0 -66
  131. data/lib/cmdx/parameter_registry.rb +0 -106
  132. data/lib/cmdx/parameter_serializer.rb +0 -59
  133. data/lib/cmdx/result_ansi.rb +0 -71
  134. data/lib/cmdx/result_inspector.rb +0 -71
  135. data/lib/cmdx/result_logger.rb +0 -59
  136. data/lib/cmdx/result_serializer.rb +0 -104
  137. data/lib/cmdx/rspec/matchers.rb +0 -28
  138. data/lib/cmdx/rspec/result_matchers/be_executed.rb +0 -42
  139. data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +0 -94
  140. data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +0 -94
  141. data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +0 -59
  142. data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +0 -57
  143. data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +0 -87
  144. data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +0 -51
  145. data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +0 -58
  146. data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +0 -59
  147. data/lib/cmdx/rspec/result_matchers/have_context.rb +0 -86
  148. data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +0 -54
  149. data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +0 -52
  150. data/lib/cmdx/rspec/result_matchers/have_metadata.rb +0 -114
  151. data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +0 -66
  152. data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +0 -64
  153. data/lib/cmdx/rspec/result_matchers/have_runtime.rb +0 -78
  154. data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +0 -76
  155. data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +0 -62
  156. data/lib/cmdx/rspec/task_matchers/have_callback.rb +0 -85
  157. data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +0 -68
  158. data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +0 -92
  159. data/lib/cmdx/rspec/task_matchers/have_middleware.rb +0 -46
  160. data/lib/cmdx/rspec/task_matchers/have_parameter.rb +0 -181
  161. data/lib/cmdx/task_deprecator.rb +0 -52
  162. data/lib/cmdx/task_processor.rb +0 -246
  163. data/lib/cmdx/task_serializer.rb +0 -57
  164. data/lib/cmdx/utils/ansi_color.rb +0 -73
  165. data/lib/cmdx/utils/log_timestamp.rb +0 -36
  166. data/lib/cmdx/utils/monotonic_runtime.rb +0 -34
  167. data/lib/cmdx/utils/name_affix.rb +0 -52
  168. data/lib/cmdx/validator.rb +0 -57
  169. data/lib/generators/cmdx/templates/workflow.rb.tt +0 -7
  170. data/lib/generators/cmdx/workflow_generator.rb +0 -84
  171. data/lib/locales/ar.yml +0 -35
  172. data/lib/locales/cs.yml +0 -35
  173. data/lib/locales/da.yml +0 -35
  174. data/lib/locales/de.yml +0 -35
  175. data/lib/locales/el.yml +0 -35
  176. data/lib/locales/es.yml +0 -35
  177. data/lib/locales/fi.yml +0 -35
  178. data/lib/locales/fr.yml +0 -35
  179. data/lib/locales/he.yml +0 -35
  180. data/lib/locales/hi.yml +0 -35
  181. data/lib/locales/it.yml +0 -35
  182. data/lib/locales/ja.yml +0 -35
  183. data/lib/locales/ko.yml +0 -35
  184. data/lib/locales/nl.yml +0 -35
  185. data/lib/locales/no.yml +0 -35
  186. data/lib/locales/pl.yml +0 -35
  187. data/lib/locales/pt.yml +0 -35
  188. data/lib/locales/ru.yml +0 -35
  189. data/lib/locales/sv.yml +0 -35
  190. data/lib/locales/th.yml +0 -35
  191. data/lib/locales/tr.yml +0 -35
  192. data/lib/locales/vi.yml +0 -35
  193. data/lib/locales/zh.yml +0 -35
@@ -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 parameter validation, structured error handling, comprehensive logging, and intelligent execution flow control that scales from simple tasks to complex multi-step processes.
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
- - [Quick Setup](#quick-setup)
10
- - [Execution](#execution)
11
- - [Result Handling](#result-handling)
12
- - [Exception Handling](#exception-handling)
13
- - [Building Workflows](#building-workflows)
14
- - [Code Generation](#code-generation)
15
-
16
- ## TLDR
17
-
18
- ```ruby
19
- # Installation
20
- gem 'cmdx' # Add to Gemfile
21
- rails g cmdx:install # Generate config
22
-
23
- # Basic task
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
- > [!NOTE]
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
- ## Quick Setup
40
+ ## Configuration Hierarchy
64
41
 
65
- > [!TIP]
66
- > Use **present tense verbs** for task names: `ProcessOrderTask`, `SendEmailTask`, `ValidatePaymentTask`
42
+ CMDx follows a two-tier configuration hierarchy:
67
43
 
68
- ```ruby
69
- class ProcessOrderTask < CMDx::Task
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
- ### Parameter Definition
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
- Parameters provide automatic type coercion and validation:
50
+ ## Global Configuration
91
51
 
92
- ```ruby
93
- class CreateUserTask < CMDx::Task
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
- ## Execution
55
+ ### Breakpoints
114
56
 
115
- Execute tasks using class methods that return result objects or raise exceptions:
57
+ Breakpoints control when `execute!` raises faults.
116
58
 
117
59
  ```ruby
118
- # Safe execution - returns Result object
119
- result = ProcessOrderTask.call(order_id: 123)
120
-
121
- # Exception-based execution - raises on failure/skip
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
- > [!IMPORTANT]
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
- ### Input Coercion
68
+ ```ruby
69
+ CMDx.configure do |config|
70
+ config.logger = CustomLogger.new($stdout)
71
+ end
72
+ ```
129
73
 
130
- Parameters automatically coerce string inputs to specified types:
74
+ ### Middlewares
131
75
 
132
76
  ```ruby
133
- # String inputs automatically converted
134
- ProcessOrderTask.call(
135
- order_id: "123", # → 123 (Integer)
136
- send_email: "false" # → false (Boolean)
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
- ## Result Handling
98
+ > [!NOTE]
99
+ > Middlewares are executed in registration order. Each middleware wraps the next, creating an execution chain around task logic.
141
100
 
142
- Results provide comprehensive execution information including status, context data, and metadata:
101
+ ### Callbacks
143
102
 
144
103
  ```ruby
145
- result = ProcessOrderTask.call(order_id: 123)
104
+ CMDx.configure do |config|
105
+ # Via method
106
+ config.callbacks.register :before_execution, :initialize_user_session
146
107
 
147
- case result.status
148
- when 'success'
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
- when 'skipped'
153
- reason = result.metadata[:reason]
154
- redirect_to order_path(123), notice: "Skipped: #{reason}"
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
- when 'failed'
157
- error_details = result.metadata[:reason]
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
- # Access execution metadata
162
- puts "Runtime: #{result.runtime}ms"
163
- puts "Task ID: #{result.id}"
164
- puts "Executed at: #{result.executed_at}"
120
+ # Remove callback
121
+ config.callbacks.deregister :on_success, LogUserActivity
122
+ end
165
123
  ```
166
124
 
167
- ### Result Properties
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
- | Property | Description | Example |
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
- ## Exception Handling
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
- > [!WARNING]
180
- > `call!` raises exceptions for failed or skipped tasks. Use this pattern when failures should halt program execution.
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
- begin
184
- result = ProcessOrderTask.call!(order_id: 123)
185
- redirect_to order_path(result.context.order), notice: "Order processed!"
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
- rescue CMDx::Skipped => e
188
- reason = e.result.metadata[:reason]
189
- redirect_to orders_path, notice: "Skipped: #{reason}"
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
- rescue CMDx::Failed => e
192
- error_details = e.result.metadata[:reason]
193
- redirect_to order_path(123), alert: "Failed: #{error_details}"
164
+ value.start_with?(expected_prefix) && value.length >= minimum_length
165
+ }
194
166
 
195
- rescue ActiveRecord::RecordNotFound
196
- redirect_to orders_path, alert: "Order not found"
167
+ # Remove validator
168
+ config.validators.deregister :username
197
169
  end
198
170
  ```
199
171
 
200
- ### Exception Types
172
+ ## Task Configuration
201
173
 
202
- - **`CMDx::Skipped`** - Task was skipped intentionally
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
- ## Building Workflows
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 OrderProcessingWorkflow < CMDx::Workflow
213
- required :order_id, type: :integer
214
- optional :priority, type: :string, default: 'standard'
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
- before_execution :log_workflow_start
217
- on_failed :notify_support
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
- process ValidateOrderTask
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
- private
205
+ Register middlewares, callbacks, coercions, and validators on a specific task.
206
+ Deregister options that should not be available.
227
207
 
228
- def log_workflow_start
229
- Rails.logger.info "Starting order processing for order #{order_id}"
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
- def notify_support
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
- def log_skip_reason
240
- Rails.logger.warn "Workflow skipped: #{result.metadata[:reason]}"
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
- ### Workflow Features
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
- ## Code Generation
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
- # Generate individual task
262
- rails generate cmdx:task ProcessOrder
263
- # Creates: app/cmds/process_order_task.rb
262
+ # Reset to framework defaults
263
+ CMDx.reset_configuration!
264
264
 
265
- # Generate workflow
266
- rails generate cmdx:workflow OrderProcessing
267
- # Creates: app/cmds/order_processing_workflow.rb
265
+ # Verify reset
266
+ CMDx.configuration.task_breakpoints #=> ["failed"] (default)
267
+ CMDx.configuration.middlewares.registry #=> Empty registry
268
268
 
269
- # Generate with parameters
270
- rails generate cmdx:task CreateUser email:string age:integer active:boolean
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
- > [!NOTE]
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
- ### Generated Task Structure
279
+ Generate new CMDx tasks quickly using the built-in generator:
277
280
 
278
- ```ruby
279
- # app/cmds/process_order_task.rb
280
- class ProcessOrderTask < ApplicationTask
281
- required :order_id, type: :integer
281
+ ```bash
282
+ rails generate cmdx:task ModerateBlogPost
283
+ ```
282
284
 
283
- def call
284
- # Task implementation
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
- - **Next:** [Configuration](configuration.md)
292
- - **See also:** [Parameters - Coercions](parameters/coercions.md) | [Workflows](workflows.md)
301
+ - **Prev:** [Tips and Tricks](tips_and_tricks.md)
302
+ - **Next:** [Basics - Setup](basics/setup.md)