cmdx 1.0.1 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.cursor/prompts/docs.md +9 -0
- data/.cursor/prompts/rspec.md +21 -0
- data/.cursor/prompts/yardoc.md +13 -0
- data/.rubocop.yml +2 -0
- data/CHANGELOG.md +29 -3
- data/README.md +2 -1
- data/docs/ai_prompts.md +269 -195
- data/docs/basics/call.md +126 -60
- data/docs/basics/chain.md +190 -160
- data/docs/basics/context.md +242 -154
- data/docs/basics/setup.md +302 -32
- data/docs/callbacks.md +382 -119
- data/docs/configuration.md +211 -49
- data/docs/deprecation.md +245 -0
- data/docs/getting_started.md +161 -39
- data/docs/internationalization.md +590 -70
- data/docs/interruptions/exceptions.md +135 -118
- data/docs/interruptions/faults.md +152 -127
- data/docs/interruptions/halt.md +134 -80
- data/docs/logging.md +183 -120
- data/docs/middlewares.md +165 -392
- data/docs/outcomes/result.md +140 -112
- data/docs/outcomes/states.md +134 -99
- data/docs/outcomes/statuses.md +204 -146
- data/docs/parameters/coercions.md +251 -289
- data/docs/parameters/defaults.md +224 -169
- data/docs/parameters/definitions.md +289 -141
- data/docs/parameters/namespacing.md +250 -161
- data/docs/parameters/validations.md +247 -159
- data/docs/testing.md +196 -203
- data/docs/workflows.md +146 -101
- data/lib/cmdx/.DS_Store +0 -0
- data/lib/cmdx/callback.rb +39 -55
- data/lib/cmdx/callback_registry.rb +80 -73
- data/lib/cmdx/chain.rb +65 -122
- data/lib/cmdx/chain_inspector.rb +23 -116
- data/lib/cmdx/chain_serializer.rb +34 -146
- data/lib/cmdx/coercion.rb +57 -0
- data/lib/cmdx/coercion_registry.rb +113 -0
- data/lib/cmdx/coercions/array.rb +18 -36
- data/lib/cmdx/coercions/big_decimal.rb +21 -33
- data/lib/cmdx/coercions/boolean.rb +21 -40
- data/lib/cmdx/coercions/complex.rb +18 -31
- data/lib/cmdx/coercions/date.rb +20 -39
- data/lib/cmdx/coercions/date_time.rb +22 -39
- data/lib/cmdx/coercions/float.rb +19 -32
- data/lib/cmdx/coercions/hash.rb +22 -41
- data/lib/cmdx/coercions/integer.rb +20 -33
- data/lib/cmdx/coercions/rational.rb +20 -32
- data/lib/cmdx/coercions/string.rb +23 -31
- data/lib/cmdx/coercions/time.rb +24 -40
- data/lib/cmdx/coercions/virtual.rb +14 -31
- data/lib/cmdx/configuration.rb +101 -162
- data/lib/cmdx/context.rb +34 -166
- data/lib/cmdx/core_ext/hash.rb +42 -67
- data/lib/cmdx/core_ext/module.rb +35 -79
- data/lib/cmdx/core_ext/object.rb +63 -98
- data/lib/cmdx/correlator.rb +59 -154
- data/lib/cmdx/error.rb +37 -202
- data/lib/cmdx/errors.rb +153 -216
- data/lib/cmdx/fault.rb +68 -150
- data/lib/cmdx/faults.rb +26 -137
- data/lib/cmdx/immutator.rb +22 -110
- data/lib/cmdx/lazy_struct.rb +110 -186
- data/lib/cmdx/log_formatters/json.rb +14 -40
- data/lib/cmdx/log_formatters/key_value.rb +14 -40
- data/lib/cmdx/log_formatters/line.rb +14 -48
- data/lib/cmdx/log_formatters/logstash.rb +14 -57
- data/lib/cmdx/log_formatters/pretty_json.rb +14 -50
- data/lib/cmdx/log_formatters/pretty_key_value.rb +13 -46
- data/lib/cmdx/log_formatters/pretty_line.rb +16 -54
- data/lib/cmdx/log_formatters/raw.rb +19 -49
- data/lib/cmdx/logger.rb +22 -79
- data/lib/cmdx/logger_ansi.rb +31 -72
- data/lib/cmdx/logger_serializer.rb +74 -103
- data/lib/cmdx/middleware.rb +56 -60
- data/lib/cmdx/middleware_registry.rb +82 -77
- data/lib/cmdx/middlewares/correlate.rb +41 -226
- data/lib/cmdx/middlewares/timeout.rb +46 -185
- data/lib/cmdx/parameter.rb +167 -183
- data/lib/cmdx/parameter_evaluator.rb +231 -0
- data/lib/cmdx/parameter_inspector.rb +37 -55
- data/lib/cmdx/parameter_registry.rb +65 -84
- data/lib/cmdx/parameter_serializer.rb +32 -76
- data/lib/cmdx/railtie.rb +24 -107
- data/lib/cmdx/result.rb +254 -259
- data/lib/cmdx/result_ansi.rb +28 -80
- data/lib/cmdx/result_inspector.rb +34 -70
- data/lib/cmdx/result_logger.rb +23 -77
- data/lib/cmdx/result_serializer.rb +59 -125
- data/lib/cmdx/rspec/matchers.rb +28 -0
- data/lib/cmdx/rspec/result_matchers/be_executed.rb +42 -0
- data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +94 -0
- data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +94 -0
- data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +59 -0
- data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +57 -0
- data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +87 -0
- data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +51 -0
- data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +58 -0
- data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +59 -0
- data/lib/cmdx/rspec/result_matchers/have_context.rb +86 -0
- data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +54 -0
- data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +52 -0
- data/lib/cmdx/rspec/result_matchers/have_metadata.rb +114 -0
- data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +66 -0
- data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +64 -0
- data/lib/cmdx/rspec/result_matchers/have_runtime.rb +78 -0
- data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +76 -0
- data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +62 -0
- data/lib/cmdx/rspec/task_matchers/have_callback.rb +85 -0
- data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +68 -0
- data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +92 -0
- data/lib/cmdx/rspec/task_matchers/have_middleware.rb +46 -0
- data/lib/cmdx/rspec/task_matchers/have_parameter.rb +181 -0
- data/lib/cmdx/task.rb +336 -427
- data/lib/cmdx/task_deprecator.rb +52 -0
- data/lib/cmdx/task_processor.rb +246 -0
- data/lib/cmdx/task_serializer.rb +34 -69
- data/lib/cmdx/utils/ansi_color.rb +13 -89
- data/lib/cmdx/utils/log_timestamp.rb +13 -42
- data/lib/cmdx/utils/monotonic_runtime.rb +11 -63
- data/lib/cmdx/utils/name_affix.rb +21 -71
- data/lib/cmdx/validator.rb +57 -0
- data/lib/cmdx/validator_registry.rb +108 -0
- data/lib/cmdx/validators/exclusion.rb +55 -94
- data/lib/cmdx/validators/format.rb +31 -85
- data/lib/cmdx/validators/inclusion.rb +65 -110
- data/lib/cmdx/validators/length.rb +117 -133
- data/lib/cmdx/validators/numeric.rb +123 -130
- data/lib/cmdx/validators/presence.rb +38 -79
- data/lib/cmdx/version.rb +1 -7
- data/lib/cmdx/workflow.rb +58 -330
- data/lib/cmdx.rb +1 -1
- data/lib/generators/cmdx/install_generator.rb +14 -31
- data/lib/generators/cmdx/task_generator.rb +39 -55
- data/lib/generators/cmdx/templates/install.rb +24 -6
- data/lib/generators/cmdx/workflow_generator.rb +41 -66
- data/lib/locales/ar.yml +0 -1
- data/lib/locales/cs.yml +0 -1
- data/lib/locales/da.yml +0 -1
- data/lib/locales/de.yml +0 -1
- data/lib/locales/el.yml +0 -1
- data/lib/locales/en.yml +0 -1
- data/lib/locales/es.yml +0 -1
- data/lib/locales/fi.yml +0 -1
- data/lib/locales/fr.yml +0 -1
- data/lib/locales/he.yml +0 -1
- data/lib/locales/hi.yml +0 -1
- data/lib/locales/it.yml +0 -1
- data/lib/locales/ja.yml +0 -1
- data/lib/locales/ko.yml +0 -1
- data/lib/locales/nl.yml +0 -1
- data/lib/locales/no.yml +0 -1
- data/lib/locales/pl.yml +0 -1
- data/lib/locales/pt.yml +0 -1
- data/lib/locales/ru.yml +0 -1
- data/lib/locales/sv.yml +0 -1
- data/lib/locales/th.yml +0 -1
- data/lib/locales/tr.yml +0 -1
- data/lib/locales/vi.yml +0 -1
- data/lib/locales/zh.yml +0 -1
- metadata +36 -8
- data/lib/cmdx/parameter_validator.rb +0 -81
- data/lib/cmdx/parameter_value.rb +0 -244
- data/lib/cmdx/parameters_inspector.rb +0 -72
- data/lib/cmdx/parameters_serializer.rb +0 -115
- data/lib/cmdx/rspec/result_matchers.rb +0 -917
- data/lib/cmdx/rspec/task_matchers.rb +0 -570
- data/lib/cmdx/validators/custom.rb +0 -102
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CMDx
|
4
|
+
# Task deprecation system for CMDx tasks.
|
5
|
+
#
|
6
|
+
# This module provides a centralized system for handling task deprecation
|
7
|
+
# warnings and errors. It supports multiple deprecation modes including
|
8
|
+
# raising exceptions, logging warnings, or issuing Ruby warnings based
|
9
|
+
# on task configuration settings.
|
10
|
+
module TaskDeprecator
|
11
|
+
|
12
|
+
module_function
|
13
|
+
|
14
|
+
# Processes task deprecation based on the task's deprecated setting.
|
15
|
+
#
|
16
|
+
# @param task [CMDx::Task] the task instance to check for deprecation
|
17
|
+
# @return [void]
|
18
|
+
# @raise [DeprecationError] when task's deprecated setting is :error
|
19
|
+
#
|
20
|
+
# @example Handle task with raise deprecation
|
21
|
+
# class MyTask < CMDx::Task
|
22
|
+
# cmd_setting!(deprecated: :error)
|
23
|
+
# end
|
24
|
+
# task = MyTask.new
|
25
|
+
# TaskDeprecator.call(task) # raises DeprecationError
|
26
|
+
#
|
27
|
+
# @example Handle task with log deprecation
|
28
|
+
# class MyTask < CMDx::Task
|
29
|
+
# cmd_setting!(deprecated: :log)
|
30
|
+
# end
|
31
|
+
# task = MyTask.new
|
32
|
+
# TaskDeprecator.call(task) # logs warning via task.logger
|
33
|
+
#
|
34
|
+
# @example Handle task with warn deprecation
|
35
|
+
# class MyTask < CMDx::Task
|
36
|
+
# cmd_setting!(deprecated: :warning)
|
37
|
+
# end
|
38
|
+
# task = MyTask.new
|
39
|
+
# TaskDeprecator.call(task) # issues Ruby warning
|
40
|
+
def call(task)
|
41
|
+
case task.cmd_setting(:deprecated)
|
42
|
+
when :error
|
43
|
+
raise(DeprecationError, "#{task.class.name} usage prohibited")
|
44
|
+
when :log, true
|
45
|
+
task.logger.warn { "DEPRECATED: migrate to replacement or discontinue use" }
|
46
|
+
when :warning
|
47
|
+
warn("[#{task.class.name}] DEPRECATED: migrate to replacement or discontinue use", category: :deprecated)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,246 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CMDx
|
4
|
+
# Core task execution processor handling the complete task lifecycle.
|
5
|
+
#
|
6
|
+
# TaskProcessor manages the execution pipeline for individual tasks, coordinating
|
7
|
+
# parameter validation, callback invocation, error handling, and result state
|
8
|
+
# management. It provides both safe execution (capturing exceptions) and unsafe
|
9
|
+
# execution (re-raising exceptions) modes through call and call! methods respectively.
|
10
|
+
# The processor ensures proper state transitions, handles fault propagation, and
|
11
|
+
# maintains execution context throughout the task lifecycle.
|
12
|
+
class TaskProcessor
|
13
|
+
|
14
|
+
# @return [CMDx::Task] The task instance being executed
|
15
|
+
attr_reader :task
|
16
|
+
|
17
|
+
# Creates a new task processor for the specified task instance.
|
18
|
+
#
|
19
|
+
# @param task [CMDx::Task] the task instance to process
|
20
|
+
#
|
21
|
+
# @return [TaskProcessor] a new processor instance for the task
|
22
|
+
#
|
23
|
+
# @example Create a processor for a task
|
24
|
+
# task = MyTask.new(user_id: 123)
|
25
|
+
# processor = TaskProcessor.new(task)
|
26
|
+
def initialize(task)
|
27
|
+
@task = task
|
28
|
+
end
|
29
|
+
|
30
|
+
class << self
|
31
|
+
|
32
|
+
# Executes the specified task and returns the result without raising exceptions.
|
33
|
+
#
|
34
|
+
# Creates a new processor instance and executes the task through the complete
|
35
|
+
# lifecycle including validation, callbacks, and error handling. Exceptions
|
36
|
+
# are captured in the result rather than being raised to the caller.
|
37
|
+
#
|
38
|
+
# @param task [CMDx::Task] the task instance to execute
|
39
|
+
#
|
40
|
+
# @return [CMDx::Result] the execution result containing state and status information
|
41
|
+
#
|
42
|
+
# @example Execute a task safely
|
43
|
+
# task = ProcessDataTask.new(data: raw_data)
|
44
|
+
# result = TaskProcessor.call(task)
|
45
|
+
# puts result.status #=> "success", "failed", or "skipped"
|
46
|
+
def call(task)
|
47
|
+
new(task).call
|
48
|
+
end
|
49
|
+
|
50
|
+
# Executes the specified task and raises exceptions on failure.
|
51
|
+
#
|
52
|
+
# Creates a new processor instance and executes the task through the complete
|
53
|
+
# lifecycle. Unlike call, this method will re-raise exceptions including
|
54
|
+
# Fault exceptions when their status matches the task's halt configuration.
|
55
|
+
#
|
56
|
+
# @param task [CMDx::Task] the task instance to execute
|
57
|
+
#
|
58
|
+
# @return [CMDx::Result] the execution result on success
|
59
|
+
#
|
60
|
+
# @raise [CMDx::Fault] when a fault occurs with status matching task halt configuration
|
61
|
+
# @raise [StandardError] when unexpected errors occur during execution
|
62
|
+
#
|
63
|
+
# @example Execute a task with exception raising
|
64
|
+
# task = CriticalTask.new(operation: "delete")
|
65
|
+
# begin
|
66
|
+
# result = TaskProcessor.call!(task)
|
67
|
+
# puts "Success: #{result.status}"
|
68
|
+
# rescue CMDx::Fault => e
|
69
|
+
# puts "Task failed: #{e.message}"
|
70
|
+
# end
|
71
|
+
def call!(task)
|
72
|
+
new(task).call!
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
# Executes the task with safe error handling and returns the result.
|
78
|
+
#
|
79
|
+
# Runs the complete task execution pipeline including parameter validation,
|
80
|
+
# callback invocation, and the task's call method. Captures all exceptions
|
81
|
+
# as result status rather than raising them, ensuring the chain continues
|
82
|
+
# execution. Handles both standard errors and Fault exceptions according
|
83
|
+
# to the task's halt configuration.
|
84
|
+
#
|
85
|
+
# @return [CMDx::Result] the execution result with captured state and status
|
86
|
+
#
|
87
|
+
# @example Safe task execution
|
88
|
+
# processor = TaskProcessor.new(task)
|
89
|
+
# result = processor.call
|
90
|
+
# if result.success?
|
91
|
+
# puts "Task completed successfully"
|
92
|
+
# else
|
93
|
+
# puts "Task failed: #{result.metadata[:reason]}"
|
94
|
+
# end
|
95
|
+
def call
|
96
|
+
task.result.runtime do
|
97
|
+
before_call
|
98
|
+
validate_parameters
|
99
|
+
task.call
|
100
|
+
rescue UndefinedCallError => e
|
101
|
+
raise(e)
|
102
|
+
rescue Fault => e
|
103
|
+
if Array(task.cmd_setting(:task_halt)).include?(e.result.status)
|
104
|
+
# No need to clear the Chain since exception is not being re-raised
|
105
|
+
task.result.throw!(e.result, original_exception: e)
|
106
|
+
end
|
107
|
+
rescue StandardError => e
|
108
|
+
task.result.fail!(reason: "[#{e.class}] #{e.message}", original_exception: e)
|
109
|
+
ensure
|
110
|
+
task.result.executed!
|
111
|
+
after_call
|
112
|
+
end
|
113
|
+
|
114
|
+
terminate_call
|
115
|
+
end
|
116
|
+
|
117
|
+
# Executes the task with exception raising on halt conditions.
|
118
|
+
#
|
119
|
+
# Runs the complete task execution pipeline including parameter validation,
|
120
|
+
# callback invocation, and the task's call method. Unlike call, this method
|
121
|
+
# will re-raise Fault exceptions when their status matches the task's halt
|
122
|
+
# configuration, and clears the execution chain before raising.
|
123
|
+
#
|
124
|
+
# @return [CMDx::Result] the execution result on successful completion
|
125
|
+
#
|
126
|
+
# @raise [CMDx::Fault] when a fault occurs with status matching task halt configuration
|
127
|
+
# @raise [CMDx::UndefinedCallError] when the task's call method is not implemented
|
128
|
+
# @raise [StandardError] when unexpected errors occur during execution
|
129
|
+
#
|
130
|
+
# @example Task execution with exception raising
|
131
|
+
# processor = TaskProcessor.new(critical_task)
|
132
|
+
# begin
|
133
|
+
# result = processor.call!
|
134
|
+
# puts "Task succeeded"
|
135
|
+
# rescue CMDx::Fault => e
|
136
|
+
# puts "Critical failure: #{e.message}"
|
137
|
+
# # Chain is cleared, execution stops
|
138
|
+
# end
|
139
|
+
def call!
|
140
|
+
task.result.runtime do
|
141
|
+
before_call
|
142
|
+
validate_parameters
|
143
|
+
task.call
|
144
|
+
rescue UndefinedCallError => e
|
145
|
+
raise!(e)
|
146
|
+
rescue Fault => e
|
147
|
+
task.result.executed!
|
148
|
+
|
149
|
+
raise!(e) if Array(task.cmd_setting(:task_halt)).include?(e.result.status)
|
150
|
+
|
151
|
+
after_call # HACK: treat as NO-OP
|
152
|
+
else
|
153
|
+
task.result.executed!
|
154
|
+
after_call # ELSE: treat as success
|
155
|
+
end
|
156
|
+
|
157
|
+
terminate_call
|
158
|
+
end
|
159
|
+
|
160
|
+
private
|
161
|
+
|
162
|
+
# Executes pre-execution callbacks and sets the task to executing state.
|
163
|
+
#
|
164
|
+
# Invokes before_execution callbacks, transitions the result to executing
|
165
|
+
# state, and triggers on_executing callbacks. This method prepares the
|
166
|
+
# task for execution and notifies registered callbacks about the state change.
|
167
|
+
#
|
168
|
+
# @return [void]
|
169
|
+
def before_call
|
170
|
+
task.cmd_callbacks.call(task, :before_execution)
|
171
|
+
|
172
|
+
task.result.executing!
|
173
|
+
task.cmd_callbacks.call(task, :on_executing)
|
174
|
+
end
|
175
|
+
|
176
|
+
# Validates task parameters and handles validation failures.
|
177
|
+
#
|
178
|
+
# Executes parameter validation callbacks, validates all task parameters
|
179
|
+
# against their defined rules, and sets the task result to failed if
|
180
|
+
# validation errors are found. Collects all validation messages into
|
181
|
+
# the result metadata.
|
182
|
+
#
|
183
|
+
# @return [void]
|
184
|
+
def validate_parameters
|
185
|
+
task.cmd_callbacks.call(task, :before_validation)
|
186
|
+
|
187
|
+
task.cmd_parameters.validate!(task)
|
188
|
+
unless task.errors.empty?
|
189
|
+
task.result.fail!(
|
190
|
+
reason: task.errors.full_messages.join(". "),
|
191
|
+
messages: task.errors.messages
|
192
|
+
)
|
193
|
+
end
|
194
|
+
|
195
|
+
task.cmd_callbacks.call(task, :after_validation)
|
196
|
+
end
|
197
|
+
|
198
|
+
# Clears the execution chain and raises the specified exception.
|
199
|
+
#
|
200
|
+
# This method is used to clean up the execution context before
|
201
|
+
# re-raising exceptions, ensuring that the chain state is properly
|
202
|
+
# reset when execution cannot continue.
|
203
|
+
#
|
204
|
+
# @param exception [Exception] the exception to raise after clearing the chain
|
205
|
+
#
|
206
|
+
# @return [void]
|
207
|
+
#
|
208
|
+
# @raise [Exception] the provided exception after chain cleanup
|
209
|
+
def raise!(exception)
|
210
|
+
Chain.clear
|
211
|
+
raise(exception)
|
212
|
+
end
|
213
|
+
|
214
|
+
# Executes post-execution callbacks based on task result state and status.
|
215
|
+
#
|
216
|
+
# Invokes appropriate callbacks based on the task's final execution state
|
217
|
+
# (success, failure, etc.) and status. Handles both state-specific and
|
218
|
+
# status-specific callback invocation, as well as general execution
|
219
|
+
# completion callbacks.
|
220
|
+
#
|
221
|
+
# @return [void]
|
222
|
+
def after_call
|
223
|
+
task.cmd_callbacks.call(task, :"on_#{task.result.state}")
|
224
|
+
task.cmd_callbacks.call(task, :on_executed) if task.result.executed?
|
225
|
+
|
226
|
+
task.cmd_callbacks.call(task, :"on_#{task.result.status}")
|
227
|
+
task.cmd_callbacks.call(task, :on_good) if task.result.good?
|
228
|
+
task.cmd_callbacks.call(task, :on_bad) if task.result.bad?
|
229
|
+
|
230
|
+
task.cmd_callbacks.call(task, :after_execution)
|
231
|
+
end
|
232
|
+
|
233
|
+
# Finalizes task execution by freezing state and logging results.
|
234
|
+
#
|
235
|
+
# Applies immutability to the task instance and logs the execution
|
236
|
+
# result. This method ensures that the task state cannot be modified
|
237
|
+
# after execution and provides visibility into the execution outcome.
|
238
|
+
#
|
239
|
+
# @return [void]
|
240
|
+
def terminate_call
|
241
|
+
Immutator.call(task)
|
242
|
+
ResultLogger.call(task.result)
|
243
|
+
end
|
244
|
+
|
245
|
+
end
|
246
|
+
end
|
data/lib/cmdx/task_serializer.rb
CHANGED
@@ -1,82 +1,47 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module CMDx
|
4
|
-
|
5
|
-
# TaskSerializer converts task instances into hash representations for
|
6
|
-
# logging, debugging, and serialization purposes. It extracts key metadata
|
7
|
-
# about the task execution context and identification.
|
4
|
+
# Task serialization module for converting task objects to hash format.
|
8
5
|
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
# - Unique task instance ID
|
15
|
-
# - Associated tags for categorization
|
16
|
-
#
|
17
|
-
# @example Basic serialization
|
18
|
-
# class ProcessOrderTask < CMDx::Task
|
19
|
-
# task_settings!(tags: [:order, :payment])
|
20
|
-
# end
|
21
|
-
#
|
22
|
-
# task = ProcessOrderTask.call(order_id: 123)
|
23
|
-
# TaskSerializer.call(task)
|
24
|
-
# #=> {
|
25
|
-
# # index: 1,
|
26
|
-
# # chain_id: "abc123...",
|
27
|
-
# # type: "Task",
|
28
|
-
# # class: "ProcessOrderTask",
|
29
|
-
# # id: "def456...",
|
30
|
-
# # tags: [:order, :payment]
|
31
|
-
# # }
|
32
|
-
#
|
33
|
-
# @example Workflow serialization
|
34
|
-
# class OrderProcessingWorkflow < CMDx::Workflow
|
35
|
-
# task_settings!(tags: [:workflow, :orders])
|
36
|
-
# end
|
37
|
-
#
|
38
|
-
# workflow = OrderProcessingWorkflow.call(orders: [...])
|
39
|
-
# TaskSerializer.call(workflow)
|
40
|
-
# #=> {
|
41
|
-
# # index: 1,
|
42
|
-
# # chain_id: "abc123...",
|
43
|
-
# # type: "Workflow",
|
44
|
-
# # class: "OrderProcessingWorkflow",
|
45
|
-
# # id: "ghi789...",
|
46
|
-
# # tags: [:workflow, :orders]
|
47
|
-
# # }
|
48
|
-
#
|
49
|
-
# @see Task Task class for execution context
|
50
|
-
# @see Workflow Workflow class for multi-task execution
|
51
|
-
# @see Chain Chain class for execution grouping
|
52
|
-
# @since 1.0.0
|
6
|
+
# TaskSerializer provides functionality to serialize task objects into a
|
7
|
+
# standardized hash representation that includes essential metadata about
|
8
|
+
# the task such as its index, chain ID, type, class, ID, and tags. The
|
9
|
+
# serialized format is commonly used for debugging, logging, and introspection
|
10
|
+
# purposes throughout the task execution pipeline.
|
53
11
|
module TaskSerializer
|
54
12
|
|
55
13
|
module_function
|
56
14
|
|
57
|
-
|
58
|
-
#
|
59
|
-
#
|
15
|
+
# Serializes a task object into a hash representation.
|
16
|
+
#
|
17
|
+
# Converts a task instance into a standardized hash format containing
|
18
|
+
# key metadata about the task's execution context and classification.
|
19
|
+
# The serialization includes information from the task's result, chain,
|
20
|
+
# and command settings to provide comprehensive task identification.
|
21
|
+
#
|
22
|
+
# @param task [CMDx::Task, CMDx::Workflow] the task or workflow object to serialize
|
60
23
|
#
|
61
|
-
# @
|
62
|
-
# @return [
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
68
|
-
# - :tags [Array] array of tags associated with the task
|
24
|
+
# @return [Hash] a hash containing the task's metadata
|
25
|
+
# @option return [Integer] :index the task's position index in the execution chain
|
26
|
+
# @option return [String] :chain_id the unique identifier of the task's execution chain
|
27
|
+
# @option return [String] :type the task type, either "Task" or "Workflow"
|
28
|
+
# @option return [String] :class the full class name of the task
|
29
|
+
# @option return [String] :id the unique identifier of the task instance
|
30
|
+
# @option return [Array] :tags the tags associated with the task from cmd settings
|
69
31
|
#
|
70
|
-
# @
|
71
|
-
# task = MyTask.call(param: "value")
|
72
|
-
# data = TaskSerializer.call(task)
|
73
|
-
# data[:class] #=> "MyTask"
|
74
|
-
# data[:type] #=> "Task"
|
75
|
-
# data[:id] #=> "550e8400-e29b-41d4-a716-446655440000"
|
32
|
+
# @raise [NoMethodError] if the task doesn't respond to required methods
|
76
33
|
#
|
77
|
-
# @example
|
78
|
-
#
|
79
|
-
#
|
34
|
+
# @example Serialize a basic task
|
35
|
+
# task = ProcessDataTask.new
|
36
|
+
# TaskSerializer.call(task)
|
37
|
+
# #=> {
|
38
|
+
# # index: 0,
|
39
|
+
# # chain_id: "abc123",
|
40
|
+
# # type: "Task",
|
41
|
+
# # class: "ProcessDataTask",
|
42
|
+
# # id: "def456",
|
43
|
+
# # tags: []
|
44
|
+
# # }
|
80
45
|
def call(task)
|
81
46
|
{
|
82
47
|
index: task.result.index,
|
@@ -84,7 +49,7 @@ module CMDx
|
|
84
49
|
type: task.is_a?(Workflow) ? "Workflow" : "Task",
|
85
50
|
class: task.class.name,
|
86
51
|
id: task.id,
|
87
|
-
tags: task.
|
52
|
+
tags: task.cmd_setting(:tags)
|
88
53
|
}
|
89
54
|
end
|
90
55
|
|
@@ -2,59 +2,12 @@
|
|
2
2
|
|
3
3
|
module CMDx
|
4
4
|
module Utils
|
5
|
-
# Utility for
|
5
|
+
# Utility module for applying ANSI color and formatting codes to text.
|
6
6
|
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
# enhanced readability of logs and console output. Used extensively
|
10
|
-
# by CMDx's pretty formatters to provide visual distinction between
|
11
|
-
# different log levels, statuses, and metadata.
|
12
|
-
#
|
13
|
-
# @example Basic color usage
|
14
|
-
# Utils::AnsiColor.call("Error", color: :red)
|
15
|
-
# # => "\e[0;31;49mError\e[0m"
|
16
|
-
#
|
17
|
-
# @example Color with text modes
|
18
|
-
# Utils::AnsiColor.call("Warning", color: :yellow, mode: :bold)
|
19
|
-
# Utils::AnsiColor.call("Info", color: :blue, mode: :underline)
|
20
|
-
#
|
21
|
-
# @example Log severity coloring
|
22
|
-
# Utils::AnsiColor.call("ERROR", color: :red, mode: :bold)
|
23
|
-
# Utils::AnsiColor.call("WARN", color: :yellow)
|
24
|
-
# Utils::AnsiColor.call("INFO", color: :blue)
|
25
|
-
# Utils::AnsiColor.call("DEBUG", color: :light_black)
|
26
|
-
#
|
27
|
-
# @example Status indicator coloring
|
28
|
-
# Utils::AnsiColor.call("success", color: :green, mode: :bold)
|
29
|
-
# Utils::AnsiColor.call("failed", color: :red, mode: :bold)
|
30
|
-
# Utils::AnsiColor.call("skipped", color: :yellow)
|
31
|
-
#
|
32
|
-
# @example Available colors
|
33
|
-
# Utils::AnsiColor.call("Text", color: :red)
|
34
|
-
# Utils::AnsiColor.call("Text", color: :green)
|
35
|
-
# Utils::AnsiColor.call("Text", color: :blue)
|
36
|
-
# Utils::AnsiColor.call("Text", color: :light_cyan)
|
37
|
-
# Utils::AnsiColor.call("Text", color: :magenta)
|
38
|
-
#
|
39
|
-
# @example Available text modes
|
40
|
-
# Utils::AnsiColor.call("Bold", color: :white, mode: :bold)
|
41
|
-
# Utils::AnsiColor.call("Italic", color: :white, mode: :italic)
|
42
|
-
# Utils::AnsiColor.call("Underline", color: :white, mode: :underline)
|
43
|
-
# Utils::AnsiColor.call("Strikethrough", color: :white, mode: :strike)
|
44
|
-
#
|
45
|
-
# @see CMDx::ResultAnsi Uses this for result status coloring
|
46
|
-
# @see CMDx::LoggerAnsi Uses this for log severity coloring
|
47
|
-
# @see CMDx::LogFormatters::PrettyLine Uses this for colorized log output
|
48
|
-
# @see CMDx::LogFormatters::PrettyKeyValue Uses this for colorized key-value pairs
|
7
|
+
# This module provides functionality to colorize and format text output
|
8
|
+
# using ANSI escape sequences, supporting various colors and text modes.
|
49
9
|
module AnsiColor
|
50
10
|
|
51
|
-
# Available color codes for text coloring.
|
52
|
-
#
|
53
|
-
# Maps color names to their corresponding ANSI escape code numbers.
|
54
|
-
# Includes both standard and light variants of common colors for
|
55
|
-
# flexible visual styling in terminal environments.
|
56
|
-
#
|
57
|
-
# @return [Hash<Symbol, Integer>] mapping of color names to ANSI codes
|
58
11
|
COLOR_CODES = {
|
59
12
|
black: 30,
|
60
13
|
red: 31,
|
@@ -74,14 +27,6 @@ module CMDx
|
|
74
27
|
light_cyan: 96,
|
75
28
|
light_white: 97
|
76
29
|
}.freeze
|
77
|
-
|
78
|
-
# Available text mode codes for formatting.
|
79
|
-
#
|
80
|
-
# Maps text formatting mode names to their corresponding ANSI escape
|
81
|
-
# code numbers. Provides various text styling options including bold,
|
82
|
-
# italic, underline, and other visual effects.
|
83
|
-
#
|
84
|
-
# @return [Hash<Symbol, Integer>] mapping of mode names to ANSI codes
|
85
30
|
MODE_CODES = {
|
86
31
|
default: 0,
|
87
32
|
bold: 1,
|
@@ -101,42 +46,21 @@ module CMDx
|
|
101
46
|
|
102
47
|
module_function
|
103
48
|
|
104
|
-
#
|
105
|
-
#
|
106
|
-
# Wraps the provided text with ANSI escape codes to apply the specified
|
107
|
-
# color and formatting mode. The resulting string will display with the
|
108
|
-
# requested styling in ANSI-compatible terminals and will gracefully
|
109
|
-
# degrade in non-ANSI environments.
|
110
|
-
#
|
111
|
-
# @param value [String] text to colorize
|
112
|
-
# @param color [Symbol] color name from COLOR_CODES
|
113
|
-
# @param mode [Symbol] text mode from MODE_CODES (defaults to :default)
|
114
|
-
# @return [String] text wrapped with ANSI escape codes
|
115
|
-
# @raise [KeyError] if color or mode is not found in the respective code maps
|
116
|
-
#
|
117
|
-
# @example Success message with green bold text
|
118
|
-
# AnsiColor.call("Success", color: :green, mode: :bold)
|
119
|
-
# # => "\e[1;32;49mSuccess\e[0m"
|
49
|
+
# Applies ANSI color and formatting to the given text value.
|
120
50
|
#
|
121
|
-
# @
|
122
|
-
#
|
123
|
-
#
|
51
|
+
# @param value [String] the text to format with ANSI codes
|
52
|
+
# @param color [Symbol] the color to apply (must be a key in COLOR_CODES)
|
53
|
+
# @param mode [Symbol] the formatting mode to apply (must be a key in MODE_CODES)
|
124
54
|
#
|
125
|
-
# @
|
126
|
-
# AnsiColor.call("Warning", color: :yellow, mode: :underline)
|
127
|
-
# # => "\e[4;33;49mWarning\e[0m"
|
55
|
+
# @return [String] the formatted text with ANSI escape sequences
|
128
56
|
#
|
129
|
-
# @
|
130
|
-
# AnsiColor.call("Debug info", color: :light_black, mode: :dim)
|
131
|
-
# # => "\e[2;90;49mDebug info\e[0m"
|
57
|
+
# @raise [KeyError] if the specified color or mode is not found in the respective code maps
|
132
58
|
#
|
133
|
-
# @example
|
134
|
-
# AnsiColor.call("
|
135
|
-
# # => KeyError: key not found: :invalid_color
|
59
|
+
# @example Basic color application
|
60
|
+
# AnsiColor.call("Hello", color: :red) #=> "\e[0;31;49mHello\e[0m"
|
136
61
|
#
|
137
|
-
# @
|
138
|
-
#
|
139
|
-
# @note The final "\e[0m" resets all formatting to default
|
62
|
+
# @example Color with formatting mode
|
63
|
+
# AnsiColor.call("Warning", color: :yellow, mode: :bold) #=> "\e[1;33;49mWarning\e[0m"
|
140
64
|
def call(value, color:, mode: :default)
|
141
65
|
color_code = COLOR_CODES.fetch(color)
|
142
66
|
mode_code = MODE_CODES.fetch(mode)
|
@@ -2,60 +2,31 @@
|
|
2
2
|
|
3
3
|
module CMDx
|
4
4
|
module Utils
|
5
|
-
# Utility for formatting timestamps
|
5
|
+
# Utility module for formatting timestamps into standardized string representations.
|
6
6
|
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# @example Basic timestamp formatting
|
12
|
-
# Utils::LogTimestamp.call(Time.now)
|
13
|
-
# # => "2022-07-17T18:43:15.123456"
|
14
|
-
#
|
15
|
-
# @example Usage in log formatters
|
16
|
-
# timestamp = Utils::LogTimestamp.call(time.utc)
|
17
|
-
# log_entry = "#{severity} [#{timestamp}] #{message}"
|
18
|
-
#
|
19
|
-
# @example Consistent formatting across formatters
|
20
|
-
# # JSON formatter
|
21
|
-
# { "timestamp": Utils::LogTimestamp.call(time.utc) }
|
22
|
-
#
|
23
|
-
# # Line formatter
|
24
|
-
# "[#{Utils::LogTimestamp.call(time.utc)} ##{Process.pid}]"
|
25
|
-
#
|
26
|
-
# @see CMDx::LogFormatters::Json Uses this for JSON timestamp field
|
27
|
-
# @see CMDx::LogFormatters::Line Uses this for traditional log format
|
28
|
-
# @see CMDx::LogFormatters::Logstash Uses this for @timestamp field
|
7
|
+
# This module provides functionality to convert Time objects into consistent
|
8
|
+
# ISO 8601-like formatted strings with microsecond precision, suitable for
|
9
|
+
# logging and timestamp display purposes.
|
29
10
|
module LogTimestamp
|
30
11
|
|
31
|
-
# ISO 8601 datetime format with microsecond precision
|
32
|
-
# @return [String] strftime format string for consistent timestamp formatting
|
33
12
|
DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%6N"
|
34
13
|
|
35
14
|
module_function
|
36
15
|
|
37
|
-
# Formats a Time object
|
16
|
+
# Formats a Time object into a standardized timestamp string.
|
38
17
|
#
|
39
|
-
#
|
40
|
-
# using ISO 8601 format with microsecond precision. This ensures
|
41
|
-
# consistent timestamp formatting across all CMDx log outputs.
|
18
|
+
# @param time [Time] the time object to format
|
42
19
|
#
|
43
|
-
# @
|
44
|
-
# @return [String] ISO 8601 formatted timestamp with microseconds
|
20
|
+
# @return [String] the formatted timestamp string in ISO 8601-like format
|
45
21
|
#
|
46
|
-
# @
|
47
|
-
# LogTimestamp.call(Time.now)
|
48
|
-
# # => "2022-07-17T18:43:15.123456"
|
22
|
+
# @raise [NoMethodError] if the time object doesn't respond to strftime
|
49
23
|
#
|
50
|
-
# @example
|
51
|
-
# LogTimestamp.call(Time.now.
|
52
|
-
# # => "2022-07-17T18:43:15.123456"
|
24
|
+
# @example Basic timestamp formatting
|
25
|
+
# LogTimestamp.call(Time.now) #=> "2023-12-25T10:30:45.123456"
|
53
26
|
#
|
54
|
-
# @example
|
55
|
-
#
|
56
|
-
#
|
57
|
-
# "#{severity} [#{timestamp}] #{message}"
|
58
|
-
# end
|
27
|
+
# @example With specific time
|
28
|
+
# time = Time.new(2023, 12, 25, 10, 30, 45, 123456)
|
29
|
+
# LogTimestamp.call(time) #=> "2023-12-25T10:30:45.123456"
|
59
30
|
def call(time)
|
60
31
|
time.strftime(DATETIME_FORMAT)
|
61
32
|
end
|