cmdx 1.1.2 → 1.5.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/.DS_Store +0 -0
- data/.cursor/prompts/docs.md +4 -1
- data/.cursor/prompts/llms.md +20 -0
- data/.cursor/prompts/rspec.md +4 -1
- data/.cursor/prompts/yardoc.md +3 -2
- data/.cursor/rules/cursor-instructions.mdc +55 -1
- data/.irbrc +6 -0
- data/.rubocop.yml +29 -18
- data/CHANGELOG.md +11 -132
- data/LLM.md +3317 -0
- data/README.md +68 -44
- data/docs/attributes/coercions.md +162 -0
- data/docs/attributes/defaults.md +90 -0
- data/docs/attributes/definitions.md +281 -0
- data/docs/attributes/naming.md +78 -0
- data/docs/attributes/validations.md +309 -0
- data/docs/basics/chain.md +56 -249
- data/docs/basics/context.md +56 -289
- data/docs/basics/execution.md +114 -0
- data/docs/basics/setup.md +37 -334
- data/docs/callbacks.md +89 -467
- data/docs/deprecation.md +91 -174
- data/docs/getting_started.md +212 -202
- data/docs/internationalization.md +11 -647
- data/docs/interruptions/exceptions.md +23 -198
- data/docs/interruptions/faults.md +71 -151
- data/docs/interruptions/halt.md +109 -186
- data/docs/logging.md +44 -256
- data/docs/middlewares.md +113 -426
- data/docs/outcomes/result.md +81 -228
- data/docs/outcomes/states.md +33 -221
- data/docs/outcomes/statuses.md +21 -311
- data/docs/tips_and_tricks.md +120 -70
- data/docs/workflows.md +99 -283
- data/lib/cmdx/.DS_Store +0 -0
- data/lib/cmdx/attribute.rb +229 -0
- data/lib/cmdx/attribute_registry.rb +94 -0
- data/lib/cmdx/attribute_value.rb +193 -0
- data/lib/cmdx/callback_registry.rb +69 -77
- data/lib/cmdx/chain.rb +56 -73
- data/lib/cmdx/coercion_registry.rb +52 -68
- data/lib/cmdx/coercions/array.rb +19 -18
- data/lib/cmdx/coercions/big_decimal.rb +20 -24
- data/lib/cmdx/coercions/boolean.rb +26 -25
- data/lib/cmdx/coercions/complex.rb +21 -22
- data/lib/cmdx/coercions/date.rb +25 -23
- data/lib/cmdx/coercions/date_time.rb +24 -25
- data/lib/cmdx/coercions/float.rb +25 -22
- data/lib/cmdx/coercions/hash.rb +31 -32
- data/lib/cmdx/coercions/integer.rb +30 -24
- data/lib/cmdx/coercions/rational.rb +29 -24
- data/lib/cmdx/coercions/string.rb +19 -22
- data/lib/cmdx/coercions/symbol.rb +37 -0
- data/lib/cmdx/coercions/time.rb +26 -25
- data/lib/cmdx/configuration.rb +49 -108
- data/lib/cmdx/context.rb +222 -44
- data/lib/cmdx/deprecator.rb +61 -0
- data/lib/cmdx/errors.rb +42 -252
- data/lib/cmdx/exceptions.rb +39 -0
- data/lib/cmdx/faults.rb +78 -39
- data/lib/cmdx/freezer.rb +51 -0
- data/lib/cmdx/identifier.rb +30 -0
- data/lib/cmdx/locale.rb +52 -0
- data/lib/cmdx/log_formatters/json.rb +21 -22
- data/lib/cmdx/log_formatters/key_value.rb +20 -22
- data/lib/cmdx/log_formatters/line.rb +15 -22
- data/lib/cmdx/log_formatters/logstash.rb +22 -23
- data/lib/cmdx/log_formatters/raw.rb +16 -22
- data/lib/cmdx/middleware_registry.rb +70 -74
- data/lib/cmdx/middlewares/correlate.rb +90 -54
- data/lib/cmdx/middlewares/runtime.rb +58 -0
- data/lib/cmdx/middlewares/timeout.rb +48 -68
- data/lib/cmdx/railtie.rb +12 -45
- data/lib/cmdx/result.rb +229 -314
- data/lib/cmdx/task.rb +194 -366
- data/lib/cmdx/utils/call.rb +49 -0
- data/lib/cmdx/utils/condition.rb +71 -0
- data/lib/cmdx/utils/format.rb +61 -0
- data/lib/cmdx/validator_registry.rb +63 -72
- data/lib/cmdx/validators/exclusion.rb +38 -67
- data/lib/cmdx/validators/format.rb +48 -49
- data/lib/cmdx/validators/inclusion.rb +43 -74
- data/lib/cmdx/validators/length.rb +101 -162
- data/lib/cmdx/validators/numeric.rb +95 -170
- data/lib/cmdx/validators/presence.rb +37 -50
- data/lib/cmdx/version.rb +1 -1
- data/lib/cmdx/worker.rb +178 -0
- data/lib/cmdx/workflow.rb +85 -81
- data/lib/cmdx.rb +19 -13
- data/lib/generators/cmdx/install_generator.rb +14 -13
- data/lib/generators/cmdx/task_generator.rb +25 -50
- data/lib/generators/cmdx/templates/install.rb +11 -46
- data/lib/generators/cmdx/templates/task.rb.tt +3 -2
- data/lib/locales/en.yml +18 -4
- data/src/cmdx-logo.png +0 -0
- metadata +32 -116
- data/docs/ai_prompts.md +0 -393
- data/docs/basics/call.md +0 -317
- data/docs/configuration.md +0 -344
- data/docs/parameters/coercions.md +0 -396
- data/docs/parameters/defaults.md +0 -335
- data/docs/parameters/definitions.md +0 -446
- data/docs/parameters/namespacing.md +0 -378
- data/docs/parameters/validations.md +0 -405
- data/docs/testing.md +0 -553
- data/lib/cmdx/callback.rb +0 -53
- data/lib/cmdx/chain_inspector.rb +0 -56
- data/lib/cmdx/chain_serializer.rb +0 -63
- data/lib/cmdx/coercion.rb +0 -57
- data/lib/cmdx/coercions/virtual.rb +0 -29
- data/lib/cmdx/core_ext/hash.rb +0 -83
- data/lib/cmdx/core_ext/module.rb +0 -98
- data/lib/cmdx/core_ext/object.rb +0 -125
- data/lib/cmdx/correlator.rb +0 -122
- data/lib/cmdx/error.rb +0 -67
- data/lib/cmdx/fault.rb +0 -140
- data/lib/cmdx/immutator.rb +0 -52
- data/lib/cmdx/lazy_struct.rb +0 -246
- data/lib/cmdx/log_formatters/pretty_json.rb +0 -40
- data/lib/cmdx/log_formatters/pretty_key_value.rb +0 -38
- data/lib/cmdx/log_formatters/pretty_line.rb +0 -41
- data/lib/cmdx/logger.rb +0 -49
- data/lib/cmdx/logger_ansi.rb +0 -68
- data/lib/cmdx/logger_serializer.rb +0 -116
- data/lib/cmdx/middleware.rb +0 -70
- data/lib/cmdx/parameter.rb +0 -312
- data/lib/cmdx/parameter_evaluator.rb +0 -231
- data/lib/cmdx/parameter_inspector.rb +0 -66
- data/lib/cmdx/parameter_registry.rb +0 -106
- data/lib/cmdx/parameter_serializer.rb +0 -59
- data/lib/cmdx/result_ansi.rb +0 -71
- data/lib/cmdx/result_inspector.rb +0 -71
- data/lib/cmdx/result_logger.rb +0 -59
- data/lib/cmdx/result_serializer.rb +0 -104
- data/lib/cmdx/rspec/matchers.rb +0 -28
- data/lib/cmdx/rspec/result_matchers/be_executed.rb +0 -42
- data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +0 -94
- data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +0 -94
- data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +0 -59
- data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +0 -57
- data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +0 -87
- data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +0 -51
- data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +0 -58
- data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +0 -59
- data/lib/cmdx/rspec/result_matchers/have_context.rb +0 -86
- data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +0 -54
- data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +0 -52
- data/lib/cmdx/rspec/result_matchers/have_metadata.rb +0 -114
- data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +0 -66
- data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +0 -64
- data/lib/cmdx/rspec/result_matchers/have_runtime.rb +0 -78
- data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +0 -76
- data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +0 -62
- data/lib/cmdx/rspec/task_matchers/have_callback.rb +0 -85
- data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +0 -68
- data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +0 -92
- data/lib/cmdx/rspec/task_matchers/have_middleware.rb +0 -46
- data/lib/cmdx/rspec/task_matchers/have_parameter.rb +0 -181
- data/lib/cmdx/task_deprecator.rb +0 -58
- data/lib/cmdx/task_processor.rb +0 -246
- data/lib/cmdx/task_serializer.rb +0 -57
- data/lib/cmdx/utils/ansi_color.rb +0 -73
- data/lib/cmdx/utils/log_timestamp.rb +0 -36
- data/lib/cmdx/utils/monotonic_runtime.rb +0 -34
- data/lib/cmdx/utils/name_affix.rb +0 -52
- data/lib/cmdx/validator.rb +0 -57
- data/lib/generators/cmdx/templates/workflow.rb.tt +0 -7
- data/lib/generators/cmdx/workflow_generator.rb +0 -84
- data/lib/locales/ar.yml +0 -35
- data/lib/locales/cs.yml +0 -35
- data/lib/locales/da.yml +0 -35
- data/lib/locales/de.yml +0 -35
- data/lib/locales/el.yml +0 -35
- data/lib/locales/es.yml +0 -35
- data/lib/locales/fi.yml +0 -35
- data/lib/locales/fr.yml +0 -35
- data/lib/locales/he.yml +0 -35
- data/lib/locales/hi.yml +0 -35
- data/lib/locales/it.yml +0 -35
- data/lib/locales/ja.yml +0 -35
- data/lib/locales/ko.yml +0 -35
- data/lib/locales/nl.yml +0 -35
- data/lib/locales/no.yml +0 -35
- data/lib/locales/pl.yml +0 -35
- data/lib/locales/pt.yml +0 -35
- data/lib/locales/ru.yml +0 -35
- data/lib/locales/sv.yml +0 -35
- data/lib/locales/th.yml +0 -35
- data/lib/locales/tr.yml +0 -35
- data/lib/locales/vi.yml +0 -35
- data/lib/locales/zh.yml +0 -35
data/docs/basics/chain.md
CHANGED
@@ -4,123 +4,106 @@ Chains automatically group related task executions within a thread, providing un
|
|
4
4
|
|
5
5
|
## Table of Contents
|
6
6
|
|
7
|
-
- [
|
8
|
-
- [
|
9
|
-
- [
|
10
|
-
- [
|
11
|
-
- [Chain Structure and Metadata](#chain-structure-and-metadata)
|
12
|
-
- [Correlation ID Integration](#correlation-id-integration)
|
13
|
-
- [State Delegation](#state-delegation)
|
14
|
-
- [Serialization and Logging](#serialization-and-logging)
|
15
|
-
- [Error Handling](#error-handling)
|
7
|
+
- [Management](#management)
|
8
|
+
- [Links](#links)
|
9
|
+
- [Inheritance](#inheritance)
|
10
|
+
- [Structure](#structure)
|
16
11
|
|
17
|
-
##
|
12
|
+
## Management
|
18
13
|
|
19
|
-
|
20
|
-
# Automatic chain creation per thread
|
21
|
-
result = ProcessOrderTask.call(order_id: 123)
|
22
|
-
result.chain.id # Unique chain ID
|
23
|
-
result.chain.results.size # All tasks in this chain
|
24
|
-
|
25
|
-
# Access current thread's chain
|
26
|
-
CMDx::Chain.current # Current chain or nil
|
27
|
-
CMDx::Chain.clear # Clear thread's chain
|
14
|
+
Each thread maintains its own chain context through thread-local storage, providing automatic isolation without manual coordination.
|
28
15
|
|
29
|
-
|
30
|
-
|
31
|
-
def call
|
32
|
-
# These inherit the same chain automatically
|
33
|
-
ValidateOrderTask.call!(order_id: order_id)
|
34
|
-
ChargePaymentTask.call!(order_id: order_id)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
```
|
38
|
-
|
39
|
-
## Thread-Local Chain Management
|
40
|
-
|
41
|
-
> [!NOTE]
|
42
|
-
> Each thread maintains its own chain context through thread-local storage, providing automatic isolation without manual coordination.
|
16
|
+
> [!WARNING]
|
17
|
+
> Chain operations are thread-local. Never share chain references across threads as this can lead to race conditions and data corruption.
|
43
18
|
|
44
19
|
```ruby
|
45
20
|
# Thread A
|
46
21
|
Thread.new do
|
47
|
-
result =
|
48
|
-
result.chain.id
|
22
|
+
result = ImportDataset.execute(file_path: "/data/batch1.csv")
|
23
|
+
result.chain.id #=> "018c2b95-b764-7615-a924-cc5b910ed1e5"
|
49
24
|
end
|
50
25
|
|
51
26
|
# Thread B (completely separate chain)
|
52
27
|
Thread.new do
|
53
|
-
result =
|
54
|
-
result.chain.id
|
28
|
+
result = ImportDataset.execute(file_path: "/data/batch2.csv")
|
29
|
+
result.chain.id #=> "z3a42b95-c821-7892-b156-dd7c921fe2a3"
|
55
30
|
end
|
56
31
|
|
57
32
|
# Access current thread's chain
|
58
|
-
CMDx::Chain.current
|
59
|
-
CMDx::Chain.clear
|
33
|
+
CMDx::Chain.current #=> Returns current chain or nil
|
34
|
+
CMDx::Chain.clear #=> Clears current thread's chain
|
60
35
|
```
|
61
36
|
|
62
|
-
##
|
37
|
+
## Links
|
63
38
|
|
64
39
|
Every task execution automatically creates or joins the current thread's chain:
|
65
40
|
|
66
|
-
|
67
|
-
|
68
|
-
result1 = ProcessOrderTask.call(order_id: 123)
|
69
|
-
result1.chain.id # "018c2b95-b764-7615-a924-cc5b910ed1e5"
|
70
|
-
result1.chain.results.size # 1
|
71
|
-
|
72
|
-
# Second task joins existing chain
|
73
|
-
result2 = SendEmailTask.call(to: "user@example.com")
|
74
|
-
result2.chain.id == result1.chain.id # true
|
75
|
-
result2.chain.results.size # 2
|
41
|
+
> [!IMPORTANT]
|
42
|
+
> Chain creation is automatic and transparent. You don't need to manually manage chain lifecycle.
|
76
43
|
|
77
|
-
|
78
|
-
|
44
|
+
```ruby
|
45
|
+
class ImportDataset < CMDx::Task
|
46
|
+
def work
|
47
|
+
# First task creates new chain
|
48
|
+
result1 = ValidateHeaders.execute(file_path: context.file_path)
|
49
|
+
result1.chain.id #=> "018c2b95-b764-7615-a924-cc5b910ed1e5"
|
50
|
+
result1.chain.results.size #=> 1
|
51
|
+
|
52
|
+
# Second task joins existing chain
|
53
|
+
result2 = SendNotification.execute(to: "admin@company.com")
|
54
|
+
result2.chain.id == result1.chain.id #=> true
|
55
|
+
result2.chain.results.size #=> 2
|
56
|
+
|
57
|
+
# Both results reference the same chain
|
58
|
+
result1.chain.results == result2.chain.results #=> true
|
59
|
+
end
|
60
|
+
end
|
79
61
|
```
|
80
62
|
|
81
|
-
##
|
63
|
+
## Inheritance
|
82
64
|
|
83
|
-
|
84
|
-
> When tasks call subtasks within the same thread, all executions automatically inherit the current chain, creating a unified execution trail.
|
65
|
+
When tasks call subtasks within the same thread, all executions automatically inherit the current chain, creating a unified execution trail.
|
85
66
|
|
86
67
|
```ruby
|
87
|
-
class
|
88
|
-
def
|
89
|
-
context.
|
68
|
+
class ImportDataset < CMDx::Task
|
69
|
+
def work
|
70
|
+
context.dataset = Dataset.find(context.dataset_id)
|
90
71
|
|
91
72
|
# Subtasks automatically inherit current chain
|
92
|
-
|
93
|
-
|
94
|
-
|
73
|
+
ValidateSchema.execute
|
74
|
+
TransformData.execute!(context)
|
75
|
+
SaveToDatabase.execute(dataset_id: context.dataset_id)
|
95
76
|
end
|
96
77
|
end
|
97
78
|
|
98
|
-
result =
|
79
|
+
result = ImportDataset.execute(dataset_id: 456)
|
99
80
|
chain = result.chain
|
100
81
|
|
101
82
|
# All tasks share the same chain
|
102
|
-
chain.results.size
|
103
|
-
chain.results.map
|
104
|
-
|
83
|
+
chain.results.size #=> 4 (main task + 3 subtasks)
|
84
|
+
chain.results.map { |r| r.task.class }
|
85
|
+
#=> [ImportDataset, ValidateSchema, TransformData, SaveToDatabase]
|
105
86
|
```
|
106
87
|
|
107
|
-
##
|
88
|
+
## Structure
|
108
89
|
|
109
90
|
Chains provide comprehensive execution information with state delegation:
|
110
91
|
|
92
|
+
> [!IMPORTANT]
|
93
|
+
> Chain state always reflects the first (outer-most) task result, not individual subtask outcomes. Subtasks maintain their own success/failure states.
|
94
|
+
|
111
95
|
```ruby
|
112
|
-
result =
|
96
|
+
result = ImportDataset.execute(dataset_id: 456)
|
113
97
|
chain = result.chain
|
114
98
|
|
115
99
|
# Chain identification
|
116
|
-
chain.id
|
117
|
-
chain.results
|
100
|
+
chain.id #=> "018c2b95-b764-7615-a924-cc5b910ed1e5"
|
101
|
+
chain.results #=> Array of all results in execution order
|
118
102
|
|
119
103
|
# State delegation (from first/outer-most result)
|
120
|
-
chain.state
|
121
|
-
chain.status
|
122
|
-
chain.outcome
|
123
|
-
chain.runtime # 1.2 (total execution time)
|
104
|
+
chain.state #=> "complete"
|
105
|
+
chain.status #=> "success"
|
106
|
+
chain.outcome #=> "success"
|
124
107
|
|
125
108
|
# Access individual results
|
126
109
|
chain.results.each_with_index do |result, index|
|
@@ -128,182 +111,6 @@ chain.results.each_with_index do |result, index|
|
|
128
111
|
end
|
129
112
|
```
|
130
113
|
|
131
|
-
## Correlation ID Integration
|
132
|
-
|
133
|
-
> [!TIP]
|
134
|
-
> Chain IDs serve as correlation identifiers, enabling request tracing across distributed systems and complex workflows.
|
135
|
-
|
136
|
-
### Automatic Correlation
|
137
|
-
|
138
|
-
Chains integrate with the correlation system using hierarchical precedence:
|
139
|
-
|
140
|
-
```ruby
|
141
|
-
# 1. Existing chain ID takes precedence
|
142
|
-
CMDx::Chain.current = CMDx::Chain.new(id: "request-123")
|
143
|
-
result = ProcessOrderTask.call(order_id: 456)
|
144
|
-
result.chain.id # "request-123"
|
145
|
-
|
146
|
-
# 2. Thread-local correlation used if no chain exists
|
147
|
-
CMDx::Chain.clear
|
148
|
-
CMDx::Correlator.id = "session-456"
|
149
|
-
result = ProcessOrderTask.call(order_id: 789)
|
150
|
-
result.chain.id # "session-456"
|
151
|
-
|
152
|
-
# 3. Generated UUID when no correlation exists
|
153
|
-
CMDx::Correlator.clear
|
154
|
-
result = ProcessOrderTask.call(order_id: 101)
|
155
|
-
result.chain.id # "018c2b95-b764-7615-a924-cc5b910ed1e5" (generated)
|
156
|
-
```
|
157
|
-
|
158
|
-
### Custom Chain IDs
|
159
|
-
|
160
|
-
```ruby
|
161
|
-
# Create chain with specific correlation ID
|
162
|
-
chain = CMDx::Chain.new(id: "api-request-789")
|
163
|
-
CMDx::Chain.current = chain
|
164
|
-
|
165
|
-
result = ProcessApiRequestTask.call(data: payload)
|
166
|
-
result.chain.id # "api-request-789"
|
167
|
-
|
168
|
-
# All subtasks inherit the same correlation ID
|
169
|
-
result.chain.results.all? { |r| r.chain.id == "api-request-789" } # true
|
170
|
-
```
|
171
|
-
|
172
|
-
### Correlation Context Management
|
173
|
-
|
174
|
-
```ruby
|
175
|
-
# Scoped correlation context
|
176
|
-
CMDx::Correlator.use("user-session-123") do
|
177
|
-
result = ProcessUserActionTask.call(action: "purchase")
|
178
|
-
result.chain.id # "user-session-123"
|
179
|
-
|
180
|
-
# Nested operations inherit correlation
|
181
|
-
AuditLogTask.call(event: "purchase_completed")
|
182
|
-
end
|
183
|
-
|
184
|
-
# Outside block, correlation context restored
|
185
|
-
result = OtherTask.call
|
186
|
-
result.chain.id # Different correlation ID
|
187
|
-
```
|
188
|
-
|
189
|
-
## State Delegation
|
190
|
-
|
191
|
-
> [!WARNING]
|
192
|
-
> Chain state always reflects the first (outer-most) task result, not individual subtask outcomes. Subtasks maintain their own success/failure states.
|
193
|
-
|
194
|
-
```ruby
|
195
|
-
class ProcessOrderTask < CMDx::Task
|
196
|
-
def call
|
197
|
-
ValidateOrderTask.call!(order_id: order_id) # Success
|
198
|
-
ChargePaymentTask.call!(order_id: order_id) # Failure
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
result = ProcessOrderTask.call(order_id: 123)
|
203
|
-
chain = result.chain
|
204
|
-
|
205
|
-
# Chain delegates to main task (first result)
|
206
|
-
chain.status # "failed" (ProcessOrderTask failed due to subtask)
|
207
|
-
chain.state # "interrupted"
|
208
|
-
|
209
|
-
# Individual results maintain their own state
|
210
|
-
chain.results[0].status # "failed" (ProcessOrderTask - main)
|
211
|
-
chain.results[1].status # "success" (ValidateOrderTask)
|
212
|
-
chain.results[2].status # "failed" (ChargePaymentTask)
|
213
|
-
```
|
214
|
-
|
215
|
-
## Serialization and Logging
|
216
|
-
|
217
|
-
Chains provide comprehensive serialization for monitoring and debugging:
|
218
|
-
|
219
|
-
```ruby
|
220
|
-
result = ProcessOrderTask.call(order_id: 123)
|
221
|
-
chain = result.chain
|
222
|
-
|
223
|
-
# Structured data representation
|
224
|
-
chain.to_h
|
225
|
-
# {
|
226
|
-
# id: "018c2b95-b764-7615-a924-cc5b910ed1e5",
|
227
|
-
# state: "complete",
|
228
|
-
# status: "success",
|
229
|
-
# outcome: "success",
|
230
|
-
# runtime: 0.8,
|
231
|
-
# results: [
|
232
|
-
# { class: "ProcessOrderTask", state: "complete", status: "success", ... },
|
233
|
-
# { class: "ValidateOrderTask", state: "complete", status: "success", ... },
|
234
|
-
# { class: "ChargePaymentTask", state: "complete", status: "success", ... }
|
235
|
-
# ]
|
236
|
-
# }
|
237
|
-
|
238
|
-
# Human-readable execution summary
|
239
|
-
puts chain.to_s
|
240
|
-
# chain: 018c2b95-b764-7615-a924-cc5b910ed1e5
|
241
|
-
# ================================================
|
242
|
-
#
|
243
|
-
# ProcessOrderTask: index=0 state=complete status=success runtime=0.8
|
244
|
-
# ValidateOrderTask: index=1 state=complete status=success runtime=0.1
|
245
|
-
# ChargePaymentTask: index=2 state=complete status=success runtime=0.5
|
246
|
-
#
|
247
|
-
# ================================================
|
248
|
-
# state: complete | status: success | outcome: success | runtime: 0.8
|
249
|
-
```
|
250
|
-
|
251
|
-
## Error Handling
|
252
|
-
|
253
|
-
### Chain Access Patterns
|
254
|
-
|
255
|
-
```ruby
|
256
|
-
# Safe chain access
|
257
|
-
result = ProcessOrderTask.call(order_id: 123)
|
258
|
-
|
259
|
-
if result.chain
|
260
|
-
correlation_id = result.chain.id
|
261
|
-
execution_count = result.chain.results.size
|
262
|
-
else
|
263
|
-
# Handle missing chain (shouldn't happen in normal execution)
|
264
|
-
correlation_id = "unknown"
|
265
|
-
end
|
266
|
-
```
|
267
|
-
|
268
|
-
### Thread Safety
|
269
|
-
|
270
|
-
> [!IMPORTANT]
|
271
|
-
> Chain operations are thread-safe within individual threads but chains should not be shared across threads. Each thread maintains its own isolated chain context.
|
272
|
-
|
273
|
-
```ruby
|
274
|
-
# Safe: Each thread has its own chain
|
275
|
-
threads = 3.times.map do |i|
|
276
|
-
Thread.new do
|
277
|
-
result = ProcessOrderTask.call(order_id: 100 + i)
|
278
|
-
result.chain.id # Unique per thread
|
279
|
-
end
|
280
|
-
end
|
281
|
-
|
282
|
-
# Collect results safely
|
283
|
-
chain_ids = threads.map(&:value)
|
284
|
-
chain_ids.uniq.size # 3 (all different)
|
285
|
-
```
|
286
|
-
|
287
|
-
### Chain State Validation
|
288
|
-
|
289
|
-
```ruby
|
290
|
-
result = ProcessOrderTask.call(order_id: 123)
|
291
|
-
chain = result.chain
|
292
|
-
|
293
|
-
# Validate chain integrity
|
294
|
-
case chain.state
|
295
|
-
when "complete"
|
296
|
-
# All tasks finished normally
|
297
|
-
process_successful_chain(chain)
|
298
|
-
when "interrupted"
|
299
|
-
# Task was halted or failed
|
300
|
-
handle_chain_interruption(chain)
|
301
|
-
else
|
302
|
-
# Unexpected state
|
303
|
-
log_chain_anomaly(chain)
|
304
|
-
end
|
305
|
-
```
|
306
|
-
|
307
114
|
---
|
308
115
|
|
309
116
|
- **Prev:** [Basics - Context](context.md)
|