cmdx 1.0.1 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.cursor/prompts/docs.md +9 -0
- data/.cursor/prompts/rspec.md +21 -0
- data/.cursor/prompts/yardoc.md +13 -0
- data/.rubocop.yml +2 -0
- data/CHANGELOG.md +29 -3
- data/README.md +2 -1
- data/docs/ai_prompts.md +269 -195
- data/docs/basics/call.md +126 -60
- data/docs/basics/chain.md +190 -160
- data/docs/basics/context.md +242 -154
- data/docs/basics/setup.md +302 -32
- data/docs/callbacks.md +382 -119
- data/docs/configuration.md +211 -49
- data/docs/deprecation.md +245 -0
- data/docs/getting_started.md +161 -39
- data/docs/internationalization.md +590 -70
- data/docs/interruptions/exceptions.md +135 -118
- data/docs/interruptions/faults.md +152 -127
- data/docs/interruptions/halt.md +134 -80
- data/docs/logging.md +183 -120
- data/docs/middlewares.md +165 -392
- data/docs/outcomes/result.md +140 -112
- data/docs/outcomes/states.md +134 -99
- data/docs/outcomes/statuses.md +204 -146
- data/docs/parameters/coercions.md +251 -289
- data/docs/parameters/defaults.md +224 -169
- data/docs/parameters/definitions.md +289 -141
- data/docs/parameters/namespacing.md +250 -161
- data/docs/parameters/validations.md +247 -159
- data/docs/testing.md +196 -203
- data/docs/workflows.md +146 -101
- data/lib/cmdx/.DS_Store +0 -0
- data/lib/cmdx/callback.rb +39 -55
- data/lib/cmdx/callback_registry.rb +80 -73
- data/lib/cmdx/chain.rb +65 -122
- data/lib/cmdx/chain_inspector.rb +23 -116
- data/lib/cmdx/chain_serializer.rb +34 -146
- data/lib/cmdx/coercion.rb +57 -0
- data/lib/cmdx/coercion_registry.rb +113 -0
- data/lib/cmdx/coercions/array.rb +18 -36
- data/lib/cmdx/coercions/big_decimal.rb +21 -33
- data/lib/cmdx/coercions/boolean.rb +21 -40
- data/lib/cmdx/coercions/complex.rb +18 -31
- data/lib/cmdx/coercions/date.rb +20 -39
- data/lib/cmdx/coercions/date_time.rb +22 -39
- data/lib/cmdx/coercions/float.rb +19 -32
- data/lib/cmdx/coercions/hash.rb +22 -41
- data/lib/cmdx/coercions/integer.rb +20 -33
- data/lib/cmdx/coercions/rational.rb +20 -32
- data/lib/cmdx/coercions/string.rb +23 -31
- data/lib/cmdx/coercions/time.rb +24 -40
- data/lib/cmdx/coercions/virtual.rb +14 -31
- data/lib/cmdx/configuration.rb +101 -162
- data/lib/cmdx/context.rb +34 -166
- data/lib/cmdx/core_ext/hash.rb +42 -67
- data/lib/cmdx/core_ext/module.rb +35 -79
- data/lib/cmdx/core_ext/object.rb +63 -98
- data/lib/cmdx/correlator.rb +59 -154
- data/lib/cmdx/error.rb +37 -202
- data/lib/cmdx/errors.rb +153 -216
- data/lib/cmdx/fault.rb +68 -150
- data/lib/cmdx/faults.rb +26 -137
- data/lib/cmdx/immutator.rb +22 -110
- data/lib/cmdx/lazy_struct.rb +110 -186
- data/lib/cmdx/log_formatters/json.rb +14 -40
- data/lib/cmdx/log_formatters/key_value.rb +14 -40
- data/lib/cmdx/log_formatters/line.rb +14 -48
- data/lib/cmdx/log_formatters/logstash.rb +14 -57
- data/lib/cmdx/log_formatters/pretty_json.rb +14 -50
- data/lib/cmdx/log_formatters/pretty_key_value.rb +13 -46
- data/lib/cmdx/log_formatters/pretty_line.rb +16 -54
- data/lib/cmdx/log_formatters/raw.rb +19 -49
- data/lib/cmdx/logger.rb +22 -79
- data/lib/cmdx/logger_ansi.rb +31 -72
- data/lib/cmdx/logger_serializer.rb +74 -103
- data/lib/cmdx/middleware.rb +56 -60
- data/lib/cmdx/middleware_registry.rb +82 -77
- data/lib/cmdx/middlewares/correlate.rb +41 -226
- data/lib/cmdx/middlewares/timeout.rb +46 -185
- data/lib/cmdx/parameter.rb +167 -183
- data/lib/cmdx/parameter_evaluator.rb +231 -0
- data/lib/cmdx/parameter_inspector.rb +37 -55
- data/lib/cmdx/parameter_registry.rb +65 -84
- data/lib/cmdx/parameter_serializer.rb +32 -76
- data/lib/cmdx/railtie.rb +24 -107
- data/lib/cmdx/result.rb +254 -259
- data/lib/cmdx/result_ansi.rb +28 -80
- data/lib/cmdx/result_inspector.rb +34 -70
- data/lib/cmdx/result_logger.rb +23 -77
- data/lib/cmdx/result_serializer.rb +59 -125
- data/lib/cmdx/rspec/matchers.rb +28 -0
- data/lib/cmdx/rspec/result_matchers/be_executed.rb +42 -0
- data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +94 -0
- data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +94 -0
- data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +59 -0
- data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +57 -0
- data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +87 -0
- data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +51 -0
- data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +58 -0
- data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +59 -0
- data/lib/cmdx/rspec/result_matchers/have_context.rb +86 -0
- data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +54 -0
- data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +52 -0
- data/lib/cmdx/rspec/result_matchers/have_metadata.rb +114 -0
- data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +66 -0
- data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +64 -0
- data/lib/cmdx/rspec/result_matchers/have_runtime.rb +78 -0
- data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +76 -0
- data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +62 -0
- data/lib/cmdx/rspec/task_matchers/have_callback.rb +85 -0
- data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +68 -0
- data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +92 -0
- data/lib/cmdx/rspec/task_matchers/have_middleware.rb +46 -0
- data/lib/cmdx/rspec/task_matchers/have_parameter.rb +181 -0
- data/lib/cmdx/task.rb +336 -427
- data/lib/cmdx/task_deprecator.rb +52 -0
- data/lib/cmdx/task_processor.rb +246 -0
- data/lib/cmdx/task_serializer.rb +34 -69
- data/lib/cmdx/utils/ansi_color.rb +13 -89
- data/lib/cmdx/utils/log_timestamp.rb +13 -42
- data/lib/cmdx/utils/monotonic_runtime.rb +11 -63
- data/lib/cmdx/utils/name_affix.rb +21 -71
- data/lib/cmdx/validator.rb +57 -0
- data/lib/cmdx/validator_registry.rb +108 -0
- data/lib/cmdx/validators/exclusion.rb +55 -94
- data/lib/cmdx/validators/format.rb +31 -85
- data/lib/cmdx/validators/inclusion.rb +65 -110
- data/lib/cmdx/validators/length.rb +117 -133
- data/lib/cmdx/validators/numeric.rb +123 -130
- data/lib/cmdx/validators/presence.rb +38 -79
- data/lib/cmdx/version.rb +1 -7
- data/lib/cmdx/workflow.rb +58 -330
- data/lib/cmdx.rb +1 -1
- data/lib/generators/cmdx/install_generator.rb +14 -31
- data/lib/generators/cmdx/task_generator.rb +39 -55
- data/lib/generators/cmdx/templates/install.rb +24 -6
- data/lib/generators/cmdx/workflow_generator.rb +41 -66
- data/lib/locales/ar.yml +0 -1
- data/lib/locales/cs.yml +0 -1
- data/lib/locales/da.yml +0 -1
- data/lib/locales/de.yml +0 -1
- data/lib/locales/el.yml +0 -1
- data/lib/locales/en.yml +0 -1
- data/lib/locales/es.yml +0 -1
- data/lib/locales/fi.yml +0 -1
- data/lib/locales/fr.yml +0 -1
- data/lib/locales/he.yml +0 -1
- data/lib/locales/hi.yml +0 -1
- data/lib/locales/it.yml +0 -1
- data/lib/locales/ja.yml +0 -1
- data/lib/locales/ko.yml +0 -1
- data/lib/locales/nl.yml +0 -1
- data/lib/locales/no.yml +0 -1
- data/lib/locales/pl.yml +0 -1
- data/lib/locales/pt.yml +0 -1
- data/lib/locales/ru.yml +0 -1
- data/lib/locales/sv.yml +0 -1
- data/lib/locales/th.yml +0 -1
- data/lib/locales/tr.yml +0 -1
- data/lib/locales/vi.yml +0 -1
- data/lib/locales/zh.yml +0 -1
- metadata +36 -8
- data/lib/cmdx/parameter_validator.rb +0 -81
- data/lib/cmdx/parameter_value.rb +0 -244
- data/lib/cmdx/parameters_inspector.rb +0 -72
- data/lib/cmdx/parameters_serializer.rb +0 -115
- data/lib/cmdx/rspec/result_matchers.rb +0 -917
- data/lib/cmdx/rspec/task_matchers.rb +0 -570
- data/lib/cmdx/validators/custom.rb +0 -102
@@ -1,106 +1,113 @@
|
|
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
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
23
|
+
# @return [Hash] hash containing callback type keys and callback definition arrays
|
24
|
+
attr_reader :registry
|
25
|
+
|
26
|
+
# Initializes a new callback registry.
|
35
27
|
#
|
36
|
-
# @param registry [
|
28
|
+
# @param registry [Hash] initial registry hash with callback definitions
|
37
29
|
#
|
38
|
-
# @
|
39
|
-
# registry = CallbackRegistry.new
|
30
|
+
# @return [CallbackRegistry] a new callback registry instance
|
40
31
|
#
|
41
|
-
# @example
|
42
|
-
#
|
43
|
-
#
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
registry
|
48
|
-
self[callback_type] = callback_definitions.dup
|
49
|
-
end
|
32
|
+
# @example Creating an empty registry
|
33
|
+
# CallbackRegistry.new
|
34
|
+
#
|
35
|
+
# @example Creating a registry with initial callbacks
|
36
|
+
# CallbackRegistry.new(before_execution: [[:my_callback, {}]])
|
37
|
+
def initialize(registry = {})
|
38
|
+
@registry = registry.to_h
|
50
39
|
end
|
51
40
|
|
52
|
-
# Registers
|
41
|
+
# Registers one or more callbacks for a specific type.
|
53
42
|
#
|
54
|
-
# @param
|
55
|
-
# @param callables [Array<
|
56
|
-
# @param options [Hash]
|
57
|
-
# @
|
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
|
43
|
+
# @param type [Symbol] the callback type to register
|
44
|
+
# @param callables [Array<Object>] callable objects to register
|
45
|
+
# @param options [Hash] options for conditional callback execution
|
46
|
+
# @param block [Proc] optional block to register as a callback
|
61
47
|
#
|
62
|
-
# @
|
63
|
-
# registry.register(:before_validation, :check_permissions)
|
48
|
+
# @return [CallbackRegistry] returns self for method chaining
|
64
49
|
#
|
65
|
-
# @example
|
66
|
-
# registry.register(:
|
50
|
+
# @example Registering a symbol callback
|
51
|
+
# registry.register(:before_execution, :setup_database)
|
67
52
|
#
|
68
|
-
# @example
|
69
|
-
# registry.register(:
|
70
|
-
|
53
|
+
# @example Registering a Proc callback
|
54
|
+
# registry.register(:on_good, ->(task) { puts "Task completed: #{task.name}" })
|
55
|
+
#
|
56
|
+
# @example Registering a Callback class
|
57
|
+
# registry.register(:after_validation, NotificationCallback)
|
58
|
+
#
|
59
|
+
# @example Registering multiple callbacks with options
|
60
|
+
# registry.register(:on_good, :send_notification, :log_success, if: -> { Rails.env.production? })
|
61
|
+
#
|
62
|
+
# @example Registering a block callback
|
63
|
+
# registry.register(:after_validation) { |task| puts "Validation complete" }
|
64
|
+
def register(type, *callables, **options, &block)
|
71
65
|
callables << block if block_given?
|
72
|
-
(
|
66
|
+
(registry[type] ||= []).push([callables, options]).uniq!
|
73
67
|
self
|
74
68
|
end
|
75
69
|
|
76
|
-
# Executes all callbacks
|
77
|
-
#
|
70
|
+
# Executes all registered callbacks for a specific type.
|
71
|
+
#
|
72
|
+
# @param task [Task] the task instance to execute callbacks on
|
73
|
+
# @param type [Symbol] the callback type to execute
|
78
74
|
#
|
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
75
|
# @return [void]
|
82
76
|
#
|
83
|
-
# @
|
77
|
+
# @raise [UnknownCallbackError] when the callback type is not recognized
|
78
|
+
#
|
79
|
+
# @example Executing before_validation callbacks
|
84
80
|
# registry.call(task, :before_validation)
|
85
81
|
#
|
86
|
-
# @example
|
87
|
-
#
|
88
|
-
|
89
|
-
|
90
|
-
return unless key?(callback)
|
82
|
+
# @example Executing outcome callbacks
|
83
|
+
# registry.call(task, :on_good)
|
84
|
+
def call(task, type)
|
85
|
+
raise UnknownCallbackError, "unknown callback #{type}" unless TYPES.include?(type)
|
91
86
|
|
92
|
-
Array(
|
93
|
-
next unless task.
|
87
|
+
Array(registry[type]).each do |callables, options|
|
88
|
+
next unless task.cmdx_eval(options)
|
94
89
|
|
95
|
-
Array(callables).each do |
|
96
|
-
|
97
|
-
|
90
|
+
Array(callables).each do |callable|
|
91
|
+
case callable
|
92
|
+
when Symbol, String, Proc
|
93
|
+
task.cmdx_try(callable)
|
98
94
|
else
|
99
|
-
|
95
|
+
callable.call(task)
|
100
96
|
end
|
101
97
|
end
|
102
98
|
end
|
103
99
|
end
|
104
100
|
|
101
|
+
# Returns a hash representation of the registry.
|
102
|
+
#
|
103
|
+
# @return [Hash] a deep copy of the registry hash
|
104
|
+
#
|
105
|
+
# @example Getting registry contents
|
106
|
+
# registry.to_h
|
107
|
+
# #=> { before_execution: [[:setup, {}]], on_good: [[:notify, { if: -> { true } }]] }
|
108
|
+
def to_h
|
109
|
+
registry.transform_values(&:dup)
|
110
|
+
end
|
111
|
+
|
105
112
|
end
|
106
113
|
end
|
data/lib/cmdx/chain.rb
CHANGED
@@ -1,79 +1,42 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module CMDx
|
4
|
-
#
|
4
|
+
# Manages execution chains for task results with thread-local storage support.
|
5
5
|
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
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
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
-
#
|
59
|
-
|
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
|
-
#
|
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
|
-
# @
|
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
|
35
|
+
# chain.id #=> "generated-uuid"
|
73
36
|
#
|
74
|
-
# @example Create chain with custom ID
|
75
|
-
# chain = CMDx::Chain.new(id: "
|
76
|
-
# chain.id
|
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
|
-
#
|
47
|
+
# Gets the current execution chain from thread-local storage.
|
85
48
|
#
|
86
|
-
# @return [
|
49
|
+
# @return [Chain, nil] the current chain or nil if none is set
|
87
50
|
#
|
88
|
-
# @example
|
89
|
-
# CMDx::Chain.current
|
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
|
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
|
-
# @
|
100
|
-
# @return [CMDx::Chain, nil] the chain that was set
|
62
|
+
# @return [Chain, nil] the chain that was set
|
101
63
|
#
|
102
|
-
# @example
|
103
|
-
#
|
104
|
-
# CMDx::Chain.current =
|
105
|
-
# CMDx::Chain.current.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
|
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
|
78
|
+
# CMDx::Chain.current #=> nil
|
118
79
|
def clear
|
119
80
|
Thread.current[THREAD_KEY] = nil
|
120
81
|
end
|
121
82
|
|
122
|
-
#
|
83
|
+
# Builds or extends the current execution chain with a new result.
|
123
84
|
#
|
124
|
-
#
|
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
|
-
# @
|
128
|
-
# @return [CMDx::Chain] the chain containing the result
|
87
|
+
# @return [Chain] the current chain with the result added
|
129
88
|
#
|
130
|
-
# @
|
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]
|
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
|
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]
|
119
|
+
# @return [String] formatted string representation of the chain
|
172
120
|
#
|
173
|
-
# @example
|
174
|
-
# chain.to_s
|
175
|
-
# #
|
176
|
-
# #
|
177
|
-
# #
|
178
|
-
# #
|
179
|
-
# #
|
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
|
data/lib/cmdx/chain_inspector.rb
CHANGED
@@ -1,137 +1,44 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module CMDx
|
4
|
-
#
|
4
|
+
# Provides formatted inspection and display functionality for chain execution results.
|
5
5
|
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# @example Basic chain inspection
|
12
|
-
# result = ProcessOrderTask.call(order_id: 123)
|
13
|
-
# chain = result.chain
|
14
|
-
#
|
15
|
-
# ChainInspector.call(chain)
|
16
|
-
# # => "
|
17
|
-
# # chain: 018c2b95-b764-7615-a924-cc5b910ed1e5
|
18
|
-
# # ================================================
|
19
|
-
# #
|
20
|
-
# # {
|
21
|
-
# # class: "ProcessOrderTask",
|
22
|
-
# # type: "Task",
|
23
|
-
# # index: 0,
|
24
|
-
# # id: "018c2b95-b764-7615-a924-cc5b910ed1e5",
|
25
|
-
# # tags: [],
|
26
|
-
# # state: "complete",
|
27
|
-
# # status: "success",
|
28
|
-
# # outcome: "success",
|
29
|
-
# # metadata: {},
|
30
|
-
# # runtime: 0.5
|
31
|
-
# # }
|
32
|
-
# #
|
33
|
-
# # ================================================
|
34
|
-
# # state: complete | status: success | outcome: success | runtime: 0.5
|
35
|
-
# # "
|
36
|
-
#
|
37
|
-
# @example Chain with multiple tasks
|
38
|
-
# class ComplexTask < CMDx::Task
|
39
|
-
# def call
|
40
|
-
# SubTask1.call(context)
|
41
|
-
# SubTask2.call(context)
|
42
|
-
# end
|
43
|
-
# end
|
44
|
-
#
|
45
|
-
# result = ComplexTask.call
|
46
|
-
# ChainInspector.call(result.chain)
|
47
|
-
# # => Shows formatted output with all three task results and summary
|
48
|
-
#
|
49
|
-
# @example Failed chain inspection
|
50
|
-
# # When a chain contains failed tasks, the summary reflects the failure state
|
51
|
-
# ChainInspector.call(failed_chain)
|
52
|
-
# # => Shows all task results with failure information and failed summary
|
53
|
-
#
|
54
|
-
# @see CMDx::Chain Chain execution context and result tracking
|
55
|
-
# @see CMDx::Result Individual result inspection via to_h
|
6
|
+
# ChainInspector creates human-readable string representations of execution chains,
|
7
|
+
# displaying chain metadata, individual task results, and execution summary information
|
8
|
+
# in a formatted layout. The inspector processes chain data to provide comprehensive
|
9
|
+
# debugging and monitoring output for task execution sequences.
|
56
10
|
module ChainInspector
|
57
11
|
|
58
|
-
# Keys to display in the chain summary footer.
|
59
|
-
#
|
60
|
-
# These keys represent the most important chain-level information
|
61
|
-
# that should be displayed in the summary footer for quick reference.
|
62
12
|
FOOTER_KEYS = %i[
|
63
13
|
state status outcome runtime
|
64
14
|
].freeze
|
65
15
|
|
66
16
|
module_function
|
67
17
|
|
68
|
-
#
|
18
|
+
# Formats a chain into a human-readable inspection string with headers, results, and summary.
|
69
19
|
#
|
70
|
-
# Creates a
|
71
|
-
#
|
72
|
-
#
|
73
|
-
#
|
74
|
-
# - Visual separator line
|
75
|
-
# - Summary footer with key chain statistics
|
20
|
+
# Creates a comprehensive string representation of the execution chain including
|
21
|
+
# a header with the chain ID, formatted individual task results, and a footer
|
22
|
+
# summary with key execution metadata. The output uses visual separators for
|
23
|
+
# clear section delineation and consistent formatting.
|
76
24
|
#
|
77
|
-
# @param chain [
|
78
|
-
# @return [String] Formatted chain summary with task details and statistics
|
25
|
+
# @param chain [Chain] the execution chain to format and inspect
|
79
26
|
#
|
80
|
-
# @
|
81
|
-
# chain = SimpleTask.call.chain
|
82
|
-
# ChainInspector.call(chain)
|
83
|
-
# # => "
|
84
|
-
# # chain: 018c2b95-b764-7615-a924-cc5b910ed1e5
|
85
|
-
# # ================================================
|
86
|
-
# #
|
87
|
-
# # {
|
88
|
-
# # class: "SimpleTask",
|
89
|
-
# # type: "Task",
|
90
|
-
# # index: 0,
|
91
|
-
# # state: "complete",
|
92
|
-
# # status: "success",
|
93
|
-
# # outcome: "success",
|
94
|
-
# # runtime: 0.1
|
95
|
-
# # }
|
96
|
-
# #
|
97
|
-
# # ================================================
|
98
|
-
# # state: complete | status: success | outcome: success | runtime: 0.1
|
99
|
-
# # "
|
100
|
-
#
|
101
|
-
# @example Multi-task chain
|
102
|
-
# class ParentTask < CMDx::Task
|
103
|
-
# def call
|
104
|
-
# ChildTask1.call(context)
|
105
|
-
# ChildTask2.call(context)
|
106
|
-
# end
|
107
|
-
# end
|
108
|
-
#
|
109
|
-
# chain = ParentTask.call.chain
|
110
|
-
# ChainInspector.call(chain)
|
111
|
-
# # => "
|
112
|
-
# # chain: 018c2b95-b764-7615-a924-cc5b910ed1e5
|
113
|
-
# # ================================================
|
114
|
-
# #
|
115
|
-
# # { class: "ParentTask", index: 0, state: "complete", status: "success", ... }
|
116
|
-
# # { class: "ChildTask1", index: 1, state: "complete", status: "success", ... }
|
117
|
-
# # { class: "ChildTask2", index: 2, state: "complete", status: "success", ... }
|
118
|
-
# #
|
119
|
-
# # ================================================
|
120
|
-
# # state: complete | status: success | outcome: success | runtime: 0.5
|
121
|
-
# # "
|
27
|
+
# @return [String] formatted multi-line string representation of the chain execution
|
122
28
|
#
|
123
|
-
# @example
|
124
|
-
#
|
125
|
-
# ChainInspector.call(
|
126
|
-
#
|
127
|
-
# #
|
128
|
-
# #
|
29
|
+
# @example Format a simple chain
|
30
|
+
# chain = MyWorkflow.call(user_id: 123)
|
31
|
+
# output = ChainInspector.call(chain.chain)
|
32
|
+
# puts output
|
33
|
+
# # =>
|
34
|
+
# # chain: abc123-def456-789
|
35
|
+
# # ===============================
|
129
36
|
# #
|
130
|
-
# #
|
37
|
+
# # {:task=>"MyTask", :state=>"complete", :status=>"success"}
|
38
|
+
# # {:task=>"OtherTask", :state=>"complete", :status=>"success"}
|
131
39
|
# #
|
132
|
-
# #
|
133
|
-
# #
|
134
|
-
# # "
|
40
|
+
# # ===============================
|
41
|
+
# # state: complete | status: success | outcome: good | runtime: 0.025
|
135
42
|
def call(chain)
|
136
43
|
header = "\nchain: #{chain.id}"
|
137
44
|
footer = FOOTER_KEYS.map { |key| "#{key}: #{chain.send(key)}" }.join(" | ")
|