cmdx 1.0.1 → 1.1.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.
- checksums.yaml +4 -4
- data/.cursor/prompts/rspec.md +20 -0
- data/.cursor/prompts/yardoc.md +8 -0
- data/.rubocop.yml +2 -0
- data/CHANGELOG.md +17 -2
- data/README.md +1 -1
- data/docs/basics/call.md +2 -2
- data/docs/basics/chain.md +1 -1
- data/docs/callbacks.md +3 -36
- data/docs/configuration.md +58 -12
- data/docs/interruptions/exceptions.md +1 -1
- data/docs/interruptions/faults.md +2 -2
- data/docs/logging.md +4 -4
- data/docs/middlewares.md +43 -43
- data/docs/parameters/coercions.md +49 -38
- data/docs/parameters/defaults.md +1 -1
- data/docs/parameters/validations.md +0 -39
- data/docs/testing.md +11 -12
- data/docs/workflows.md +4 -4
- data/lib/cmdx/.DS_Store +0 -0
- data/lib/cmdx/callback.rb +36 -56
- data/lib/cmdx/callback_registry.rb +82 -73
- data/lib/cmdx/chain.rb +65 -122
- data/lib/cmdx/chain_inspector.rb +22 -115
- data/lib/cmdx/chain_serializer.rb +17 -148
- data/lib/cmdx/coercion.rb +49 -0
- data/lib/cmdx/coercion_registry.rb +94 -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 +57 -171
- data/lib/cmdx/context.rb +22 -165
- 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 +40 -156
- data/lib/cmdx/error.rb +37 -202
- data/lib/cmdx/errors.rb +165 -202
- data/lib/cmdx/fault.rb +55 -158
- data/lib/cmdx/faults.rb +26 -137
- data/lib/cmdx/immutator.rb +22 -109
- data/lib/cmdx/lazy_struct.rb +103 -187
- 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 +20 -82
- data/lib/cmdx/logger_ansi.rb +18 -75
- data/lib/cmdx/logger_serializer.rb +24 -114
- data/lib/cmdx/middleware.rb +38 -60
- data/lib/cmdx/middleware_registry.rb +81 -77
- data/lib/cmdx/middlewares/correlate.rb +41 -226
- data/lib/cmdx/middlewares/timeout.rb +46 -185
- data/lib/cmdx/parameter.rb +120 -198
- data/lib/cmdx/parameter_evaluator.rb +231 -0
- data/lib/cmdx/parameter_inspector.rb +25 -56
- data/lib/cmdx/parameter_registry.rb +59 -84
- data/lib/cmdx/parameter_serializer.rb +23 -74
- data/lib/cmdx/railtie.rb +24 -107
- data/lib/cmdx/result.rb +254 -260
- data/lib/cmdx/result_ansi.rb +19 -85
- data/lib/cmdx/result_inspector.rb +27 -68
- data/lib/cmdx/result_logger.rb +18 -81
- data/lib/cmdx/result_serializer.rb +28 -132
- 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 +213 -425
- data/lib/cmdx/task_deprecator.rb +55 -0
- data/lib/cmdx/task_processor.rb +245 -0
- data/lib/cmdx/task_serializer.rb +22 -70
- 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 +13 -63
- data/lib/cmdx/utils/name_affix.rb +21 -71
- data/lib/cmdx/validator.rb +48 -0
- data/lib/cmdx/validator_registry.rb +86 -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 +46 -339
- 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 +34 -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,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CMDx
|
4
|
+
# Handles deprecation checking for CMDx tasks.
|
5
|
+
#
|
6
|
+
# This module provides functionality to check if a task is marked as deprecated
|
7
|
+
# and raise appropriate errors when deprecated tasks are instantiated. It integrates
|
8
|
+
# with the task lifecycle to prevent usage of deprecated functionality.
|
9
|
+
module TaskDeprecator
|
10
|
+
|
11
|
+
module_function
|
12
|
+
|
13
|
+
# Checks deprecation status of a task and handles it according to the configured behavior.
|
14
|
+
#
|
15
|
+
# This method examines the task's deprecation setting and takes appropriate action:
|
16
|
+
# - :raise - raises DeprecationError to prevent task execution
|
17
|
+
# - :warn - issues Ruby deprecation warning
|
18
|
+
# - :log or true - logs deprecation warning
|
19
|
+
# - nil or false - allows task execution without warnings
|
20
|
+
#
|
21
|
+
# @param task [Task] the task instance to check for deprecation
|
22
|
+
#
|
23
|
+
# @return [void]
|
24
|
+
#
|
25
|
+
# @raise [DeprecationError] when task is marked with deprecated: :raise
|
26
|
+
#
|
27
|
+
# @example Task with raise deprecation setting
|
28
|
+
# class MyTask < CMDx::Task
|
29
|
+
# cmd_settings! deprecated: :raise
|
30
|
+
# end
|
31
|
+
# CMDx::TaskDeprecator.call(MyTask.new) # raises DeprecationError
|
32
|
+
#
|
33
|
+
# @example Task with a proc deprecation setting
|
34
|
+
# class MyTask < CMDx::Task
|
35
|
+
# cmd_settings! deprecated: -> { Time.now.year > 2025 ? :raise : :log }
|
36
|
+
# end
|
37
|
+
# CMDx::TaskDeprecator.call(MyTask.new) # issues warnings
|
38
|
+
#
|
39
|
+
# @example Task with no deprecation setting
|
40
|
+
# class MyTask < CMDx::Task
|
41
|
+
# end
|
42
|
+
# CMDx::TaskDeprecator.call(MyTask.new) # no action taken
|
43
|
+
def call(task)
|
44
|
+
case task.cmd_setting(:deprecated)
|
45
|
+
when :raise
|
46
|
+
raise(DeprecationError, "#{task.class.name} usage prohibited")
|
47
|
+
when :log, true
|
48
|
+
task.logger.warn { "DEPRECATED: migrate to replacement or discontinue use" }
|
49
|
+
when :warn
|
50
|
+
warn("[#{task.class.name}] DEPRECATED: migrate to replacement or discontinue use", category: :deprecated)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,245 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CMDx
|
4
|
+
# Handles the execution orchestration of Task instances with middleware and callback support.
|
5
|
+
#
|
6
|
+
# TaskProcessor provides the core execution logic for Task instances, managing
|
7
|
+
# the complete lifecycle including parameter validation, callback execution,
|
8
|
+
# middleware processing, error handling, and result finalization. It supports
|
9
|
+
# both regular and bang execution modes with different error handling behaviors.
|
10
|
+
class TaskProcessor
|
11
|
+
|
12
|
+
# @return [CMDx::Task] The task instance being executed
|
13
|
+
attr_reader :task
|
14
|
+
|
15
|
+
# Creates a new TaskProcessor instance for the specified task.
|
16
|
+
#
|
17
|
+
# @param task [CMDx::Task] the task instance to execute
|
18
|
+
#
|
19
|
+
# @return [TaskProcessor] a new TaskProcessor instance
|
20
|
+
#
|
21
|
+
# @example Create processor for a task
|
22
|
+
# task = MyTask.new(user_id: 123)
|
23
|
+
# processor = TaskProcessor.new(task)
|
24
|
+
# processor.task # => #<MyTask:...>
|
25
|
+
def initialize(task)
|
26
|
+
@task = task
|
27
|
+
end
|
28
|
+
|
29
|
+
class << self
|
30
|
+
|
31
|
+
# Executes a task with full error handling and result management.
|
32
|
+
#
|
33
|
+
# This is a convenience method that creates a new TaskProcessor instance
|
34
|
+
# and immediately calls the safe execution method. It provides the same
|
35
|
+
# comprehensive error handling as the instance method, catching faults
|
36
|
+
# and StandardErrors and converting them to failed results.
|
37
|
+
#
|
38
|
+
# @param task [CMDx::Task] the task instance to execute
|
39
|
+
#
|
40
|
+
# @return [Result] the task's result object after execution
|
41
|
+
#
|
42
|
+
# @raise [UndefinedCallError] if the task doesn't implement a call method
|
43
|
+
#
|
44
|
+
# @example Execute a task safely using class method
|
45
|
+
# task = MyTask.new(name: "test")
|
46
|
+
# result = TaskProcessor.call(task)
|
47
|
+
# result.success? # => true or false
|
48
|
+
#
|
49
|
+
# @example Handle task with validation errors
|
50
|
+
# task = MyTask.new # missing required parameters
|
51
|
+
# result = TaskProcessor.call(task)
|
52
|
+
# result.failed? # => true
|
53
|
+
def call(task)
|
54
|
+
new(task).call
|
55
|
+
end
|
56
|
+
|
57
|
+
# Executes a task with bang semantics, re-raising exceptions.
|
58
|
+
#
|
59
|
+
# This is a convenience method that creates a new TaskProcessor instance
|
60
|
+
# and immediately calls the strict execution method. It provides the same
|
61
|
+
# strict error handling as the instance method, re-raising exceptions
|
62
|
+
# after proper cleanup and chain clearing.
|
63
|
+
#
|
64
|
+
# @param task [CMDx::Task] the task instance to execute
|
65
|
+
#
|
66
|
+
# @return [Result] the task's result object after execution
|
67
|
+
#
|
68
|
+
# @raise [UndefinedCallError] if the task doesn't implement a call method
|
69
|
+
# @raise [Fault] if a fault occurs during execution
|
70
|
+
#
|
71
|
+
# @example Execute task with strict error handling
|
72
|
+
# task = MyTask.new(name: "test")
|
73
|
+
# result = TaskProcessor.call!(task) # raises on failure
|
74
|
+
#
|
75
|
+
# @example Handle exceptions in bang mode
|
76
|
+
# begin
|
77
|
+
# TaskProcessor.call!(task)
|
78
|
+
# rescue CMDx::Fault => e
|
79
|
+
# puts "Task failed: #{e.result.status}"
|
80
|
+
# end
|
81
|
+
def call!(task)
|
82
|
+
new(task).call!
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
# Executes the task with full error handling and result management.
|
88
|
+
#
|
89
|
+
# This method provides safe task execution with comprehensive error handling,
|
90
|
+
# automatic result state management, and callback execution. Faults are caught
|
91
|
+
# and processed according to task halt settings, while StandardErrors are
|
92
|
+
# converted to failed results.
|
93
|
+
#
|
94
|
+
# @return [Result] the task's result object after execution
|
95
|
+
#
|
96
|
+
# @raise [UndefinedCallError] if the task doesn't implement a call method
|
97
|
+
#
|
98
|
+
# @example Execute a task safely
|
99
|
+
# task = MyTask.new(name: "test")
|
100
|
+
# processor = TaskProcessor.new(task)
|
101
|
+
# result = processor.call
|
102
|
+
# result.success? # => true or false
|
103
|
+
#
|
104
|
+
# @example Handle task with validation errors
|
105
|
+
# task = MyTask.new # missing required parameters
|
106
|
+
# processor = TaskProcessor.new(task)
|
107
|
+
# result = processor.call
|
108
|
+
# result.failed? # => true
|
109
|
+
def call
|
110
|
+
task.result.runtime do
|
111
|
+
before_call
|
112
|
+
validate_parameters
|
113
|
+
task.call
|
114
|
+
rescue UndefinedCallError => e
|
115
|
+
raise(e)
|
116
|
+
rescue Fault => e
|
117
|
+
if Array(task.cmd_setting(:task_halt)).include?(e.result.status)
|
118
|
+
# No need to clear the Chain since exception is not being re-raised
|
119
|
+
task.result.throw!(e.result, original_exception: e)
|
120
|
+
end
|
121
|
+
rescue StandardError => e
|
122
|
+
task.result.fail!(reason: "[#{e.class}] #{e.message}", original_exception: e)
|
123
|
+
ensure
|
124
|
+
task.result.executed!
|
125
|
+
after_call
|
126
|
+
end
|
127
|
+
|
128
|
+
terminate_call
|
129
|
+
end
|
130
|
+
|
131
|
+
# Executes the task with bang semantics, re-raising exceptions.
|
132
|
+
#
|
133
|
+
# This method provides strict task execution where exceptions are re-raised
|
134
|
+
# after proper cleanup. It clears the execution chain on failures and
|
135
|
+
# provides different error handling behavior compared to the regular call method.
|
136
|
+
#
|
137
|
+
# @return [Result] the task's result object after execution
|
138
|
+
#
|
139
|
+
# @raise [UndefinedCallError] if the task doesn't implement a call method
|
140
|
+
# @raise [Fault] if a fault occurs during execution
|
141
|
+
#
|
142
|
+
# @example Execute task with strict error handling
|
143
|
+
# task = MyTask.new(name: "test")
|
144
|
+
# processor = TaskProcessor.new(task)
|
145
|
+
# result = processor.call! # raises on failure
|
146
|
+
#
|
147
|
+
# @example Handle exceptions in bang mode
|
148
|
+
# begin
|
149
|
+
# processor.call!
|
150
|
+
# rescue CMDx::Fault => e
|
151
|
+
# puts "Task failed: #{e.result.status}"
|
152
|
+
# end
|
153
|
+
def call!
|
154
|
+
task.result.runtime do
|
155
|
+
before_call
|
156
|
+
validate_parameters
|
157
|
+
task.call
|
158
|
+
rescue UndefinedCallError => e
|
159
|
+
raise!(e)
|
160
|
+
rescue Fault => e
|
161
|
+
task.result.executed!
|
162
|
+
|
163
|
+
raise!(e) if Array(task.cmd_setting(:task_halt)).include?(e.result.status)
|
164
|
+
|
165
|
+
after_call # HACK: treat as NO-OP
|
166
|
+
else
|
167
|
+
task.result.executed!
|
168
|
+
after_call # ELSE: treat as success
|
169
|
+
end
|
170
|
+
|
171
|
+
terminate_call
|
172
|
+
end
|
173
|
+
|
174
|
+
private
|
175
|
+
|
176
|
+
# Executes pre-execution callbacks and parameter validation.
|
177
|
+
#
|
178
|
+
# @return [void]
|
179
|
+
def before_call
|
180
|
+
task.cmd_callbacks.call(task, :before_execution)
|
181
|
+
|
182
|
+
task.result.executing!
|
183
|
+
task.cmd_callbacks.call(task, :on_executing)
|
184
|
+
end
|
185
|
+
|
186
|
+
# Validates task parameters and handles validation errors.
|
187
|
+
#
|
188
|
+
# This method orchestrates the parameter validation process by executing
|
189
|
+
# validation callbacks, performing parameter validation, and handling
|
190
|
+
# any validation errors that occur. If validation fails, the task result
|
191
|
+
# is marked as failed with detailed error messages.
|
192
|
+
#
|
193
|
+
# @return [void]
|
194
|
+
#
|
195
|
+
# @raise [Exception] Validations, coercions, or exceptions
|
196
|
+
def validate_parameters
|
197
|
+
task.cmd_callbacks.call(task, :before_validation)
|
198
|
+
|
199
|
+
task.cmd_parameters.validate!(task)
|
200
|
+
unless task.errors.empty?
|
201
|
+
task.result.fail!(
|
202
|
+
reason: task.errors.full_messages.join(". "),
|
203
|
+
messages: task.errors.messages
|
204
|
+
)
|
205
|
+
end
|
206
|
+
|
207
|
+
task.cmd_callbacks.call(task, :after_validation)
|
208
|
+
end
|
209
|
+
|
210
|
+
# Clears the execution chain and re-raises the given exception.
|
211
|
+
#
|
212
|
+
# @param exception [Exception] the exception to re-raise after chain cleanup
|
213
|
+
#
|
214
|
+
# @return [void] this method never returns as it always raises
|
215
|
+
#
|
216
|
+
# @raise [Exception] always re-raises the provided exception
|
217
|
+
def raise!(exception)
|
218
|
+
Chain.clear
|
219
|
+
raise(exception)
|
220
|
+
end
|
221
|
+
|
222
|
+
# Executes post-execution callbacks based on result state and status.
|
223
|
+
#
|
224
|
+
# @return [void]
|
225
|
+
def after_call
|
226
|
+
task.cmd_callbacks.call(task, :"on_#{task.result.state}")
|
227
|
+
task.cmd_callbacks.call(task, :on_executed) if task.result.executed?
|
228
|
+
|
229
|
+
task.cmd_callbacks.call(task, :"on_#{task.result.status}")
|
230
|
+
task.cmd_callbacks.call(task, :on_good) if task.result.good?
|
231
|
+
task.cmd_callbacks.call(task, :on_bad) if task.result.bad?
|
232
|
+
|
233
|
+
task.cmd_callbacks.call(task, :after_execution)
|
234
|
+
end
|
235
|
+
|
236
|
+
# Finalizes task execution with immutability and logging.
|
237
|
+
#
|
238
|
+
# @return [Result] the task's result object
|
239
|
+
def terminate_call
|
240
|
+
Immutator.call(task)
|
241
|
+
ResultLogger.call(task.result)
|
242
|
+
end
|
243
|
+
|
244
|
+
end
|
245
|
+
end
|
data/lib/cmdx/task_serializer.rb
CHANGED
@@ -1,82 +1,34 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module CMDx
|
4
|
-
|
5
|
-
#
|
6
|
-
#
|
7
|
-
# about the task execution context and identification.
|
8
|
-
#
|
9
|
-
# The serialized format includes:
|
10
|
-
# - Execution index within the chain
|
11
|
-
# - Chain identifier for grouping related tasks
|
12
|
-
# - Task type (Task vs Workflow)
|
13
|
-
# - Class name for identification
|
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
|
4
|
+
# Serializes Task objects into hash representations for external consumption.
|
5
|
+
# Provides a consistent interface for converting task execution data into
|
6
|
+
# structured format suitable for logging, API responses, or persistence.
|
53
7
|
module TaskSerializer
|
54
8
|
|
55
9
|
module_function
|
56
10
|
|
57
|
-
|
58
|
-
#
|
59
|
-
#
|
11
|
+
# Converts a task object into a hash representation containing task metadata.
|
12
|
+
# Extracts key task attributes including execution index, chain association,
|
13
|
+
# type classification, and associated tags for complete task identification.
|
60
14
|
#
|
61
|
-
# @param task [Task
|
62
|
-
# @return [Hash] serialized task data with the following keys:
|
63
|
-
# - :index [Integer] position within the execution chain
|
64
|
-
# - :chain_id [String] identifier of the containing chain
|
65
|
-
# - :type [String] "Task" or "Workflow" based on instance type
|
66
|
-
# - :class [String] class name of the task
|
67
|
-
# - :id [String] unique identifier for this task instance
|
68
|
-
# - :tags [Array] array of tags associated with the task
|
15
|
+
# @param task [Task] the task instance to serialize
|
69
16
|
#
|
70
|
-
# @
|
71
|
-
#
|
72
|
-
#
|
73
|
-
# data[:class] #=> "MyTask"
|
74
|
-
# data[:type] #=> "Task"
|
75
|
-
# data[:id] #=> "550e8400-e29b-41d4-a716-446655440000"
|
17
|
+
# @return [Hash] hash containing task metadata and execution details
|
18
|
+
#
|
19
|
+
# @raise [NoMethodError] if task doesn't respond to required methods
|
76
20
|
#
|
77
|
-
# @example
|
78
|
-
#
|
79
|
-
#
|
21
|
+
# @example Serializing a task
|
22
|
+
# task = UserRegistrationTask.call(email: "user@example.com")
|
23
|
+
# TaskSerializer.call(task)
|
24
|
+
# # => {
|
25
|
+
# # index: 0,
|
26
|
+
# # chain_id: "abc123",
|
27
|
+
# # type: "Task",
|
28
|
+
# # class: "UserRegistrationTask",
|
29
|
+
# # id: "def456",
|
30
|
+
# # tags: [:authentication, :user_management]
|
31
|
+
# # }
|
80
32
|
def call(task)
|
81
33
|
{
|
82
34
|
index: task.result.index,
|
@@ -84,7 +36,7 @@ module CMDx
|
|
84
36
|
type: task.is_a?(Workflow) ? "Workflow" : "Task",
|
85
37
|
class: task.class.name,
|
86
38
|
id: task.id,
|
87
|
-
tags: task.
|
39
|
+
tags: task.cmd_setting(:tags)
|
88
40
|
}
|
89
41
|
end
|
90
42
|
|
@@ -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
|