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
data/lib/cmdx/callback.rb CHANGED
@@ -1,67 +1,47 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CMDx
4
- ##
5
- # Base class for CMDx callbacks that provides lifecycle execution points.
4
+ # Base class for implementing callback functionality in task execution.
6
5
  #
7
- # Callback components can wrap or observe task execution at specific lifecycle
8
- # points like before validation, on success, after execution, etc.
9
- # Each callback must implement the `call` method which receives the
10
- # task instance and callback context.
11
- #
12
- # @example Basic callback implementation
13
- # class LoggingCallback < CMDx::Callback
14
- # def call(task, callback_type)
15
- # puts "Executing #{callback_type} callback for #{task.class.name}"
16
- # task.logger.info("Callback executed: #{callback_type}")
17
- # end
18
- # end
19
- #
20
- # @example Callback with initialization parameters
21
- # class NotificationCallback < CMDx::Callback
22
- # def initialize(channels)
23
- # @channels = channels
24
- # end
25
- #
26
- # def call(task, callback_type)
27
- # return unless callback_type == :on_success
28
- #
29
- # @channels.each do |channel|
30
- # NotificationService.send(channel, "Task #{task.class.name} completed")
31
- # end
32
- # end
33
- # end
34
- #
35
- # @example Conditional callback execution
36
- # class ErrorReportingCallback < CMDx::Callback
37
- # def call(task, callback_type)
38
- # return unless callback_type == :on_failure
39
- # return unless task.result.failed?
40
- #
41
- # ErrorReporter.notify(
42
- # task.errors.full_messages.join(", "),
43
- # context: task.context.to_h
44
- # )
45
- # end
46
- # end
47
- #
48
- # @see CallbackRegistry Callback management
49
- # @see Task Callback integration
50
- # @since 1.0.0
6
+ # Callbacks are executed at specific points during task lifecycle to
7
+ # provide hooks for custom behavior, logging, validation, or cleanup.
8
+ # All callback implementations must inherit from this class and implement
9
+ # the abstract call method.
51
10
  class Callback
52
11
 
53
- ##
54
- # Executes the callback logic.
12
+ # Executes a callback by creating a new instance and calling it.
13
+ #
14
+ # @param task [Task] the task instance executing the callback
15
+ # @param type [Symbol] the callback type identifier
16
+ #
17
+ # @return [Object] the result of the callback execution
18
+ #
19
+ # @raise [UndefinedCallError] when the callback subclass doesn't implement call
20
+ #
21
+ # @example Execute a callback on a task
22
+ # MyCallback.call(task, :before)
23
+ def self.call(task, type)
24
+ new.call(task, type)
25
+ end
26
+
27
+ # Abstract method that must be implemented by callback subclasses.
28
+ #
29
+ # This method contains the actual callback logic to be executed.
30
+ # Subclasses must override this method to provide their specific
31
+ # callback implementation.
32
+ #
33
+ # @param _task [Task] the task instance executing the callback
34
+ # @param _type [Symbol] the callback type identifier
35
+ #
36
+ # @return [Object] the result of the callback execution
55
37
  #
56
- # This method must be implemented by subclasses to define the callback
57
- # behavior. The method receives the task instance and the callback type
58
- # being executed.
38
+ # @raise [UndefinedCallError] always raised in the base class
59
39
  #
60
- # @param task [Task] the task instance being executed
61
- # @param callback_type [Symbol] the type of callback being executed
62
- # @return [void]
63
- # @abstract Subclasses must implement this method
64
- def call(_task, _callback_type)
40
+ # @example Implement in a subclass
41
+ # def call(task, type)
42
+ # puts "Executing #{type} callback for #{task.class.name}"
43
+ # end
44
+ def call(_task, _type)
65
45
  raise UndefinedCallError, "call method not defined in #{self.class.name}"
66
46
  end
67
47
 
@@ -1,106 +1,115 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CMDx
4
- ##
5
- # The CallbackRegistry collection provides a lifecycle callback system that executes
6
- # registered callbacks at specific points during task execution. Callbacks can be
7
- # conditionally executed based on task state and support both method references
8
- # and callable objects.
4
+ # Registry for managing callback definitions and execution within tasks.
9
5
  #
