cmdx 0.4.0 → 1.0.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 (126) hide show
  1. checksums.yaml +4 -4
  2. data/.DS_Store +0 -0
  3. data/.cursor/rules/cursor-instructions.mdc +6 -0
  4. data/.rubocop.yml +16 -1
  5. data/.ruby-version +1 -1
  6. data/CHANGELOG.md +42 -1
  7. data/README.md +72 -25
  8. data/docs/ai_prompts.md +309 -0
  9. data/docs/basics/call.md +225 -14
  10. data/docs/basics/chain.md +271 -0
  11. data/docs/basics/context.md +232 -33
  12. data/docs/basics/setup.md +76 -12
  13. data/docs/callbacks.md +273 -0
  14. data/docs/configuration.md +158 -28
  15. data/docs/getting_started.md +134 -22
  16. data/docs/interruptions/exceptions.md +189 -11
  17. data/docs/interruptions/faults.md +187 -44
  18. data/docs/interruptions/halt.md +179 -35
  19. data/docs/logging.md +194 -53
  20. data/docs/middlewares.md +735 -0
  21. data/docs/outcomes/result.md +296 -10
  22. data/docs/outcomes/states.md +212 -19
  23. data/docs/outcomes/statuses.md +284 -18
  24. data/docs/parameters/coercions.md +402 -29
  25. data/docs/parameters/defaults.md +249 -25
  26. data/docs/parameters/definitions.md +238 -72
  27. data/docs/parameters/namespacing.md +250 -27
  28. data/docs/parameters/validations.md +193 -168
  29. data/docs/testing.md +550 -0
  30. data/docs/tips_and_tricks.md +95 -43
  31. data/docs/workflows.md +319 -0
  32. data/lib/cmdx/.DS_Store +0 -0
  33. data/lib/cmdx/callback.rb +69 -0
  34. data/lib/cmdx/callback_registry.rb +106 -0
  35. data/lib/cmdx/chain.rb +190 -0
  36. data/lib/cmdx/chain_inspector.rb +149 -0
  37. data/lib/cmdx/chain_serializer.rb +175 -0
  38. data/lib/cmdx/coercions/array.rb +37 -0
  39. data/lib/cmdx/coercions/big_decimal.rb +33 -0
  40. data/lib/cmdx/coercions/boolean.rb +41 -1
  41. data/lib/cmdx/coercions/complex.rb +31 -0
  42. data/lib/cmdx/coercions/date.rb +39 -0
  43. data/lib/cmdx/coercions/date_time.rb +39 -0
  44. data/lib/cmdx/coercions/float.rb +31 -0
  45. data/lib/cmdx/coercions/hash.rb +42 -0
  46. data/lib/cmdx/coercions/integer.rb +32 -0
  47. data/lib/cmdx/coercions/rational.rb +31 -0
  48. data/lib/cmdx/coercions/string.rb +31 -0
  49. data/lib/cmdx/coercions/time.rb +39 -0
  50. data/lib/cmdx/coercions/virtual.rb +31 -0
  51. data/lib/cmdx/configuration.rb +217 -9
  52. data/lib/cmdx/context.rb +173 -2
  53. data/lib/cmdx/core_ext/hash.rb +72 -0
  54. data/lib/cmdx/core_ext/module.rb +94 -0
  55. data/lib/cmdx/core_ext/object.rb +105 -0
  56. data/lib/cmdx/correlator.rb +217 -0
  57. data/lib/cmdx/error.rb +210 -8
  58. data/lib/cmdx/errors.rb +256 -1
  59. data/lib/cmdx/fault.rb +177 -2
  60. data/lib/cmdx/faults.rb +158 -2
  61. data/lib/cmdx/immutator.rb +121 -2
  62. data/lib/cmdx/lazy_struct.rb +261 -18
  63. data/lib/cmdx/log_formatters/json.rb +46 -0
  64. data/lib/cmdx/log_formatters/key_value.rb +46 -0
  65. data/lib/cmdx/log_formatters/line.rb +54 -0
  66. data/lib/cmdx/log_formatters/logstash.rb +64 -0
  67. data/lib/cmdx/log_formatters/pretty_json.rb +57 -0
  68. data/lib/cmdx/log_formatters/pretty_key_value.rb +51 -0
  69. data/lib/cmdx/log_formatters/pretty_line.rb +60 -0
  70. data/lib/cmdx/log_formatters/raw.rb +54 -0
  71. data/lib/cmdx/logger.rb +85 -0
  72. data/lib/cmdx/logger_ansi.rb +93 -7
  73. data/lib/cmdx/logger_serializer.rb +116 -0
  74. data/lib/cmdx/middleware.rb +74 -0
  75. data/lib/cmdx/middleware_registry.rb +106 -0
  76. data/lib/cmdx/middlewares/correlate.rb +266 -0
  77. data/lib/cmdx/middlewares/timeout.rb +232 -0
  78. data/lib/cmdx/parameter.rb +228 -1
  79. data/lib/cmdx/parameter_inspector.rb +61 -0
  80. data/lib/cmdx/parameter_registry.rb +125 -0
  81. data/lib/cmdx/parameter_serializer.rb +83 -0
  82. data/lib/cmdx/parameter_validator.rb +62 -0
  83. data/lib/cmdx/parameter_value.rb +109 -1
  84. data/lib/cmdx/parameters_inspector.rb +59 -0
  85. data/lib/cmdx/parameters_serializer.rb +102 -0
  86. data/lib/cmdx/railtie.rb +123 -3
  87. data/lib/cmdx/result.rb +399 -20
  88. data/lib/cmdx/result_ansi.rb +105 -9
  89. data/lib/cmdx/result_inspector.rb +76 -0
  90. data/lib/cmdx/result_logger.rb +90 -3
  91. data/lib/cmdx/result_serializer.rb +137 -0
  92. data/lib/cmdx/rspec/result_matchers.rb +917 -0
  93. data/lib/cmdx/rspec/task_matchers.rb +570 -0
  94. data/lib/cmdx/task.rb +409 -34
  95. data/lib/cmdx/task_serializer.rb +74 -2
  96. data/lib/cmdx/utils/ansi_color.rb +95 -0
  97. data/lib/cmdx/utils/log_timestamp.rb +48 -0
  98. data/lib/cmdx/utils/monotonic_runtime.rb +71 -4
  99. data/lib/cmdx/utils/name_affix.rb +78 -0
  100. data/lib/cmdx/validators/custom.rb +82 -0
  101. data/lib/cmdx/validators/exclusion.rb +94 -0
  102. data/lib/cmdx/validators/format.rb +102 -8
  103. data/lib/cmdx/validators/inclusion.rb +104 -0
  104. data/lib/cmdx/validators/length.rb +128 -0
  105. data/lib/cmdx/validators/numeric.rb +128 -0
  106. data/lib/cmdx/validators/presence.rb +93 -7
  107. data/lib/cmdx/version.rb +7 -1
  108. data/lib/cmdx/workflow.rb +394 -0
  109. data/lib/cmdx.rb +25 -64
  110. data/lib/generators/cmdx/install_generator.rb +37 -1
  111. data/lib/generators/cmdx/task_generator.rb +69 -1
  112. data/lib/generators/cmdx/templates/install.rb +8 -12
  113. data/lib/generators/cmdx/workflow_generator.rb +109 -0
  114. metadata +54 -15
  115. data/docs/basics/run.md +0 -34
  116. data/docs/batch.md +0 -53
  117. data/docs/example.md +0 -82
  118. data/docs/hooks.md +0 -59
  119. data/lib/cmdx/batch.rb +0 -43
  120. data/lib/cmdx/parameters.rb +0 -34
  121. data/lib/cmdx/run.rb +0 -38
  122. data/lib/cmdx/run_inspector.rb +0 -26
  123. data/lib/cmdx/run_serializer.rb +0 -16
  124. data/lib/cmdx/task_hook.rb +0 -18
  125. data/lib/generators/cmdx/batch_generator.rb +0 -30
  126. /data/lib/generators/cmdx/templates/{batch.rb.tt → workflow.rb.tt} +0 -0
