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.
Files changed (157) hide show
  1. checksums.yaml +4 -4
  2. data/.cursor/prompts/rspec.md +20 -0
  3. data/.cursor/prompts/yardoc.md +8 -0
  4. data/.rubocop.yml +2 -0
  5. data/CHANGELOG.md +17 -2
  6. data/README.md +1 -1
  7. data/docs/basics/call.md +2 -2
  8. data/docs/basics/chain.md +1 -1
  9. data/docs/callbacks.md +3 -36
  10. data/docs/configuration.md +58 -12
  11. data/docs/interruptions/exceptions.md +1 -1
  12. data/docs/interruptions/faults.md +2 -2
  13. data/docs/logging.md +4 -4
  14. data/docs/middlewares.md +43 -43
  15. data/docs/parameters/coercions.md +49 -38
  16. data/docs/parameters/defaults.md +1 -1
  17. data/docs/parameters/validations.md +0 -39
  18. data/docs/testing.md +11 -12
  19. data/docs/workflows.md +4 -4
  20. data/lib/cmdx/.DS_Store +0 -0
  21. data/lib/cmdx/callback.rb +36 -56
  22. data/lib/cmdx/callback_registry.rb +82 -73
  23. data/lib/cmdx/chain.rb +65 -122
  24. data/lib/cmdx/chain_inspector.rb +22 -115
  25. data/lib/cmdx/chain_serializer.rb +17 -148
  26. data/lib/cmdx/coercion.rb +49 -0
  27. data/lib/cmdx/coercion_registry.rb +94 -0
  28. data/lib/cmdx/coercions/array.rb +18 -36
  29. data/lib/cmdx/coercions/big_decimal.rb +21 -33
  30. data/lib/cmdx/coercions/boolean.rb +21 -40
  31. data/lib/cmdx/coercions/complex.rb +18 -31
  32. data/lib/cmdx/coercions/date.rb +20 -39
  33. data/lib/cmdx/coercions/date_time.rb +22 -39
  34. data/lib/cmdx/coercions/float.rb +19 -32
  35. data/lib/cmdx/coercions/hash.rb +22 -41
  36. data/lib/cmdx/coercions/integer.rb +20 -33
  37. data/lib/cmdx/coercions/rational.rb +20 -32
  38. data/lib/cmdx/coercions/string.rb +23 -31
  39. data/lib/cmdx/coercions/time.rb +24 -40
  40. data/lib/cmdx/coercions/virtual.rb +14 -31
  41. data/lib/cmdx/configuration.rb +57 -171
  42. data/lib/cmdx/context.rb +22 -165
  43. data/lib/cmdx/core_ext/hash.rb +42 -67
  44. data/lib/cmdx/core_ext/module.rb +35 -79
  45. data/lib/cmdx/core_ext/object.rb +63 -98
  46. data/lib/cmdx/correlator.rb +40 -156
  47. data/lib/cmdx/error.rb +37 -202
  48. data/lib/cmdx/errors.rb +165 -202
  49. data/lib/cmdx/fault.rb +55 -158
  50. data/lib/cmdx/faults.rb +26 -137
  51. data/lib/cmdx/immutator.rb +22 -109
  52. data/lib/cmdx/lazy_struct.rb +103 -187
  53. data/lib/cmdx/log_formatters/json.rb +14 -40
  54. data/lib/cmdx/log_formatters/key_value.rb +14 -40
  55. data/lib/cmdx/log_formatters/line.rb +14 -48
  56. data/lib/cmdx/log_formatters/logstash.rb +14 -57
  57. data/lib/cmdx/log_formatters/pretty_json.rb +14 -50
  58. data/lib/cmdx/log_formatters/pretty_key_value.rb +13 -46
  59. data/lib/cmdx/log_formatters/pretty_line.rb +16 -54
  60. data/lib/cmdx/log_formatters/raw.rb +19 -49
  61. data/lib/cmdx/logger.rb +20 -82
  62. data/lib/cmdx/logger_ansi.rb +18 -75
  63. data/lib/cmdx/logger_serializer.rb +24 -114
  64. data/lib/cmdx/middleware.rb +38 -60
  65. data/lib/cmdx/middleware_registry.rb +81 -77
  66. data/lib/cmdx/middlewares/correlate.rb +41 -226
  67. data/lib/cmdx/middlewares/timeout.rb +46 -185
  68. data/lib/cmdx/parameter.rb +120 -198
  69. data/lib/cmdx/parameter_evaluator.rb +231 -0
  70. data/lib/cmdx/parameter_inspector.rb +25 -56
  71. data/lib/cmdx/parameter_registry.rb +59 -84
  72. data/lib/cmdx/parameter_serializer.rb +23 -74
  73. data/lib/cmdx/railtie.rb +24 -107
  74. data/lib/cmdx/result.rb +254 -260
  75. data/lib/cmdx/result_ansi.rb +19 -85
  76. data/lib/cmdx/result_inspector.rb +27 -68
  77. data/lib/cmdx/result_logger.rb +18 -81
  78. data/lib/cmdx/result_serializer.rb +28 -132
  79. data/lib/cmdx/rspec/matchers.rb +28 -0
  80. data/lib/cmdx/rspec/result_matchers/be_executed.rb +42 -0
  81. data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +94 -0
  82. data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +94 -0
  83. data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +59 -0
  84. data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +57 -0
  85. data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +87 -0
  86. data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +51 -0
  87. data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +58 -0
  88. data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +59 -0
  89. data/lib/cmdx/rspec/result_matchers/have_context.rb +86 -0
  90. data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +54 -0
  91. data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +52 -0
  92. data/lib/cmdx/rspec/result_matchers/have_metadata.rb +114 -0
  93. data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +66 -0
  94. data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +64 -0
  95. data/lib/cmdx/rspec/result_matchers/have_runtime.rb +78 -0
  96. data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +76 -0
  97. data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +62 -0
  98. data/lib/cmdx/rspec/task_matchers/have_callback.rb +85 -0
  99. data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +68 -0
  100. data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +92 -0
  101. data/lib/cmdx/rspec/task_matchers/have_middleware.rb +46 -0
  102. data/lib/cmdx/rspec/task_matchers/have_parameter.rb +181 -0
  103. data/lib/cmdx/task.rb +213 -425
  104. data/lib/cmdx/task_deprecator.rb +55 -0
  105. data/lib/cmdx/task_processor.rb +245 -0
  106. data/lib/cmdx/task_serializer.rb +22 -70
  107. data/lib/cmdx/utils/ansi_color.rb +13 -89
  108. data/lib/cmdx/utils/log_timestamp.rb +13 -42
  109. data/lib/cmdx/utils/monotonic_runtime.rb +13 -63
  110. data/lib/cmdx/utils/name_affix.rb +21 -71
  111. data/lib/cmdx/validator.rb +48 -0
  112. data/lib/cmdx/validator_registry.rb +86 -0
  113. data/lib/cmdx/validators/exclusion.rb +55 -94
  114. data/lib/cmdx/validators/format.rb +31 -85
  115. data/lib/cmdx/validators/inclusion.rb +65 -110
  116. data/lib/cmdx/validators/length.rb +117 -133
  117. data/lib/cmdx/validators/numeric.rb +123 -130
  118. data/lib/cmdx/validators/presence.rb +38 -79
  119. data/lib/cmdx/version.rb +1 -7
  120. data/lib/cmdx/workflow.rb +46 -339
  121. data/lib/cmdx.rb +1 -1
  122. data/lib/generators/cmdx/install_generator.rb +14 -31
  123. data/lib/generators/cmdx/task_generator.rb +39 -55
  124. data/lib/generators/cmdx/templates/install.rb +24 -6
  125. data/lib/generators/cmdx/workflow_generator.rb +41 -66
  126. data/lib/locales/ar.yml +0 -1
  127. data/lib/locales/cs.yml +0 -1
  128. data/lib/locales/da.yml +0 -1
  129. data/lib/locales/de.yml +0 -1
  130. data/lib/locales/el.yml +0 -1
  131. data/lib/locales/en.yml +0 -1
  132. data/lib/locales/es.yml +0 -1
  133. data/lib/locales/fi.yml +0 -1
  134. data/lib/locales/fr.yml +0 -1
  135. data/lib/locales/he.yml +0 -1
  136. data/lib/locales/hi.yml +0 -1
  137. data/lib/locales/it.yml +0 -1
  138. data/lib/locales/ja.yml +0 -1
  139. data/lib/locales/ko.yml +0 -1
  140. data/lib/locales/nl.yml +0 -1
  141. data/lib/locales/no.yml +0 -1
  142. data/lib/locales/pl.yml +0 -1
  143. data/lib/locales/pt.yml +0 -1
  144. data/lib/locales/ru.yml +0 -1
  145. data/lib/locales/sv.yml +0 -1
  146. data/lib/locales/th.yml +0 -1
  147. data/lib/locales/tr.yml +0 -1
  148. data/lib/locales/vi.yml +0 -1
  149. data/lib/locales/zh.yml +0 -1
  150. metadata +34 -8
  151. data/lib/cmdx/parameter_validator.rb +0 -81
  152. data/lib/cmdx/parameter_value.rb +0 -244
  153. data/lib/cmdx/parameters_inspector.rb +0 -72
  154. data/lib/cmdx/parameters_serializer.rb +0 -115
  155. data/lib/cmdx/rspec/result_matchers.rb +0 -917
  156. data/lib/cmdx/rspec/task_matchers.rb +0 -570
  157. 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
@@ -1,82 +1,34 @@
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.
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
- # Serializes a task instance into a hash representation containing
59
- # essential metadata for identification and tracking.
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, 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
15
+ # @param task [Task] the task instance to serialize
69
16
  #
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"
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 Using serialized data for logging
78
- # task_data = TaskSerializer.call(task)
79
- # logger.info("Task executed", task_data)
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.task_setting(:tags)
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 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