10
- # The CallbackRegistry collection extends Hash to provide specialized functionality for
11
- # managing collections of callback definitions within CMDx tasks. It handles
12
- # callback registration, conditional execution, and inspection.
13
- #
14
- # @example Basic callback usage
15
- # callback_registry = CallbackRegistry.new
16
- # callback_registry.register(:before_validation, :check_permissions)
17
- # callback_registry.register(:on_success, :log_success, if: :important?)
18
- # callback_registry.register(:on_failure, proc { alert_admin }, unless: :test_env?)
19
- #
20
- # callback_registry.call(task, :before_validation)
21
- #
22
- # @example Hash-like operations
23
- # callback_registry[:before_validation] = [[:check_permissions, {}]]
24
- # callback_registry.keys # => [:before_validation]
25
- # callback_registry.empty? # => false
26
- # callback_registry.each { |callback_name, callbacks| puts "#{callback_name}: #{callbacks}" }
27
- #
28
- # @see Callback Base callback execution class
29
- # @see Task Task lifecycle callbacks
30
- # @since 1.0.0
31
- class CallbackRegistry < Hash
6
+ # This registry handles the registration and execution of callbacks at various
7
+ # points in the task lifecycle, including validation, execution, and outcome
8
+ # handling phases.
9
+ class CallbackRegistry
10
+
11
+ TYPES = [
12
+ :before_validation,
13
+ :after_validation,
14
+ :before_execution,
15
+ :after_execution,
16
+ :on_executed,
17
+ :on_good,
18
+ :on_bad,
19
+ *Result::STATUSES.map { |s| :"on_#{s}" },
20
+ *Result::STATES.map { |s| :"on_#{s}" }
21
+ ].freeze
32
22
 
33
- ##
34
- # Initializes a new CallbackRegistry.
23
+ # The internal hash storing callback definitions.
35
24
  #
36
- # @param registry [CallbackRegistry, Hash, nil] Optional registry to copy from
25
+ # @return [Hash] hash containing callback type keys and callback definition arrays
26
+ attr_reader :registry
27
+
28
+ # Initializes a new callback registry.
37
29
  #
38
- # @example Initialize empty registry
39
- # registry = CallbackRegistry.new
30
+ # @param registry [Hash] initial registry hash with callback definitions
40
31
  #
41
- # @example Initialize with existing registry
42
- # global_callbacks = CallbackRegistry.new
43
- # task_callbacks = CallbackRegistry.new(global_callbacks)
44
- def initialize(registry = nil)
45
- super()
46
-
47
- registry&.each do |callback_type, callback_definitions|
48
- self[callback_type] = callback_definitions.dup
49
- end
32
+ # @return [CallbackRegistry] a new callback registry instance
33
+ #
34
+ # @example Creating an empty registry
35
+ # CallbackRegistry.new
36
+ #
37
+ # @example Creating a registry with initial callbacks
38
+ # CallbackRegistry.new(before_execution: [[:my_callback, {}]])
39
+ def initialize(registry = {})
40
+ @registry = registry.to_h
50
41
  end
51
42
 
52
- # Registers a callback for the given callback type.
43
+ # Registers one or more callbacks for a specific type.
53
44
  #
54
- # @param callback [Symbol] The callback type (e.g., :before_validation, :on_success)
55
- # @param callables [Array<Symbol, Proc, #call>] Methods or callables to execute
56
- # @param options [Hash] Conditions for callback execution
57
- # @option options [Symbol, Proc, #call] :if condition that must be truthy
58
- # @option options [Symbol, Proc, #call] :unless condition that must be falsy
59
- # @param block [Proc] Block to execute as part of the callback
60
- # @return [CallbackRegistry] self for method chaining
45
+ # @param type [Symbol] the callback type to register
46
+ # @param callables [Array<Object>] callable objects to register
47
+ # @param options [Hash] options for conditional callback execution
48
+ # @param block [Proc] optional block to register as a callback
61
49
  #
62
- # @example Register method callback
63
- # registry.register(:before_validation, :check_permissions)
50
+ # @return [CallbackRegistry] returns self for method chaining
64
51
  #
65
- # @example Register conditional callback
66
- # registry.register(:on_failure, :alert_admin, if: :critical?)
52
+ # @example Registering a symbol callback
53
+ # registry.register(:before_execution, :setup_database)
67
54
  #