data/docs/logging.md CHANGED
@@ -1,104 +1,245 @@
1
1
  # Logging
2
2
 
3
- Tasks log the result object after execution. Multi-threaded systems will have many
4
- tasks executing concurrently so `CMDx` uses a custom logger to make debugging easier.
5
-
6
- ## Output
7
-
8
- Built-in log formatters are:
9
- - Standard: `Line`, `Json`, `KeyValue`, `Logstash`, `Raw`
10
- - Stylized: `PrettyLine` (default), `PrettyJson`, `PrettyKeyValue`
11
-
12
- #### Success:
13
- ```txt
14
- I, [2022-07-17T18:43:15.000000 #3784] INFO -- SimulationTask: index=0 run_id=018c2b95-b764-7615-a924-cc5b910ed1e5 type=Task class=SimulationTask id=018c2b95-b764-7615-a924-cc5b910ed1e5 tags=[] state=complete status=success outcome=success metadata={} runtime=0 origin=CMDx
15
- ```
16
-
17
- #### Skipped:
3
+ CMDx provides comprehensive automatic logging for task execution with structured data, customizable formatters, and intelligent severity mapping. All task results are logged after completion with rich metadata for debugging and monitoring.
4
+
5
+ ## Table of Contents
6
+ - [Log Formatters](#log-formatters)
7
+ - [Standard Formatters](#standard-formatters)
8
+ - [Stylized Formatters (ANSI Colors)](#stylized-formatters-ansi-colors)
9
+ - [Sample Output](#sample-output)
10
+ - [Success Result](#success-result)
11
+ - [Skipped Result](#skipped-result)
12
+ - [Failed Result](#failed-result)
13
+ - [Failure Chain (Workflow Workflows)](#failure-chain-workflow-workflows)
14
+ - [Configuration](#configuration)
15
+ - [Global Configuration](#global-configuration)
16
+ - [Task-Specific Configuration](#task-specific-configuration)
17
+ - [Environment-Specific Configuration](#environment-specific-configuration)
18
+ - [Severity Mapping](#severity-mapping)
19
+ - [Manual Logging](#manual-logging)
20
+ - [Advanced Formatter Usage](#advanced-formatter-usage)
21
+ - [Custom Formatter](#custom-formatter)
22
+ - [Multi-Destination Logging](#multi-destination-logging)
23
+ - [Log Data Structure](#log-data-structure)
24
+
25
+ ## Log Formatters
26
+
27
+ CMDx provides 8 built-in log formatters organized into standard and stylized categories:
28
+
29
+ ### Standard Formatters
30
+ - **`Line`** - Traditional single-line format similar to Ruby's Logger
31
+ - **`Json`** - Compact single-line JSON for structured logging systems
32
+ - **`KeyValue`** - Space-separated key=value pairs for easy parsing
33
+ - **`Logstash`** - ELK stack compatible JSON with @version and @timestamp fields
34
+ - **`Raw`** - Minimal output containing only the message content
35
+
36
+ ### Stylized Formatters (ANSI Colors)
37
+
38
+ > [!NOTE]
39
+ > Stylized formatters include ANSI color codes for terminal readability and are best suited for development environments.
40
+
41
+ - **`PrettyLine`** - Colorized line format (default)
42
+ - **`PrettyJson`** - Human-readable multi-line JSON with syntax highlighting
43
+ - **`PrettyKeyValue`** - Colorized key=value pairs for terminal readability
44
+
45
+ ## Sample Output
46
+
47
+ ### Success Result
18
48
  ```txt
19
- W, [2022-07-17T18:43:15.000000 #3784] WARN -- SimulationTask: index=0 run_id=018c2b95-b764-7615-a924-cc5b910ed1e5 type=Task class=SimulationTask id=018c2b95-b764-7615-a924-cc5b910ed1e5 tags=[] state=interrupted status=skipped outcome=skipped metadata={} runtime=0 origin=CMDx
49
+ I, [2022-07-17T18:43:15.000000 #3784] INFO -- CreateOrderTask: index=0 chain_id=018c2b95-b764-7615-a924-cc5b910ed1e5 type=Task class=CreateOrderTask id=018c2b95-b764-7615-a924-cc5b910ed1e5 tags=[] state=complete status=success outcome=success metadata={order_id: 123, confirmation: "ABC123"} runtime=0.45 origin=CMDx
20
50
  ```
21
51
 
22
- #### Failed:
52
+ ### Skipped Result
23
53
  ```txt
24
- E, [2022-07-17T18:43:15.000000 #3784] ERROR -- SimulationTask: index=0 run_id=018c2b95-b764-7615-a924-cc5b910ed1e5 type=Task class=SimulationTask id=018c2b95-b764-7615-a924-cc5b910ed1e5 tags=[] state=interrupted status=failed outcome=failed metadata={} runtime=0 origin=CMDx
54
+ W, [2022-07-17T18:43:15.000000 #3784] WARN -- ValidatePaymentTask: index=0 chain_id=018c2b95-b764-7615-a924-cc5b910ed1e5 type=Task class=ValidatePaymentTask id=018c2b95-b764-7615-a924-cc5b910ed1e5 tags=[] state=interrupted status=skipped outcome=skipped metadata={reason: "Order already processed"} runtime=0.02 origin=CMDx
25
55
  ```
26
56
 
27
- #### Level 1 subtask failure:
57
+ ### Failed Result
28
58
  ```txt
29
- E, [2022-07-17T18:43:15.000000 #3784] ERROR -- SimulationTask: index=0 run_id=018c2b95-b764-7615-a924-cc5b910ed1e5 type=Task class=SimulationTask id=018c2b95-b764-7615-a924-cc5b910ed1e5 tags=[] state=interrupted status=failed outcome=interrupted metadata={} runtime=0 caused_failure={index: 1, run_id: "018c2b95-b764-7615-a924-cc5b910ed1e5", type: "Task", class: "SimulationTask", id: "018c2b95-b764-7615-a924-cc5b910ed1e5", tags: [], state: "interrupted", status: "failed", outcome: "failed", metadata: {}, runtime: 0} threw_failure={index: 1, run_id: "018c2b95-b764-7615-a924-cc5b910ed1e5", type: "Task", class: "SimulationTask", id: "018c2b95-b764-7615-a924-cc5b910ed1e5", tags: [], state: "interrupted", status: "failed", outcome: "failed", metadata: {}, runtime: 0} origin=CMDx
59
+ E, [2022-07-17T18:43:15.000000 #3784] ERROR -- ProcessPaymentTask: index=0 chain_id=018c2b95-b764-7615-a924-cc5b910ed1e5 type=Task class=ProcessPaymentTask id=018c2b95-b764-7615-a924-cc5b910ed1e5 tags=[] state=interrupted status=failed outcome=failed metadata={reason: "Payment declined", error_code: "INSUFFICIENT_FUNDS"} runtime=0.15 origin=CMDx
30
60
  ```
31
61
 
32
- #### Level 2+ subtask failure:
62
+ ### Failure Chain (Workflow Workflows)
33
63
  ```txt
34
- E, [2022-07-17T18:43:15.000000 #3784] ERROR -- SimulationTask: index=0 run_id=018c2b95-b764-7615-a924-cc5b910ed1e5 type=Task class=SimulationTask id=018c2b95-b764-7615-a924-cc5b910ed1e5 tags=[] state=interrupted status=failed outcome=interrupted metadata={} runtime=0 caused_failure={index: 2, run_id: "018c2b95-b764-7615-a924-cc5b910ed1e5", type: "Task", class: "SimulationTask", id: "018c2b95-b764-7615-a924-cc5b910ed1e5", tags: [], state: "interrupted", status: "failed", outcome: "failed", metadata: {}, runtime: 0} threw_failure={index: 1, run_id: "018c2b95-b764-7615-a924-cc5b910ed1e5", type: "Task", class: "SimulationTask", id: "018c2b95-b764-7615-a924-cc5b910ed1e5", tags: [], state: "interrupted", status: "failed", outcome: "interrupted", metadata: {}, runtime: 0} origin=CMDx
64
+ E, [2022-07-17T18:43:15.000000 #3784] ERROR -- OrderCreationWorkflow: index=0 chain_id=018c2b95-b764-7615-a924-cc5b910ed1e5 type=Workflow class=OrderCreationWorkflow id=018c2b95-b764-7615-a924-cc5b910ed1e5 tags=[] state=interrupted status=failed outcome=interrupted metadata={} runtime=0.75 caused_failure={index: 2, class: "ValidatePaymentTask", status: "failed"} threw_failure={index: 1, class: "ProcessPaymentTask", status: "failed"} origin=CMDx
35
65
  ```
36
66
 
37
- ## Logger
67
+ ## Configuration
38
68
 
39
- CMDx defaults to using Ruby's standard library Logger. Log levels thus follow the
40
- [stdlib documentation](http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc/Logger.html).
69
+ ### Global Configuration
41
70
 
42
- #### Global settings:
71
+ Configure logging globally in your CMDx initializer:
43
72
 
44
73
  ```ruby
45
74
  CMDx.configure do |config|
46
- # Single declaration:
47
- config.logger = Logger.new($stdout, formatter: CMDx::LogFormatters::Line.new, level: Logger::DEBUG)
48
-
49
- # Multiline declarations:
50
- config.logger = Rails.logger
51
- config.logger.formatter = CMDx::LogFormatters::Line.new
52
- config.logger.level = Logger::WARN
75
+ config.logger = Logger.new("log/cmdx.log", formatter: CMDx::LogFormatters::Json.new)
76
+ config.logger.level = Logger::INFO
53
77
  end
54
78
  ```
55
79
 
56
- #### Task settings:
80
+ ### Task-Specific Configuration
57
81
 
58
- ```ruby
59
- class ProcessOrderTask < CMDx::Task
82
+ Override logging settings for individual tasks:
60
83
 
61
- task_settings!(logger: Rails.logger, log_format: CMDx::LogFormatters::Logstash.new, log_level: Logger::WARN)
84
+ ```ruby
85
+ class SendEmailTask < CMDx::Task
86
+ task_settings!(
87
+ logger: Rails.logger,
88
+ log_formatter: CMDx::LogFormatters::Json.new,
89
+ log_level: Logger::WARN
90
+ )
62
91
 
63
92
  def call
64
- # Do work...
93
+ # Task implementation
65
94
  end
95
+ end
66
96
 
97
+ # Base class with shared logging configuration
98
+ class ApplicationTask < CMDx::Task
99
+ task_settings!(
100
+ logger: Logger.new("log/tasks.log"),
101
+ log_formatter: CMDx::LogFormatters::Logstash.new,
102
+ log_level: Logger::INFO
103
+ )
67
104
  end
68
105
  ```
69
106
 
70
- > [!TIP]
71
- > In production environments, a log level of DEBUG may be too verbose for your needs.
72
- > For quieter logs that use less disk space, you can change the log level to only show INFO and higher.
107
+ ## Severity Mapping
108
+
109
+ > [!IMPORTANT]
110
+ > CMDx automatically maps result statuses to appropriate log severity levels. Manual log level overrides are not recommended.
111
+
112
+ | Result Status | Log Level | Use Case |
113
+ | ------------- | --------- | -------- |
114
+ | `success` | `INFO` | Normal successful completion |
115
+ | `skipped` | `WARN` | Intentional skip (business logic) |
116
+ | `failed` | `ERROR` | Task failure or exception |
73
117
 
74
- ## Write to log
118
+ ## Manual Logging
75
119
 
76
- Write to log via the `logger` method.
120
+ Access the configured logger within tasks for custom log messages:
77
121
 
78
122
  ```ruby
79
123
  class ProcessOrderTask < CMDx::Task
80
-
81
124
  def call
82
- logger.info "Processing order"
83
- logger.debug { context.to_h }
125
+ logger.info "Starting order processing", order_id: context.order_id
126
+
127
+ # Performance-optimized debug logging
128
+ logger.debug { "Order details: #{context.order.inspect}" }
129
+
130
+ # Structured logging
131
+ logger.info "Payment processed", {
132
+ order_id: context.order_id,
133
+ amount: context.order.total,
134
+ payment_method: context.payment_method
135
+ }
136
+
137
+ # Exception handling with logging
138
+ begin
139
+ validate_inventory
140
+ rescue StandardError => e
141
+ logger.error "Inventory validation failed: #{e.message}", {
142
+ exception: e.class.name,
143
+ order_id: context.order_id
144
+ }
145
+ raise
146
+ end
84
147
  end
85
-
86
148
  end
87
149
  ```
88
150
 
89
- ## Output format
151
+ ## Advanced Formatter Usage
90
152
 
91
- Define a custom log formatter to match your expected output, for example one that changes the JSON keys:
153
+ ### Custom Formatter
154
+
155
+ Create custom formatters for specific output requirements:
92
156
 
93
157
  ```ruby
94
- class CustomCmdxLogFormat
158
+ class SlackLogFormatter
95
159
  def call(severity, time, task, message)
96
- # Return string, hash, array, etc to output...
160
+ emoji = case severity
161
+ when 'INFO' then '✅'
162
+ when 'WARN' then '⚠️'
163
+ when 'ERROR' then '❌'
164
+ else '📝'
165
+ end
166
+
167
+ "#{emoji} #{task.class.name}: #{message}\n"
168
+ end
169
+ end
170
+
171
+ class SendNotificationTask < CMDx::Task
172
+ task_settings!(
173
+ logger: Logger.new("log/notifications.log", formatter: SlackLogFormatter.new)
174
+ )
175
+ end
176
+ ```
177
+
178
+ ### Multi-Destination Logging
179
+
180
+ > [!TIP]
181
+ > Use multi-destination logging to send output to both console and files simultaneously during development.
182
+
183
+ ```ruby
184
+ class MultiLogger
185
+ def initialize(*loggers)
186
+ @loggers = loggers
187
+ end
188
+
189
+ %w[debug info warn error fatal].each do |level|
190
+ define_method(level) do |message = nil, &block|
191
+ @loggers.each { |logger| logger.send(level, message, &block) }
192
+ end
193
+ end
194
+
195
+ def formatter=(formatter)
196
+ @loggers.each { |logger| logger.formatter = formatter }
197
+ end
198
+
199
+ def level=(level)
200
+ @loggers.each { |logger| logger.level = level }
97
201
  end
98
202
  end
203
+
204
+ # Usage
205
+ CMDx.configure do |config|
206
+ config.logger = MultiLogger.new(
207
+ Logger.new(STDOUT, formatter: CMDx::LogFormatters::PrettyLine.new),
208
+ Logger.new("log/cmdx.log", formatter: CMDx::LogFormatters::Json.new)
209
+ )
210
+ end
99
211
  ```
100
212
 
213
+ ## Log Data Structure
214
+
215
+ CMDx logs contain comprehensive execution metadata:
216
+
217
+ #### Standard Fields
218
+ - `severity` - Log level (INFO, WARN, ERROR)
219
+ - `pid` - Process ID for multi-process debugging
220
+ - `timestamp` - ISO 8601 formatted execution time
221
+ - `origin` - Always "CMDx" for filtering
222
+
223
+ #### Task Identification
224
+ - `index` - Position in execution sequence
225
+ - `chain_id` - Unique identifier for execution chain
226
+ - `type` - Task or Workflow
227
+ - `class` - Task class name
228
+ - `id` - Unique task instance identifier
229
+ - `tags` - Custom tags for categorization
230
+
231
+ #### Execution Information
232
+ - `state` - Execution lifecycle state (initialized, executing, complete, interrupted)
233
+ - `status` - Business logic outcome (success, skipped, failed)
234
+ - `outcome` - Final result classification
235
+ - `metadata` - Custom data from skip!/fail! calls
236
+ - `runtime` - Execution time in seconds
237
+
238
+ #### Failure Chain (Complex Workflows)
239
+ - `caused_failure` - Original failing task information
240
+ - `threw_failure` - Task that propagated the failure
241
+
101
242
  ---
102
243
 
103
- - **Prev:** [Batch](https://github.com/drexed/cmdx/blob/main/docs/batch.md)
104
- - **Next:** [Tips & Tricks](https://github.com/drexed/cmdx/blob/main/docs/tips_and_tricks.md)
244
+ - **Prev:** [Workflows](workflows.md)
245
+ - **Next:** [Testing](testing.md)