cmdx 1.1.0 → 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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/.cursor/prompts/docs.md +9 -0
  3. data/.cursor/prompts/rspec.md +13 -12
  4. data/.cursor/prompts/yardoc.md +11 -6
  5. data/CHANGELOG.md +13 -2
  6. data/README.md +1 -0
  7. data/docs/ai_prompts.md +269 -195
  8. data/docs/basics/call.md +124 -58
  9. data/docs/basics/chain.md +190 -160
  10. data/docs/basics/context.md +242 -154
  11. data/docs/basics/setup.md +302 -32
  12. data/docs/callbacks.md +390 -94
  13. data/docs/configuration.md +181 -65
  14. data/docs/deprecation.md +245 -0
  15. data/docs/getting_started.md +161 -39
  16. data/docs/internationalization.md +590 -70
  17. data/docs/interruptions/exceptions.md +135 -118
  18. data/docs/interruptions/faults.md +150 -125
  19. data/docs/interruptions/halt.md +134 -80
  20. data/docs/logging.md +181 -118
  21. data/docs/middlewares.md +150 -377
  22. data/docs/outcomes/result.md +140 -112
  23. data/docs/outcomes/states.md +134 -99
  24. data/docs/outcomes/statuses.md +204 -146
  25. data/docs/parameters/coercions.md +232 -281
  26. data/docs/parameters/defaults.md +224 -169
  27. data/docs/parameters/definitions.md +289 -141
  28. data/docs/parameters/namespacing.md +250 -161
  29. data/docs/parameters/validations.md +260 -133
  30. data/docs/testing.md +191 -197
  31. data/docs/workflows.md +143 -98
  32. data/lib/cmdx/callback.rb +23 -19
  33. data/lib/cmdx/callback_registry.rb +1 -3
  34. data/lib/cmdx/chain_inspector.rb +23 -23
  35. data/lib/cmdx/chain_serializer.rb +38 -19
  36. data/lib/cmdx/coercion.rb +20 -12
  37. data/lib/cmdx/coercion_registry.rb +51 -32
  38. data/lib/cmdx/configuration.rb +84 -31
  39. data/lib/cmdx/context.rb +32 -21
  40. data/lib/cmdx/core_ext/hash.rb +13 -13
  41. data/lib/cmdx/core_ext/module.rb +1 -1
  42. data/lib/cmdx/core_ext/object.rb +12 -12
  43. data/lib/cmdx/correlator.rb +60 -39
  44. data/lib/cmdx/errors.rb +105 -131
  45. data/lib/cmdx/fault.rb +66 -45
  46. data/lib/cmdx/immutator.rb +20 -21
  47. data/lib/cmdx/lazy_struct.rb +78 -70
  48. data/lib/cmdx/log_formatters/json.rb +1 -1
  49. data/lib/cmdx/log_formatters/key_value.rb +1 -1
  50. data/lib/cmdx/log_formatters/line.rb +1 -1
  51. data/lib/cmdx/log_formatters/logstash.rb +1 -1
  52. data/lib/cmdx/log_formatters/pretty_json.rb +1 -1
  53. data/lib/cmdx/log_formatters/pretty_key_value.rb +1 -1
  54. data/lib/cmdx/log_formatters/pretty_line.rb +1 -1
  55. data/lib/cmdx/log_formatters/raw.rb +2 -2
  56. data/lib/cmdx/logger.rb +19 -14
  57. data/lib/cmdx/logger_ansi.rb +33 -17
  58. data/lib/cmdx/logger_serializer.rb +85 -24
  59. data/lib/cmdx/middleware.rb +39 -21
  60. data/lib/cmdx/middleware_registry.rb +4 -3
  61. data/lib/cmdx/parameter.rb +151 -89
  62. data/lib/cmdx/parameter_inspector.rb +34 -21
  63. data/lib/cmdx/parameter_registry.rb +36 -30
  64. data/lib/cmdx/parameter_serializer.rb +21 -14
  65. data/lib/cmdx/result.rb +136 -135
  66. data/lib/cmdx/result_ansi.rb +31 -17
  67. data/lib/cmdx/result_inspector.rb +32 -27
  68. data/lib/cmdx/result_logger.rb +23 -14
  69. data/lib/cmdx/result_serializer.rb +65 -27
  70. data/lib/cmdx/task.rb +234 -113
  71. data/lib/cmdx/task_deprecator.rb +22 -25
  72. data/lib/cmdx/task_processor.rb +89 -88
  73. data/lib/cmdx/task_serializer.rb +27 -14
  74. data/lib/cmdx/utils/monotonic_runtime.rb +2 -4
  75. data/lib/cmdx/validator.rb +25 -16
  76. data/lib/cmdx/validator_registry.rb +53 -31
  77. data/lib/cmdx/validators/exclusion.rb +1 -1
  78. data/lib/cmdx/validators/format.rb +2 -2
  79. data/lib/cmdx/validators/inclusion.rb +2 -2
  80. data/lib/cmdx/validators/length.rb +2 -2
  81. data/lib/cmdx/validators/numeric.rb +3 -3
  82. data/lib/cmdx/validators/presence.rb +2 -2
  83. data/lib/cmdx/version.rb +1 -1
  84. data/lib/cmdx/workflow.rb +54 -33
  85. data/lib/generators/cmdx/task_generator.rb +6 -6
  86. data/lib/generators/cmdx/workflow_generator.rb +6 -6
  87. metadata +3 -1