68
- # @example Register proc callback
69
- # registry.register(:on_success, proc { log_completion })
70
- def register(callback, *callables, **options, &block)
55
+ # @example Registering a Proc callback
56
+ # registry.register(:on_good, ->(task) { puts "Task completed: #{task.name}" })
57
+ #
58
+ # @example Registering a Callback class
59
+ # registry.register(:after_validation, NotificationCallback)
60
+ #
61
+ # @example Registering multiple callbacks with options
62
+ # registry.register(:on_good, :send_notification, :log_success, if: -> { Rails.env.production? })
63
+ #
64
+ # @example Registering a block callback
65
+ # registry.register(:after_validation) { |task| puts "Validation complete" }
66
+ def register(type, *callables, **options, &block)
71
67
  callables << block if block_given?
72
- (self[callback] ||= []).push([callables, options]).uniq!
68
+ (registry[type] ||= []).push([callables, options]).uniq!
73
69
  self
74
70
  end
75
71
 
76
- # Executes all callbacks registered for a specific callback type on the given task.
77
- # Each callback is evaluated for its conditions (if/unless) before execution.
72
+ # Executes all registered callbacks for a specific type.
73
+ #
74
+ # @param task [Task] the task instance to execute callbacks on
75
+ # @param type [Symbol] the callback type to execute
78
76
  #
79
- # @param task [Task] The task instance to execute callbacks on
80
- # @param callback [Symbol] The callback type to execute (e.g., :before_validation, :on_success)
81
77
  # @return [void]
82
78
  #
83
- # @example Execute callbacks
79
+ # @raise [UnknownCallbackError] when the callback type is not recognized
80
+ #
81
+ # @example Executing before_validation callbacks
84
82
  # registry.call(task, :before_validation)
85
83
  #
86
- # @example Execute conditional callbacks
87
- # # Only executes if task.critical? returns true
88
- # registry.call(task, :on_failure) # where registry has on_failure :alert, if: :critical?
89
- def call(task, callback)
90
- return unless key?(callback)
84
+ # @example Executing outcome callbacks
85
+ # registry.call(task, :on_good)
86
+ def call(task, type)
87
+ raise UnknownCallbackError, "unknown callback #{type}" unless TYPES.include?(type)
91
88
 
92
- Array(self[callback]).each do |callables, options|
93
- next unless task.__cmdx_eval(options)
89
+ Array(registry[type]).each do |callables, options|
90
+ next unless task.cmdx_eval(options)
94
91
 
95
- Array(callables).each do |c|
96
- if c.is_a?(Callback)
97
- c.call(task, callback)
92
+ Array(callables).each do |callable|
93
+ case callable
94
+ when Symbol, String, Proc
95
+ task.cmdx_try(callable)
98
96
  else
99
- task.__cmdx_try(c)
97
+ callable.call(task)
100
98
  end
101
99
  end
102
100
  end
103
101
  end
104
102
 
103
+ # Returns a hash representation of the registry.
104
+ #
105
+ # @return [Hash] a deep copy of the registry hash
106
+ #
107
+ # @example Getting registry contents
108
+ # registry.to_h
109
+ # # => { before_execution: [[:setup, {}]], on_good: [[:notify, { if: -> { true } }]] }
110
+ def to_h
111
+ registry.transform_values(&:dup)
112
+ end
113
+
105
114
  end
106
115
  end
data/lib/cmdx/chain.rb CHANGED
@@ -1,79 +1,42 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CMDx
4
- # Thread-local chain that tracks task execution results within a correlation context.
4
+ # Manages execution chains for task results with thread-local storage support.
5
5
  #
6
- # A Chain represents a sequence of task executions that are logically related,
7
- # typically within the same request or operation flow. It provides thread-local
8
- # storage to ensure that tasks executing in the same thread share the same chain
9
- # while maintaining isolation across different threads.
10
- #
11
- # @example Basic usage with automatic chain creation
12
- # # Chain is automatically created when first task runs
13
- # result1 = MyTask.call(data: "first")
14
- # result2 = MyTask.call(data: "second")
15
- #
16
- # result1.chain.id == result2.chain.id #=> true
17
- # result1.index #=> 0
18
- # result2.index #=> 1
19
- #
20
- # @example Using custom chain ID
21
- # chain = CMDx::Chain.new(id: "custom-correlation-123")
22
- # CMDx::Chain.current = chain
23
- #
24
- # result = MyTask.call(data: "test")
25
- # result.chain.id #=> "custom-correlation-123"
26
- #
27
- # @example Thread isolation
28
- # # Each thread gets its own chain
29
- # Thread.new do
30
- # result = MyTask.call(data: "thread1")
31
- # result.chain.id #=> unique ID for this thread
32
- # end
33
- #
34
- # Thread.new do
35
- # result = MyTask.call(data: "thread2")
36
- # result.chain.id #=> different unique ID
37
- # end
38
- #
39
- # @example Temporary chain context
40
- # CMDx::Chain.use(id: "temp-correlation") do
41
- # result = MyTask.call(data: "test")
42
- # result.chain.id #=> "temp-correlation"
43
- # end
44
- # # Original chain is restored after block
45
- #
46
- # @see CMDx::Correlator
47
- # @since 1.0.0
6
+ # Chain provides a mechanism to track and correlate multiple task executions
7
+ # within a single logical operation. It maintains a collection of results
8
+ # and provides thread-local storage for tracking the current execution chain.
9
+ # The chain automatically delegates common methods to its results collection
10
+ # and the first result for convenient access to execution state.
48
11
  class Chain
