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.
Files changed (170) hide show
  1. checksums.yaml +4 -4
  2. data/.cursor/prompts/docs.md +9 -0
  3. data/.cursor/prompts/rspec.md +21 -0
  4. data/.cursor/prompts/yardoc.md +13 -0
  5. data/.rubocop.yml +2 -0
  6. data/CHANGELOG.md +29 -3
  7. data/README.md +2 -1
  8. data/docs/ai_prompts.md +269 -195
  9. data/docs/basics/call.md +126 -60
  10. data/docs/basics/chain.md +190 -160
  11. data/docs/basics/context.md +242 -154
  12. data/docs/basics/setup.md +302 -32
  13. data/docs/callbacks.md +382 -119
  14. data/docs/configuration.md +211 -49
  15. data/docs/deprecation.md +245 -0
  16. data/docs/getting_started.md +161 -39
  17. data/docs/internationalization.md +590 -70
  18. data/docs/interruptions/exceptions.md +135 -118
  19. data/docs/interruptions/faults.md +152 -127
  20. data/docs/interruptions/halt.md +134 -80
  21. data/docs/logging.md +183 -120
  22. data/docs/middlewares.md +165 -392
  23. data/docs/outcomes/result.md +140 -112
  24. data/docs/outcomes/states.md +134 -99
  25. data/docs/outcomes/statuses.md +204 -146
  26. data/docs/parameters/coercions.md +251 -289
  27. data/docs/parameters/defaults.md +224 -169
  28. data/docs/parameters/definitions.md +289 -141
  29. data/docs/parameters/namespacing.md +250 -161
  30. data/docs/parameters/validations.md +247 -159
  31. data/docs/testing.md +196 -203
  32. data/docs/workflows.md +146 -101
  33. data/lib/cmdx/.DS_Store +0 -0
  34. data/lib/cmdx/callback.rb +39 -55
  35. data/lib/cmdx/callback_registry.rb +80 -73
  36. data/lib/cmdx/chain.rb +65 -122
  37. data/lib/cmdx/chain_inspector.rb +23 -116
  38. data/lib/cmdx/chain_serializer.rb +34 -146
  39. data/lib/cmdx/coercion.rb +57 -0
  40. data/lib/cmdx/coercion_registry.rb +113 -0
  41. data/lib/cmdx/coercions/array.rb +18 -36
  42. data/lib/cmdx/coercions/big_decimal.rb +21 -33
  43. data/lib/cmdx/coercions/boolean.rb +21 -40
  44. data/lib/cmdx/coercions/complex.rb +18 -31
  45. data/lib/cmdx/coercions/date.rb +20 -39
  46. data/lib/cmdx/coercions/date_time.rb +22 -39
  47. data/lib/cmdx/coercions/float.rb +19 -32
  48. data/lib/cmdx/coercions/hash.rb +22 -41
  49. data/lib/cmdx/coercions/integer.rb +20 -33
  50. data/lib/cmdx/coercions/rational.rb +20 -32
  51. data/lib/cmdx/coercions/string.rb +23 -31
  52. data/lib/cmdx/coercions/time.rb +24 -40
  53. data/lib/cmdx/coercions/virtual.rb +14 -31
  54. data/lib/cmdx/configuration.rb +101 -162
  55. data/lib/cmdx/context.rb +34 -166
  56. data/lib/cmdx/core_ext/hash.rb +42 -67
  57. data/lib/cmdx/core_ext/module.rb +35 -79
  58. data/lib/cmdx/core_ext/object.rb +63 -98
  59. data/lib/cmdx/correlator.rb +59 -154
  60. data/lib/cmdx/error.rb +37 -202
  61. data/lib/cmdx/errors.rb +153 -216
  62. data/lib/cmdx/fault.rb +68 -150
  63. data/lib/cmdx/faults.rb +26 -137
  64. data/lib/cmdx/immutator.rb +22 -110
  65. data/lib/cmdx/lazy_struct.rb +110 -186
  66. data/lib/cmdx/log_formatters/json.rb +14 -40
  67. data/lib/cmdx/log_formatters/key_value.rb +14 -40
  68. data/lib/cmdx/log_formatters/line.rb +14 -48
  69. data/lib/cmdx/log_formatters/logstash.rb +14 -57
  70. data/lib/cmdx/log_formatters/pretty_json.rb +14 -50
  71. data/lib/cmdx/log_formatters/pretty_key_value.rb +13 -46
  72. data/lib/cmdx/log_formatters/pretty_line.rb +16 -54
  73. data/lib/cmdx/log_formatters/raw.rb +19 -49
  74. data/lib/cmdx/logger.rb +22 -79
  75. data/lib/cmdx/logger_ansi.rb +31 -72
  76. data/lib/cmdx/logger_serializer.rb +74 -103
  77. data/lib/cmdx/middleware.rb +56 -60
  78. data/lib/cmdx/middleware_registry.rb +82 -77
  79. data/lib/cmdx/middlewares/correlate.rb +41 -226
  80. data/lib/cmdx/middlewares/timeout.rb +46 -185
  81. data/lib/cmdx/parameter.rb +167 -183
  82. data/lib/cmdx/parameter_evaluator.rb +231 -0
  83. data/lib/cmdx/parameter_inspector.rb +37 -55
  84. data/lib/cmdx/parameter_registry.rb +65 -84
  85. data/lib/cmdx/parameter_serializer.rb +32 -76
  86. data/lib/cmdx/railtie.rb +24 -107
  87. data/lib/cmdx/result.rb +254 -259
  88. data/lib/cmdx/result_ansi.rb +28 -80
  89. data/lib/cmdx/result_inspector.rb +34 -70
  90. data/lib/cmdx/result_logger.rb +23 -77
  91. data/lib/cmdx/result_serializer.rb +59 -125
  92. data/lib/cmdx/rspec/matchers.rb +28 -0
  93. data/lib/cmdx/rspec/result_matchers/be_executed.rb +42 -0
  94. data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +94 -0
  95. data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +94 -0
  96. data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +59 -0
  97. data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +57 -0
  98. data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +87 -0
  99. data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +51 -0
  100. data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +58 -0
  101. data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +59 -0
  102. data/lib/cmdx/rspec/result_matchers/have_context.rb +86 -0
  103. data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +54 -0
  104. data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +52 -0
  105. data/lib/cmdx/rspec/result_matchers/have_metadata.rb +114 -0
  106. data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +66 -0
  107. data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +64 -0
  108. data/lib/cmdx/rspec/result_matchers/have_runtime.rb +78 -0
  109. data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +76 -0
  110. data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +62 -0
  111. data/lib/cmdx/rspec/task_matchers/have_callback.rb +85 -0
  112. data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +68 -0
  113. data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +92 -0
  114. data/lib/cmdx/rspec/task_matchers/have_middleware.rb +46 -0
  115. data/lib/cmdx/rspec/task_matchers/have_parameter.rb +181 -0
  116. data/lib/cmdx/task.rb +336 -427
  117. data/lib/cmdx/task_deprecator.rb +52 -0
  118. data/lib/cmdx/task_processor.rb +246 -0
  119. data/lib/cmdx/task_serializer.rb +34 -69
  120. data/lib/cmdx/utils/ansi_color.rb +13 -89
  121. data/lib/cmdx/utils/log_timestamp.rb +13 -42
  122. data/lib/cmdx/utils/monotonic_runtime.rb +11 -63
  123. data/lib/cmdx/utils/name_affix.rb +21 -71
  124. data/lib/cmdx/validator.rb +57 -0
  125. data/lib/cmdx/validator_registry.rb +108 -0
  126. data/lib/cmdx/validators/exclusion.rb +55 -94
  127. data/lib/cmdx/validators/format.rb +31 -85
  128. data/lib/cmdx/validators/inclusion.rb +65 -110
  129. data/lib/cmdx/validators/length.rb +117 -133
  130. data/lib/cmdx/validators/numeric.rb +123 -130
  131. data/lib/cmdx/validators/presence.rb +38 -79
  132. data/lib/cmdx/version.rb +1 -7
  133. data/lib/cmdx/workflow.rb +58 -330
  134. data/lib/cmdx.rb +1 -1
  135. data/lib/generators/cmdx/install_generator.rb +14 -31
  136. data/lib/generators/cmdx/task_generator.rb +39 -55
  137. data/lib/generators/cmdx/templates/install.rb +24 -6
  138. data/lib/generators/cmdx/workflow_generator.rb +41 -66
  139. data/lib/locales/ar.yml +0 -1
  140. data/lib/locales/cs.yml +0 -1
  141. data/lib/locales/da.yml +0 -1
  142. data/lib/locales/de.yml +0 -1
  143. data/lib/locales/el.yml +0 -1
  144. data/lib/locales/en.yml +0 -1
  145. data/lib/locales/es.yml +0 -1
  146. data/lib/locales/fi.yml +0 -1
  147. data/lib/locales/fr.yml +0 -1
  148. data/lib/locales/he.yml +0 -1
  149. data/lib/locales/hi.yml +0 -1
  150. data/lib/locales/it.yml +0 -1
  151. data/lib/locales/ja.yml +0 -1
  152. data/lib/locales/ko.yml +0 -1
  153. data/lib/locales/nl.yml +0 -1
  154. data/lib/locales/no.yml +0 -1
  155. data/lib/locales/pl.yml +0 -1
  156. data/lib/locales/pt.yml +0 -1
  157. data/lib/locales/ru.yml +0 -1
  158. data/lib/locales/sv.yml +0 -1
  159. data/lib/locales/th.yml +0 -1
  160. data/lib/locales/tr.yml +0 -1
  161. data/lib/locales/vi.yml +0 -1
  162. data/lib/locales/zh.yml +0 -1
  163. metadata +36 -8
  164. data/lib/cmdx/parameter_validator.rb +0 -81
  165. data/lib/cmdx/parameter_value.rb +0 -244
  166. data/lib/cmdx/parameters_inspector.rb +0 -72
  167. data/lib/cmdx/parameters_serializer.rb +0 -115
  168. data/lib/cmdx/rspec/result_matchers.rb +0 -917
  169. data/lib/cmdx/rspec/task_matchers.rb +0 -570
  170. 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