data/docs/workflows.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Workflow
2
2
 
3
- A CMDx::Workflow orchestrates sequential execution of multiple tasks in a linear pipeline. Workflows provide a declarative DSL for composing complex business workflows from individual task components, with support for conditional execution, context propagation, and configurable halt behavior.
3
+ CMDx::Workflow orchestrates sequential execution of multiple tasks in a linear pipeline. Workflows provide a declarative DSL for composing complex business workflows from individual task components, with support for conditional execution, context propagation, and configurable halt behavior.
4
4
 
5
5
  Workflows inherit from Task, gaining all task capabilities including callbacks, parameter validation, result tracking, and configuration. The key difference is that workflows coordinate other tasks rather than implementing business logic directly.
6
6
 
@@ -17,55 +17,68 @@ Workflows inherit from Task, gaining all task capabilities including callbacks,
17
17
  - [Group-Level Configuration](#group-level-configuration)
18
18
  - [Available Result Statuses](#available-result-statuses)
19
19
  - [Process Method Options](#process-method-options)
20
- - [Condition Callables](#condition-callables)
20
+ - [Error Handling](#error-handling)
21
21
  - [Nested Workflows](#nested-workflows)
22
22
  - [Task Settings Integration](#task-settings-integration)
23
23
  - [Generator](#generator)
24
24
 
25
25
  ## TLDR
26
26
 
27
- - **Purpose** - Orchestrate sequential execution of multiple tasks in linear pipeline
28
- - **Declaration** - Use `process` method to declare tasks in execution order
29
- - **Context sharing** - Context object shared across all tasks for data pipeline
30
- - **Conditional execution** - Support `:if` and `:unless` options for conditional tasks
31
- - **Halt behavior** - Configurable stopping on failed/skipped results (default: halt on failed only)
32
- - **No call method** - Workflows automatically provide execution logic, don't define `call`
27
+ ```ruby
28
+ # Basic workflow - sequential task execution
29
+ class OrderWorkflow < CMDx::Workflow
30
+ process ValidateOrderTask # Step 1
31
+ process CalculateTaxTask # Step 2
32
+ process ChargePaymentTask # Step 3
33
+ end
34
+
35
+ # Conditional execution
36
+ process SendEmailTask, if: proc { context.notify_user? }
37
+ process SkipableTask, unless: :should_skip?
38
+
39
+ # Halt behavior control
40
+ process CriticalTask, workflow_halt: [CMDx::Result::FAILED, CMDx::Result::SKIPPED]
41
+ process OptionalTask, workflow_halt: [] # Never halt
42
+
43
+ # Context flows through all tasks automatically
44
+ result = OrderWorkflow.call(order: order)
45
+ result.context.tax_amount # Set by CalculateTaxTask
46
+ result.context.payment_id # Set by ChargePaymentTask
47
+ ```
33
48
 
34
49
  ## Basic Usage
35
50
 
36
51
  > [!WARNING]
37
- > Do **NOT** define a `call` method in workflow classes. The workflow class automatically provides the call logic.
52
+ > Do **NOT** define a `call` method in workflow classes. The workflow automatically provides execution logic.
38
53
 
39
54
  ```ruby
40
55
  class OrderProcessingWorkflow < CMDx::Workflow
41
- # Sequential task execution
42
56
  process ValidateOrderTask
43
57
  process CalculateTaxTask
44
58
  process ChargePaymentTask
45
59
  process FulfillOrderTask
46
60
  end
47
61
 
48
- # Execute the workflow
49
- result = WorkflowProcessOrders.call(order: order, user: current_user)
62
+ # Execute workflow
63
+ result = OrderProcessingWorkflow.call(order: order, user: current_user)
50
64
 
51
65
  if result.success?
52
- redirect_to success_path
66
+ redirect_to order_path(result.context.order)
53
67
  elsif result.failed?
54
- flash[:error] = "Order processing failed: #{result.metadata[:reason]}"
55
- redirect_to cart_path
68
+ handle_error(result.metadata[:reason])
56
69
  end
57
70
  ```
58
71
 
59
72
  ## Task Declaration
60
73
 
61
- Tasks are declared using the `process` method and organized into groups with shared execution options:
74
+ Tasks are declared using the `process` method in execution order:
62
75
 
63
76
  ```ruby
64
- class NotificationDeliveryWorkflow < CMDx::Workflow
65
- # Single task declaration
77
+ class NotificationWorkflow < CMDx::Workflow
78
+ # Single task
66
79
  process PrepareNotificationTask
67
80
 
68
- # Multiple tasks in one declaration (grouped)
81
+ # Multiple tasks (grouped with same options)
69
82
  process SendEmailTask, SendSmsTask, SendPushTask
70
83
 
71
84
  # Tasks with conditions
@@ -81,34 +94,32 @@ end
81
94
  ```
82
95
 
83
96
  > [!IMPORTANT]
84
- > Process steps are executed in the order they are declared (FIFO: first in, first out).
97
+ > Tasks execute in declaration order (FIFO). Use grouping to apply the same options to multiple tasks.
85
98
 
86
99
  ## Context Propagation
87
100
 
88
- The context object is shared across all tasks in the workflow, creating a data pipeline:
101
+ The context object flows through all tasks, creating a data pipeline:
89
102
 
90
103
  ```ruby
91
- class EcommerceProcessingWorkflow < CMDx::Workflow
92
- process ValidateOrderTask # Sets context.validation_result
93
- process CalculateTaxTask # Uses context.order, sets context.tax_amount
94
- process ChargePaymentTask # Uses context.tax_amount, sets context.payment_id
95
- process FulfillOrderTask # Uses context.payment_id, sets context.tracking_number
104
+ class PaymentWorkflow < CMDx::Workflow
105
+ process ValidateOrderTask # Sets context.validation_errors
106
+ process CalculateTaxTask # Uses context.order, sets context.tax_amount
107
+ process ChargePaymentTask # Uses context.tax_amount, sets context.payment_id
96
108
  end
97
109
 
98
- result = WorkflowProcessEcommerce.call(order: order)
99
- # Final context contains data from all executed tasks
100
- result.context.validation_result # From ValidateOrderTask
101
- result.context.tax_amount # From CalculateTaxTask
102
- result.context.payment_id # From ChargePaymentTask
103
- result.context.tracking_number # From FulfillOrderTask
110
+ result = PaymentWorkflow.call(order: order)
111
+ # Context contains cumulative data from all executed tasks
112
+ result.context.validation_errors # From ValidateOrderTask
113
+ result.context.tax_amount # From CalculateTaxTask
114
+ result.context.payment_id # From ChargePaymentTask
104
115
  ```
105
116
 
106
117
  ## Conditional Execution
107
118
 
108
- Tasks can be executed conditionally using `:if` and `:unless` options. Conditions can be procs, lambdas, or method names:
119
+ Tasks can execute conditionally using `:if` and `:unless` options:
109
120
 
110
121
  ```ruby
111
- class UserProcessingWorkflow < CMDx::Workflow
122
+ class UserWorkflow < CMDx::Workflow
112
123
  process ValidateUserTask
113
124
 
114
125
  # Proc condition
@@ -124,7 +135,7 @@ class UserProcessingWorkflow < CMDx::Workflow
124
135
  process SendSpecialOfferTask, if: proc {
125
136
  context.user.active? &&
126
137
  context.feature_enabled?(:offers) &&
127
- Time.now.hour.between?(9, 17)
138
+ business_hours?
128
139
  }
129
140
 
130
141
  private
@@ -132,31 +143,38 @@ class UserProcessingWorkflow < CMDx::Workflow
132
143
  def debug_enabled?
133
144
  Rails.env.development?
134
145
  end
146
+
147
+ def business_hours?
148
+ Time.now.hour.between?(9, 17)
149
+ end
135
150
  end
136
151
  ```
137
152
 
153
+ > [!NOTE]
154
+ > Conditions are evaluated in the workflow instance context. Skipped tasks return `SKIPPED` status but don't halt execution by default.
155
+
138
156
  ## Halt Behavior
139
157
 
140
- Workflows control execution flow through halt behavior, which determines when to stop processing based on task results.
158
+ Workflows control execution flow by halting on specific result statuses.
141
159
 
142
160
  ### Default Behavior
143
161
 
144
- By default, workflows halt on `FAILED` status but continue on `SKIPPED`. This reflects the philosophy that skipped tasks are bypass mechanisms, not execution blockers.
162
+ By default, workflows halt on `FAILED` status but continue on `SKIPPED`:
145
163
 
146
164
  ```ruby
147
- class DataProcessingWorkflow < CMDx::Workflow
148
- process LoadDataTask # If this fails, workflow stops
149
- process ValidateDataTask # If this is skipped, workflow continues
150
- process SaveDataTask # This only runs if LoadDataTask and ValidateDataTask don't fail
165
+ class DataWorkflow < CMDx::Workflow
166
+ process LoadDataTask # If fails workflow stops
167
+ process ValidateDataTask # If skipped workflow continues
168
+ process SaveDataTask # Only runs if no failures occurred
151
169
  end
152
170
  ```
153
171
 
154
172
  ### Class-Level Configuration
155
173
 
156
- Configure halt behavior for the entire workflow using `cmd_settings!`:
174
+ Configure halt behavior for the entire workflow:
157
175
 
158
176
  ```ruby
159
- class CriticalDataProcessingWorkflow < CMDx::Workflow
177
+ class CriticalWorkflow < CMDx::Workflow
160
178
  # Halt on both failed and skipped results
161
179
  cmd_settings!(workflow_halt: [CMDx::Result::FAILED, CMDx::Result::SKIPPED])
162
180
 
@@ -164,7 +182,7 @@ class CriticalDataProcessingWorkflow < CMDx::Workflow
164
182
  process ValidateCriticalDataTask
165
183
  end
166
184
 
167
- class OptionalDataProcessingWorkflow < CMDx::Workflow
185
+ class OptionalWorkflow < CMDx::Workflow
168
186
  # Never halt, always continue
169
187
  cmd_settings!(workflow_halt: [])
170
188
 
@@ -176,73 +194,102 @@ end
176
194
 
177
195
  ### Group-Level Configuration
178
196
 
179
- Different groups can have different halt behavior:
197
+ Different task groups can have different halt behavior:
180
198
 
181
199
  ```ruby
182
- class UserAccountProcessingWorkflow < CMDx::Workflow
200
+ class AccountWorkflow < CMDx::Workflow
183
201
  # Critical tasks - halt on any failure or skip
184
202
  process CreateUserTask, ValidateUserTask,
185
203
  workflow_halt: [CMDx::Result::FAILED, CMDx::Result::SKIPPED]
186
204
 
187
- # Optional tasks - never halt execution
188
- process SendWelcomeEmailTask, CreateProfileTask, workflow_halt: []
205
+ # Optional tasks - never halt
206
+ process SendWelcomeEmailTask, CreateProfileTask,
207
+ workflow_halt: []
189
208
 
190
- # Notification tasks - use default behavior (halt on failed only)
209
+ # Default behavior for remaining tasks
191
210
  process NotifyAdminTask, LogUserCreationTask
192
211
  end
193
212
  ```
194
213
 
195
214
  ### Available Result Statuses
196
215
 
197
- The following result statuses can be used in `workflow_halt` arrays:
216
+ Use these statuses in `workflow_halt` arrays:
198
217
 
199
- - `CMDx::Result::SUCCESS` - Task completed successfully
200
- - `CMDx::Result::SKIPPED` - Task was skipped intentionally
201
- - `CMDx::Result::FAILED` - Task failed due to error or validation
218
+ | Status | Description |
219
+ |--------|-------------|
220
+ | `CMDx::Result::SUCCESS` | Task completed successfully |
221
+ | `CMDx::Result::SKIPPED` | Task was skipped intentionally |
222
+ | `CMDx::Result::FAILED` | Task failed due to error or validation |
202
223
 
203
224
  ## Process Method Options
204
225
 
205
- The `process` method supports the following options:
226
+ The `process` method supports these options:
206
227
 
207
- | Option | Description |
208
- | ------------- | ----------- |
209
- | `:if` | Specifies a callable method, proc or string to determine if processing steps should occur. |
210
- | `:unless` | Specifies a callable method, proc, or string to determine if processing steps should not occur. |
211
- | `:workflow_halt` | Sets which result statuses processing of further steps should be prevented. (default: `CMDx::Result::FAILED`) |
228
+ | Option | Description | Example |
229
+ |--------|-------------|---------|
230
+ | `:if` | Execute task if condition is true | `if: proc { context.enabled? }` |
231
+ | `:unless` | Execute task if condition is false | `unless: :should_skip?` |
232
+ | `:workflow_halt` | Which statuses should halt execution | `workflow_halt: [CMDx::Result::FAILED]` |
212
233
 
213
- ### Condition Callables
234
+ Conditions can be procs, lambdas, symbols, or strings referencing instance methods.
214
235
 
215
- Conditions can be provided in several formats:
236
+ ## Error Handling
216
237
 
217
- ```ruby
218
- class AccountProcessingWorkflow < CMDx::Workflow
219
- # Proc - executed in workflow instance context
220
- process UpgradeAccountTask, if: proc { context.user.admin? }
238
+ > [!WARNING]
239
+ > Workflow failures provide detailed information about which task failed and why, enabling precise error handling and debugging.
221
240
 
222
- # Lambda - executed in workflow instance context
223
- process MaintenanceModeTask, unless: -> { context.maintenance_mode? }
241
+ ```ruby
242
+ class OrderWorkflow < CMDx::Workflow
243
+ process ValidateOrderTask
244
+ process CalculateTaxTask
245
+ process ChargePaymentTask
246
+ end
224
247
 
225
- # Symbol - method name called on workflow instance
226
- process AdvancedFeatureTask, if: :feature_enabled?
248
+ result = OrderWorkflow.call(order: invalid_order)
249
+
250
+ if result.failed?
251
+ result.metadata
252
+ # {
253
+ # reason: "ValidateOrderTask failed: Order ID is required",
254
+ # failed_task: "ValidateOrderTask",
255
+ # task_index: 0,
256
+ # executed_tasks: ["ValidateOrderTask"],
257
+ # skipped_tasks: [],
258
+ # context_at_failure: { order: {...} }
259
+ # }
260
+ end
261
+ ```
227
262
 
228
- # String - method name called on workflow instance
229
- process OptionalTask, unless: "skip_task?"
263
+ ### Common Error Scenarios
230
264
 
231
- private
265
+ ```ruby
266
+ # Task raises exception
267
+ class ProcessDataWorkflow < CMDx::Workflow
268
+ process ValidateDataTask # Raises validation error
269
+ process TransformDataTask # Never executes
270
+ end
232
271
 
233
- def feature_enabled?
234
- context.features.include?(:advanced)
235
- end
272
+ result = ProcessDataWorkflow.call(data: nil)
273
+ result.failed? # → true
274
+ result.metadata[:reason] # → "ValidateDataTask failed: Data cannot be nil"
236
275
 
237
- def skip_task?
238
- context.skip_optional_tasks?
239
- end
276
+ # Halt on skipped task
277
+ class StrictWorkflow < CMDx::Workflow
278
+ process RequiredTask, workflow_halt: [CMDx::Result::SKIPPED]
279
+ process OptionalTask, if: proc { false } # Always skipped
280
+ process FinalTask # Never executes
240
281
  end
282
+
283
+ result = StrictWorkflow.call
284
+ result.failed? # → true (halted on skipped task)
241
285
  ```
242
286
 
287
+ > [!TIP]
288
+ > Use specific halt configurations to implement different failure strategies: strict validation, best-effort processing, or fault-tolerant pipelines.
289
+
243
290
  ## Nested Workflows
244
291
 
245
- Workflows can process other workflows, creating hierarchical workflows:
292
+ Workflows can process other workflows for hierarchical composition:
246
293
 
247
294
  ```ruby
248
295
  class DataPreProcessingWorkflow < CMDx::Workflow
@@ -255,35 +302,33 @@ class DataProcessingWorkflow < CMDx::Workflow
255
302
  process ApplyBusinessLogicTask
256
303
  end
257
304
 
258
- class DataPostProcessingWorkflow < CMDx::Workflow
259
- process GenerateReportTask
260
- process SendNotificationTask
261
- end
262
-
263
- class CompleteDataProcessingWorkflow < CMDx::Workflow
305
+ class CompleteDataWorkflow < CMDx::Workflow
264
306
  process DataPreProcessingWorkflow
265
307
  process DataProcessingWorkflow, if: proc { context.pre_processing_successful? }
266
- process DataPostProcessingWorkflow, unless: proc { context.skip_post_processing? }
308
+ process GenerateReportTask
267
309
  end
268
310
  ```
269
311
 
312
+ > [!NOTE]
313
+ > Nested workflows share the same context object, enabling seamless data flow across workflow boundaries.
314
+
270
315
  ## Task Settings Integration
271
316
 
272
- Workflows support all task settings and can be configured like regular tasks:
317
+ Workflows support all task capabilities including parameters, callbacks, and configuration:
273
318
 
274
319
  ```ruby
275
- class PaymentProcessingWorkflow < CMDx::Workflow
276
- # Configure workflow-specific settings
320
+ class PaymentWorkflow < CMDx::Workflow
321
+ # Parameter validation
322
+ required :order_id, type: :integer
323
+ optional :notify_user, type: :boolean, default: true
324
+
325
+ # Workflow settings
277
326
  cmd_settings!(
278
327
  workflow_halt: [CMDx::Result::FAILED],
279
328
  log_level: :debug,
280
329
  tags: [:critical, :payment]
281
330
  )
282
331
 
283
- # Parameter validation
284
- required :order_id, type: :integer
285
- optional :notify_user, type: :boolean, default: true
286
-
287
332
  # Callbacks
288
333
  before_execution :setup_context
289
334
  after_execution :cleanup_resources
@@ -306,22 +351,22 @@ end
306
351
 
307
352
  ## Generator
308
353
 
309
- Generate a new workflow using the Rails generator:
354
+ Generate workflow scaffolding using the Rails generator:
310
355
 
311
356
  ```bash
312
357
  rails g cmdx:workflow ProcessOrder
313
358
  ```
314
359
 
315
- This creates a workflow template file under `app/cmds`:
360
+ Creates `app/commands/process_order_workflow.rb`:
316
361
 
317
362
  ```ruby
318
- class OrderProcessingWorkflow < ApplicationWorkflow
319
- process # TODO
363
+ class ProcessOrderWorkflow < ApplicationWorkflow
364
+ process # TODO: Add your tasks here
320
365
  end
321
366
  ```
322
367
 
323
368
  > [!NOTE]
324
- > The generator creates workflow files in `app/commands/workflow_[name].rb`, inherits from `ApplicationWorkflow` if available (otherwise `CMDx::Workflow`) and handles proper naming conventions.
369
+ > The generator creates workflow files in `app/commands/`, inherits from `ApplicationWorkflow` if available (otherwise `CMDx::Workflow`), and handles proper naming conventions.
325
370
 
326
371
  ---
327
372
 
data/lib/cmdx/callback.rb CHANGED
@@ -1,47 +1,51 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CMDx
4
- # Base class for implementing callback functionality in task execution.
4
+ # Base class for implementing callback functionality in task processing.
5
5
  #
6
- # Callbacks are executed at specific points during task lifecycle to
7
- # provide hooks for custom behavior, logging, validation, or cleanup.
8
- # All callback implementations must inherit from this class and implement
9
- # the abstract call method.
6
+ # Callbacks are executed at specific points in the task lifecycle, such as
7
+ # before execution, after success, or on failure. All callback implementations
8
+ # must inherit from this class and implement the abstract call method.
10
9
  class Callback
11
10
 
12
11
  # Executes a callback by creating a new instance and calling it.
13
12
  #
14
- # @param task [Task] the task instance executing the callback
15
- # @param type [Symbol] the callback type identifier
13
+ # @param task [CMDx::Task] the task instance triggering the callback
14
+ # @param type [Symbol] the callback type being executed (e.g., :before_execution, :on_success, :on_failure)
16
15
  #
17
- # @return [Object] the result of the callback execution
16
+ # @return [void]
18
17
  #
19
18
  # @raise [UndefinedCallError] when the callback subclass doesn't implement call
20
19
  #
21
- # @example Execute a callback on a task
22
- # MyCallback.call(task, :before)
20
+ # @example Execute a callback for task success
21
+ # LogSuccessCallback.call(task, :on_success)
22
+ #
23
+ # @example Execute a callback before task execution
24
+ # SetupCallback.call(task, :before_execution)
23
25
  def self.call(task, type)
24
26
  new.call(task, type)
25
27
  end
26
28
 
27
29
  # Abstract method that must be implemented by callback subclasses.
28
30
  #
29
- # This method contains the actual callback logic to be executed.
30
- # Subclasses must override this method to provide their specific
31
- # callback implementation.
31
+ # This method contains the actual callback logic to be executed at the
32
+ # specified point in the task lifecycle. Subclasses must override this method
33
+ # to provide their specific callback implementation.
32
34
  #
33
- # @param _task [Task] the task instance executing the callback
34
- # @param _type [Symbol] the callback type identifier
35
+ # @param task [CMDx::Task] the task instance triggering the callback
36
+ # @param type [Symbol] the callback type being executed
35
37
  #
36
- # @return [Object] the result of the callback execution
38
+ # @return [void]
37
39
  #
38
40
  # @raise [UndefinedCallError] always raised in the base class
39
41
  #
40
42
  # @example Implement in a subclass
41
- # def call(task, type)
42
- # puts "Executing #{type} callback for #{task.class.name}"
43
+ # class NotificationCallback < CMDx::Callback
44
+ # def call(task, type)
45
+ # puts "Task #{task.class.name} triggered #{type} callback"
46
+ # end
43
47
  # end
44
- def call(_task, _type)
48
+ def call(task, type) # rubocop:disable Lint/UnusedMethodArgument
45
49
  raise UndefinedCallError, "call method not defined in #{self.class.name}"
46
50
  end
47
51
 
@@ -20,8 +20,6 @@ module CMDx
20
20
  *Result::STATES.map { |s| :"on_#{s}" }
21
21
  ].freeze
22
22
 
23
- # The internal hash storing callback definitions.
24
- #
25
23
  # @return [Hash] hash containing callback type keys and callback definition arrays
26
24
  attr_reader :registry
27
25
 
@@ -106,7 +104,7 @@ module CMDx
106
104
  #
107
105
  # @example Getting registry contents
108
106
  # registry.to_h
109
- # # => { before_execution: [[:setup, {}]], on_good: [[:notify, { if: -> { true } }]] }
107
+ # #=> { before_execution: [[:setup, {}]], on_good: [[:notify, { if: -> { true } }]] }
110
108
  def to_h
111
109
  registry.transform_values(&:dup)
112
110
  end
@@ -1,11 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CMDx
4
- # Provides formatted inspection and display functionality for execution chains.
4
+ # Provides formatted inspection and display functionality for chain execution results.
5
5
  #
6
- # This module formats chain execution information into a human-readable string
7
- # representation, including the chain ID, individual task results, and summary
8
- # information about the chain's final state.
6
+ # ChainInspector creates human-readable string representations of execution chains,
7
+ # displaying chain metadata, individual task results, and execution summary information
8
+ # in a formatted layout. The inspector processes chain data to provide comprehensive
9
+ # debugging and monitoring output for task execution sequences.
9
10
  module ChainInspector
10
11
 
11
12
  FOOTER_KEYS = %i[
@@ -14,31 +15,30 @@ module CMDx
14
15
 
15
16
  module_function
16
17
 
17
- # Formats a chain into a human-readable inspection string.
18
+ # Formats a chain into a human-readable inspection string with headers, results, and summary.
18
19
  #
19
- # Creates a formatted display showing the chain ID, individual task results,
20
- # and summary footer with execution state information. The output includes
21
- # visual separators and structured formatting for easy reading.
20
+ # Creates a comprehensive string representation of the execution chain including
21
+ # a header with the chain ID, formatted individual task results, and a footer
22
+ # summary with key execution metadata. The output uses visual separators for
23
+ # clear section delineation and consistent formatting.
22
24
  #
23
- # @param chain [CMDx::Chain] the chain object to inspect
25
+ # @param chain [Chain] the execution chain to format and inspect
24
26
  #
25
- # @return [String] formatted multi-line string representation of the chain
27
+ # @return [String] formatted multi-line string representation of the chain execution
26
28
  #
27
- # @raise [NoMethodError] if chain doesn't respond to required methods (id, results, state, status, outcome, runtime)
28
- #
29
- # @example Inspect a simple chain
30
- # chain = CMDx::Chain.new(id: "abc123")
31
- # result = CMDx::Result.new(task)
32
- # chain.results << result
33
- # puts CMDx::ChainInspector.call(chain)
34
- # # Output:
35
- # # chain: abc123
36
- # # ===================
29
+ # @example Format a simple chain
30
+ # chain = MyWorkflow.call(user_id: 123)
31
+ # output = ChainInspector.call(chain.chain)
32
+ # puts output
33
+ # # =>
34
+ # # chain: abc123-def456-789
35
+ # # ===============================
37
36
  # #
38
- # # {:state=>"complete", :status=>"success", ...}
37
+ # # {:task=>"MyTask", :state=>"complete", :status=>"success"}
38
+ # # {:task=>"OtherTask", :state=>"complete", :status=>"success"}
39
39
  # #
40
- # # ===================
41
- # # state: complete | status: success | outcome: success | runtime: 0.001
40
+ # # ===============================
41
+ # # state: complete | status: success | outcome: good | runtime: 0.025
42
42
  def call(chain)
43
43
  header = "\nchain: #{chain.id}"
44
44
  footer = FOOTER_KEYS.map { |key| "#{key}: #{chain.send(key)}" }.join(" | ")
@@ -1,33 +1,52 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CMDx
4
- # Serializes Chain objects into hash representations for external consumption.
5
- # Provides a consistent interface for converting chain execution data into
6
- # structured format suitable for logging, API responses, or persistence.
4
+ # Serialization module for converting chain objects to hash representation.
5
+ #
6
+ # ChainSerializer provides functionality to serialize chain objects into a
7
+ # standardized hash format that includes essential metadata about the chain
8
+ # execution including unique identification, execution state, status, outcome,
9
+ # runtime, and all contained task results. The serialized format is commonly
10
+ # used for debugging, logging, introspection, and data exchange throughout
11
+ # the task execution pipeline.
7
12
  module ChainSerializer
8
13
 
9
14
  module_function
10
15
 
11
- # Converts a chain object into a hash representation containing execution metadata.
12
- # Extracts key chain attributes and serializes all contained results for complete
13
- # execution state capture.
16
+ # Serializes a chain object into a hash representation.
14
17
  #
15
- # @param chain [Chain] the chain instance to serialize
18
+ # Converts a chain instance into a standardized hash format containing
19
+ # key metadata about the chain's execution context and all contained results.
20
+ # The serialization includes information delegated from the first result in
21
+ # the chain (state, status, outcome, runtime) along with the chain's unique
22
+ # identifier and complete collection of task results converted to hashes.
16
23
  #
17
- # @return [Hash] hash containing chain metadata and serialized results
24
+ # @param chain [CMDx::Chain] the chain object to serialize
18
25
  #
19
- # @raise [NoMethodError] if chain doesn't respond to required methods
26
+ # @return [Hash] a hash containing the chain's metadata and execution information
27
+ # @option return [String] :id the unique identifier of the chain
28
+ # @option return [String] :state the execution state delegated from first result
29
+ # @option return [String] :status the execution status delegated from first result
30
+ # @option return [String] :outcome the execution outcome delegated from first result
31
+ # @option return [Float] :runtime the execution runtime in seconds delegated from first result
32
+ # @option return [Array<Hash>] :results array of serialized result hashes from all tasks in the chain
20
33
  #
21
- # @example Serializing a workflow chain
22
- # chain = UserWorkflow.call(user_id: 123)
23
- # ChainSerializer.call(chain)
24
- # # => {
25
- # # id: "abc123",
26
- # # state: :complete,
27
- # # status: :success,
28
- # # outcome: :good,
29
- # # runtime: 0.045,
30
- # # results: [...]
34
+ # @raise [NoMethodError] if the chain doesn't respond to required methods (id, state, status, outcome, runtime, results)
35
+ #
36
+ # @example Serialize a workflow chain with multiple tasks
37
+ # workflow = DataProcessingWorkflow.call(input: "data")
38
+ # ChainSerializer.call(workflow.chain)
39
+ # #=> {
40
+ # # id: "def456",
41
+ # # state: "complete",
42
+ # # status: "success",
43
+ # # outcome: "success",
44
+ # # runtime: 0.123,
45
+ # # results: [
46
+ # # { index: 0, class: "ValidateDataTask", status: "success", ... },
47
+ # # { index: 1, class: "ProcessDataTask", status: "success", ... },
48
+ # # { index: 2, class: "SaveDataTask", status: "success", ... }
49
+ # # ]
31
50
  # # }
32
51
  def call(chain)
33
52
  {