49
12
 
50
- # Thread-local storage key for the current chain
51
13
  THREAD_KEY = :cmdx_correlation_chain
52
14
 
53
- __cmdx_attr_delegator :index, :first, :last, :size,
54
- to: :results
55
- __cmdx_attr_delegator :state, :status, :outcome, :runtime,
56
- to: :first
15
+ cmdx_attr_delegator :index, :first, :last, :size,
16
+ to: :results
17
+ cmdx_attr_delegator :state, :status, :outcome, :runtime,
18
+ to: :first
57
19
 
58
- # @!attribute [r] id
59
- # @return [String] the unique identifier for this chain
60
- # @!attribute [r] results
61
- # @return [Array<CMDx::Result>] the collection of task results in this chain
62
- attr_reader :id, :results
20
+ # @return [String] the unique identifier for this chain
21
+ attr_reader :id
63
22
 
64
- # Creates a new chain instance.
23
+ # @return [Array<CMDx::Result>] the collection of task results in this chain
24
+ attr_reader :results
25
+
26
+ # Creates a new execution chain with optional attributes.
27
+ #
28
+ # @param attributes [Hash] optional attributes for chain initialization
29
+ # @option attributes [String] :id custom chain identifier, defaults to current correlation ID or generates new one
65
30
  #
66
- # @param attributes [Hash] configuration options for the chain
67
- # @option attributes [String] :id custom identifier for the chain.
68
- # If not provided, uses the current correlator ID or generates a new UUID.
31
+ # @return [Chain] the newly created chain instance
69
32
  #
70
- # @example Create chain with default ID
33
+ # @example Create a chain with default ID
71
34
  # chain = CMDx::Chain.new
72
- # chain.id #=> "018c2b95-b764-7615-a924-cc5b910ed1e5"
35
+ # chain.id #=> "generated-uuid"
73
36
  #
74
- # @example Create chain with custom ID
75
- # chain = CMDx::Chain.new(id: "user-session-123")
76
- # chain.id #=> "user-session-123"
37
+ # @example Create a chain with custom ID
38
+ # chain = CMDx::Chain.new(id: "custom-123")
39
+ # chain.id #=> "custom-123"
77
40
  def initialize(attributes = {})
78
41
  @id = attributes[:id] || CMDx::Correlator.id || CMDx::Correlator.generate
79
42
  @results = []
@@ -81,53 +44,55 @@ module CMDx
81
44
 
82
45
  class << self
83
46
 
84
- # Returns the current thread-local chain.
47
+ # Gets the current execution chain from thread-local storage.
85
48
  #
86
- # @return [CMDx::Chain, nil] the chain for the current thread, or nil if none exists
49
+ # @return [Chain, nil] the current chain or nil if none is set
87
50
  #
88
- # @example
89
- # CMDx::Chain.current #=> nil (no chain set)
90
- #
91
- # MyTask.call(data: "test")
92
- # CMDx::Chain.current #=> #<CMDx::Chain:0x... @id="018c2b95...">
51
+ # @example Access current chain
52
+ # chain = CMDx::Chain.current
53
+ # chain.id if chain #=> "current-chain-id"
93
54
  def current
94
55
  Thread.current[THREAD_KEY]
95
56
  end
96
57
 
97
- # Sets the current thread-local chain.
58
+ # Sets the current execution chain in thread-local storage.
59
+ #
60
+ # @param chain [Chain, nil] the chain to set as current
98
61
  #
99
- # @param chain [CMDx::Chain, nil] the chain to set for the current thread
100
- # @return [CMDx::Chain, nil] the chain that was set
62
+ # @return [Chain, nil] the chain that was set
101
63
  #