@@ -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
- # 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
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
- # Serializes a task instance into a hash representation containing
59
- # essential metadata for identification and tracking.
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
- # @param task [Task, Workflow] the task instance to serialize
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
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
- # @example Serializing a task
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 Using serialized data for logging
78
- # task_data = TaskSerializer.call(task)
79
- # logger.info("Task executed", task_data)
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.task_setting(:tags)
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 adding ANSI color codes to terminal output.
5
+ # Utility module for applying ANSI color and formatting codes to text.
6
6
  #
7
- # AnsiColor provides methods to colorize text output in terminal
8
- # environments, supporting various colors and text modes for
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
- # Apply ANSI color and mode formatting to text.
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
- # @example Error message with red text
122
- # AnsiColor.call("Error", color: :red)
123
- # # => "\e[0;31;49mError\e[0m"
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
- # @example Warning with yellow underlined text
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
- # @example Debug info with dimmed light text
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 Invalid color raises KeyError
134
- # AnsiColor.call("Text", color: :invalid_color)
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
- # @note The escape sequence format is: \e[{mode};{color};49m{text}\e[0m
138
- # @note The "49" represents the default background color
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 in CMDx log entries.
5
+ # Utility module for formatting timestamps into standardized string representations.
6
6
  #
7
- # LogTimestamp provides consistent timestamp formatting across all CMDx
8
- # log formatters, ensuring uniform time representation in logs regardless
9
- # of the chosen output format. Uses ISO 8601 format with microsecond precision.
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 as an ISO 8601 timestamp string.
16
+ # Formats a Time object into a standardized timestamp string.
38
17
  #
39
- # Converts the given time to a standardized string representation
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
- # @param time [Time] Time object to format
44
- # @return [String] ISO 8601 formatted timestamp with microseconds
20
+ # @return [String] the formatted timestamp string in ISO 8601-like format
45
21
  #
46
- # @example Current time formatting
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 UTC time formatting for logs
51
- # LogTimestamp.call(Time.now.utc)
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 Integration with log formatters
55
- # def format_log_entry(severity, time, message)
56
- # timestamp = LogTimestamp.call(time.utc)
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