102
- # @example
103
- # chain = CMDx::Chain.new(id: "custom-id")
104
- # CMDx::Chain.current = chain
105
- # CMDx::Chain.current.id #=> "custom-id"
64
+ # @example Set current chain
65
+ # new_chain = CMDx::Chain.new
66
+ # CMDx::Chain.current = new_chain
67
+ # CMDx::Chain.current.id #=> new_chain.id
106
68
  def current=(chain)
107
69
  Thread.current[THREAD_KEY] = chain
108
70
  end
109
71
 
110
- # Clears the current thread-local chain.
72
+ # Clears the current execution chain from thread-local storage.
111
73
  #
112
- # @return [nil]
74
+ # @return [nil] always returns nil
113
75
  #
114
- # @example
115
- # CMDx::Chain.current #=> #<CMDx::Chain:0x...>
76
+ # @example Clear current chain
116
77
  # CMDx::Chain.clear
117
- # CMDx::Chain.current #=> nil
78
+ # CMDx::Chain.current #=> nil
118
79
  def clear
119
80
  Thread.current[THREAD_KEY] = nil
120
81
  end
121
82
 
122
- # Adds a result to the current chain, creating a new chain if none exists.
83
+ # Builds or extends the current execution chain with a new result.
123
84
  #
124
- # This method is typically called internally by the task execution framework
125
- # and should not be used directly in application code.
85
+ # @param result [CMDx::Result] the result to add to the chain
126
86
  #
127
- # @param result [CMDx::Result] the task result to add to the chain
128
- # @return [CMDx::Chain] the chain containing the result
87
+ # @return [Chain] the current chain with the result added
129
88
  #
130
- # @api private
89
+ # @raise [TypeError] if result is not a Result instance
90
+ #
91
+ # @example Build chain with result
92
+ # task = MyTask.new
93
+ # result = CMDx::Result.new(task)
94
+ # chain = CMDx::Chain.build(result)
95
+ # chain.results.size #=> 1
131
96
  def build(result)
132
97
  raise TypeError, "must be a Result" unless result.is_a?(Result)
133
98
 
@@ -138,50 +103,28 @@ module CMDx
138
103
 
139
104
  end
140
105
 
141
- # Converts the chain to a hash representation.
142
- #
143
- # Serializes the chain and all its results into a structured hash
144
- # suitable for logging, debugging, and data interchange.
106
+ # Converts the chain to a hash representation using the serializer.
145
107
  #
146
- # @return [Hash] Structured hash representation of the chain
108
+ # @return [Hash] serialized hash representation of the chain
147
109
  #
148
- # @example
149
- # chain.to_h
150
- # # => {
151
- # # id: "018c2b95-b764-7615-a924-cc5b910ed1e5",
152
- # # state: "complete",
153
- # # status: "success",
154
- # # outcome: "success",
155
- # # runtime: 0.5,
156
- # # results: [
157
- # # { class: "ProcessOrderTask", state: "complete", status: "success", ... },
158
- # # { class: "SendEmailTask", state: "complete", status: "success", ... }
159
- # # ]
160
- # # }
110
+ # @example Convert to hash
111
+ # chain.to_h #=> { id: "abc123", results: [...], state: "complete" }
161
112
  def to_h
162
113
  ChainSerializer.call(self)
163
114
  end
164
115
  alias to_a to_h
165
116
 
166
- # Converts the chain to a string representation for inspection.
167
- #
168
- # Creates a comprehensive, human-readable summary of the chain including
169
- # all task results with formatted headers and footers.
117
+ # Converts the chain to a formatted string representation.
170
118
  #
171
- # @return [String] Formatted chain summary with task details
119
+ # @return [String] formatted string representation of the chain
172
120
  #
173
- # @example
174
- # chain.to_s
175
- # # => "
176
- # # chain: 018c2b95-b764-7615-a924-cc5b910ed1e5
177
- # # ================================================
178
- # #
179
- # # ProcessOrderTask: index=0 state=complete status=success ...
180
- # # SendEmailTask: index=1 state=complete status=success ...
181
- # #
182
- # # ================================================
183
- # # state: complete | status: success | outcome: success | runtime: 0.5
184
- # # "
121
+ # @example Convert to string
122
+ # puts chain.to_s
123
+ # # chain: abc123
124
+ # # ===================
125
+ # # {...}
126
+ # # ===================
127
+ # # state: complete | status: success | outcome: success | runtime: 0.001
185
128
  def to_s
186
129
  ChainInspector.call(self)
187
130
  end