cmdx 2.0.0 → 2.0.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/CHANGELOG.md +10 -1
- data/lib/cmdx/context.rb +4 -4
- data/lib/cmdx/errors.rb +4 -1
- data/lib/cmdx/i18n_proxy.rb +1 -1
- data/lib/cmdx/pipeline.rb +11 -9
- data/lib/cmdx/runtime.rb +4 -23
- data/lib/cmdx/telemetry.rb +15 -1
- data/lib/cmdx/version.rb +1 -1
- data/lib/generators/cmdx/templates/install.rb +22 -0
- data/mkdocs.yml +1 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f2cf3294b8f53c29043999fe6749ffee42f6c9a52d2e4e327f1ac4589ae96c47
|
|
4
|
+
data.tar.gz: 8d73284f355b01e171bb75f69c3fab37240462140e834f96e07831402517b89c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5c1e9c43bddefec0085027447651b20de2e1ef16c57492c060d411b08fc036435c7358bcc910fcb7b7f14a8a3d166f45d4a856b17aaa2454bdb2f5f609ca4be3
|
|
7
|
+
data.tar.gz: 382b8a147035f476f2b03fda8fc1573a4b729bfc336d9776bf04d6c3f5bd95f6617d9efa77b0bbd3576a3feae828537618c06cfa53df1b119d8ac2425ac63682
|
data/CHANGELOG.md
CHANGED
|
@@ -4,7 +4,16 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
-
## [2.
|
|
7
|
+
## [2.x.x] - UNRELEASED
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- Link cause of a raised Fault exception
|
|
11
|
+
- Simplify context `method_missing` lookup order (small perf boost)
|
|
12
|
+
- Attempt translation of errors `full_messages`
|
|
13
|
+
- Emit `:task_rolled_back` telemetry event on workflow rollback
|
|
14
|
+
- `around_execution` callbacks now wrap only `Task#work` (and any `#rollback`); `before_validation` runs *before* the around-block and `after_execution` runs *after* it. Previously both were nested inside the around-block
|
|
15
|
+
|
|
16
|
+
## [2.0.0] - 2026-05-05
|
|
8
17
|
|
|
9
18
|
Full runtime rewrite: the v1 state-machine plus Zeitwerk architecture is replaced by an explicit signal-based runtime, immutable results, fiber-local chains, and a slimmer registry surface. See [docs/v2-migration.md](docs/v2-migration.md) for the full upgrade guide.
|
|
10
19
|
|
data/lib/cmdx/context.rb
CHANGED
|
@@ -281,14 +281,14 @@ module CMDx
|
|
|
281
281
|
# @raise [NoMethodError] when {#strict?} is true and the key is missing
|
|
282
282
|
# @api private
|
|
283
283
|
def method_missing(method_name, *args, **_kwargs, &)
|
|
284
|
-
if
|
|
284
|
+
if @table.key?(method_name)
|
|
285
|
+
@table[method_name]
|
|
286
|
+
elsif method_name.end_with?("=")
|
|
285
287
|
@table[method_name[..-2].to_sym] = args.first
|
|
286
288
|
elsif method_name.end_with?("?")
|
|
287
289
|
!!@table[method_name[..-2].to_sym]
|
|
288
|
-
elsif strict?
|
|
290
|
+
elsif strict?
|
|
289
291
|
raise NoMethodError, "unknown context key #{method_name.inspect} (strict mode)"
|
|
290
|
-
else
|
|
291
|
-
@table[method_name]
|
|
292
292
|
end
|
|
293
293
|
end
|
|
294
294
|
|
data/lib/cmdx/errors.rb
CHANGED
|
@@ -113,7 +113,10 @@ module CMDx
|
|
|
113
113
|
# (e.g. `{ name: ["name is required"] }`)
|
|
114
114
|
def full_messages
|
|
115
115
|
messages.each_with_object({}) do |(key, set), hash|
|
|
116
|
-
hash[key] = set.map
|
|
116
|
+
hash[key] = set.map do |message|
|
|
117
|
+
i18n_message = I18nProxy.t(message, default: message)
|
|
118
|
+
"#{key} #{i18n_message}"
|
|
119
|
+
end
|
|
117
120
|
end
|
|
118
121
|
end
|
|
119
122
|
|
data/lib/cmdx/i18n_proxy.rb
CHANGED
|
@@ -62,7 +62,7 @@ module CMDx
|
|
|
62
62
|
# @option options [Hash{Symbol => Object}] extra keys interpolated via `String#%` for bundled translations
|
|
63
63
|
# @return [String, Object] the translated/interpolated message
|
|
64
64
|
def translate(key, **options)
|
|
65
|
-
return ::I18n.translate(key, **options) if defined?(::I18n)
|
|
65
|
+
return ::I18n.translate(key, **options) if defined?(::I18n)
|
|
66
66
|
|
|
67
67
|
message = translation_default(key) || options[:default]
|
|
68
68
|
|
data/lib/cmdx/pipeline.rb
CHANGED
|
@@ -70,8 +70,6 @@ module CMDx
|
|
|
70
70
|
|
|
71
71
|
private
|
|
72
72
|
|
|
73
|
-
# @param group [Workflow::ExecutionGroup]
|
|
74
|
-
# @return [Result, nil] failed result to halt on, or nil when the group succeeds
|
|
75
73
|
def run_sequential(group)
|
|
76
74
|
continue = group.options[:continue_on_failure]
|
|
77
75
|
failures = group.tasks.each_with_object([]) do |task_class, bucket|
|
|
@@ -87,8 +85,6 @@ module CMDx
|
|
|
87
85
|
aggregate(failures, continue:)
|
|
88
86
|
end
|
|
89
87
|
|
|
90
|
-
# @param group [Workflow::ExecutionGroup]
|
|
91
|
-
# @return [Result, nil] failed result to halt on, or nil when the group succeeds
|
|
92
88
|
def run_parallel(group)
|
|
93
89
|
tasks = group.tasks
|
|
94
90
|
chain = Chain.current
|
|
@@ -146,17 +142,23 @@ module CMDx
|
|
|
146
142
|
next unless result.success?
|
|
147
143
|
next unless instance.respond_to?(:rollback)
|
|
148
144
|
|
|
149
|
-
instance.rollback
|
|
150
|
-
|
|
151
145
|
old_opts = result.instance_variable_get(:@options)
|
|
152
146
|
new_opts = old_opts.merge(rolled_back: true).freeze
|
|
153
147
|
result.instance_variable_set(:@options, new_opts)
|
|
148
|
+
|
|
149
|
+
emit_telemetry(instance, :task_rolled_back)
|
|
150
|
+
instance.rollback
|
|
154
151
|
end
|
|
155
152
|
end
|
|
156
153
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
154
|
+
def emit_telemetry(instance, name, payload = EMPTY_HASH)
|
|
155
|
+
telemetry = instance.class.telemetry
|
|
156
|
+
return unless telemetry.subscribed?(name)
|
|
157
|
+
|
|
158
|
+
event = Telemetry::Event.build(instance, name, root: false, payload:)
|
|
159
|
+
telemetry.emit(name, event)
|
|
160
|
+
end
|
|
161
|
+
|
|
160
162
|
def aggregate(failures, continue:)
|
|
161
163
|
return if failures.empty?
|
|
162
164
|
return failures.first unless continue
|
data/lib/cmdx/runtime.rb
CHANGED
|
@@ -90,12 +90,12 @@ module CMDx
|
|
|
90
90
|
def run_lifecycle
|
|
91
91
|
measure_duration do
|
|
92
92
|
run_callbacks(:before_execution)
|
|
93
|
+
run_callbacks(:before_validation)
|
|
93
94
|
run_around(:around_execution) do
|
|
94
|
-
run_callbacks(:before_validation)
|
|
95
95
|
perform_work
|
|
96
96
|
perform_rollback if @signal.failed?
|
|
97
|
-
run_callbacks(:after_execution)
|
|
98
97
|
end
|
|
98
|
+
run_callbacks(:after_execution)
|
|
99
99
|
run_callbacks(:"on_#{@signal.state}")
|
|
100
100
|
run_callbacks(:"on_#{@signal.status}")
|
|
101
101
|
run_callbacks(:on_ok) if @signal.ok?
|
|
@@ -109,7 +109,7 @@ module CMDx
|
|
|
109
109
|
cause = @signal.cause
|
|
110
110
|
raise cause if cause && !cause.is_a?(Fault)
|
|
111
111
|
|
|
112
|
-
raise Fault, @result.caused_failure
|
|
112
|
+
raise Fault, @result.caused_failure, cause:
|
|
113
113
|
end
|
|
114
114
|
|
|
115
115
|
def finalize_result
|
|
@@ -151,8 +151,6 @@ module CMDx
|
|
|
151
151
|
@duration = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond) - start
|
|
152
152
|
end
|
|
153
153
|
|
|
154
|
-
# @param event [Symbol] callback event from {Callbacks::EVENTS}
|
|
155
|
-
# @return [void]
|
|
156
154
|
def run_callbacks(event)
|
|
157
155
|
callbacks = @task.class.callbacks
|
|
158
156
|
return if callbacks.empty?
|
|
@@ -160,9 +158,6 @@ module CMDx
|
|
|
160
158
|
callbacks.process(event, @task)
|
|
161
159
|
end
|
|
162
160
|
|
|
163
|
-
# @param event [Symbol] callback event from {Callbacks::EVENTS}
|
|
164
|
-
# @yield nested lifecycle segment wrapped by `around_execution` callbacks
|
|
165
|
-
# @return [Object] the wrapped block's return value
|
|
166
161
|
def run_around(event, &)
|
|
167
162
|
callbacks = @task.class.callbacks
|
|
168
163
|
return yield if callbacks.empty?
|
|
@@ -225,25 +220,11 @@ module CMDx
|
|
|
225
220
|
throw(Signal::TAG, Signal.failed(@task.errors.to_s, metadata: @task.metadata))
|
|
226
221
|
end
|
|
227
222
|
|
|
228
|
-
# @param name [Symbol] telemetry channel from {Telemetry::EVENTS}
|
|
229
|
-
# @param payload [Hash{Symbol => Object}] forwarded onto {Telemetry::Event#payload}
|
|
230
|
-
# @return [void]
|
|
231
223
|
def emit_telemetry(name, payload = EMPTY_HASH)
|
|
232
224
|
telemetry = @task.class.telemetry
|
|
233
225
|
return unless telemetry.subscribed?(name)
|
|
234
226
|
|
|
235
|
-
event = Telemetry::Event.
|
|
236
|
-
xid: Chain.current.xid,
|
|
237
|
-
cid: Chain.current.id,
|
|
238
|
-
root: @root,
|
|
239
|
-
type: @task.class.type,
|
|
240
|
-
task: @task.class,
|
|
241
|
-
tid: @task.tid,
|
|
242
|
-
name:,
|
|
243
|
-
payload:,
|
|
244
|
-
timestamp: Time.now.utc
|
|
245
|
-
)
|
|
246
|
-
|
|
227
|
+
event = Telemetry::Event.build(@task, name, root: @root, payload:)
|
|
247
228
|
telemetry.emit(name, event)
|
|
248
229
|
end
|
|
249
230
|
|
data/lib/cmdx/telemetry.rb
CHANGED
|
@@ -7,7 +7,21 @@ module CMDx
|
|
|
7
7
|
class Telemetry
|
|
8
8
|
|
|
9
9
|
# Immutable event payload passed to subscribers.
|
|
10
|
-
Event = Data.define(:xid, :cid, :root, :type, :task, :tid, :name, :payload, :timestamp)
|
|
10
|
+
Event = Data.define(:xid, :cid, :root, :type, :task, :tid, :name, :payload, :timestamp) do
|
|
11
|
+
def self.build(task, name, root: false, payload: EMPTY_HASH)
|
|
12
|
+
new(
|
|
13
|
+
xid: Chain.current.xid,
|
|
14
|
+
cid: Chain.current.id,
|
|
15
|
+
root:,
|
|
16
|
+
type: task.class.type,
|
|
17
|
+
task: task.class,
|
|
18
|
+
tid: task.tid,
|
|
19
|
+
name:,
|
|
20
|
+
payload:,
|
|
21
|
+
timestamp: Time.now.utc
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
11
25
|
|
|
12
26
|
# Lifecycle event names Runtime emits.
|
|
13
27
|
EVENTS = %i[
|
data/lib/cmdx/version.rb
CHANGED
|
@@ -108,6 +108,28 @@ CMDx.configure do |config|
|
|
|
108
108
|
# end
|
|
109
109
|
# end)
|
|
110
110
|
|
|
111
|
+
# ===========================================================================
|
|
112
|
+
# Retriers
|
|
113
|
+
# ===========================================================================
|
|
114
|
+
# Registered retriers compute the sleep duration between `retry_on` attempts.
|
|
115
|
+
# Built-ins: `:exponential` (default), `:linear`, `:fibonacci`, `:half_random`,
|
|
116
|
+
# `:full_random`, `:bounded_random`, `:decorrelated_jitter`. A callable
|
|
117
|
+
# receives `call(attempt, delay, prev_delay)` and returns seconds.
|
|
118
|
+
#
|
|
119
|
+
# config.retriers.register(:capped_exponential, proc do |attempt, delay, _prev|
|
|
120
|
+
# [delay * (2**(attempt - 1)), 30.0].min
|
|
121
|
+
# end)
|
|
122
|
+
|
|
123
|
+
# ===========================================================================
|
|
124
|
+
# Deprecators
|
|
125
|
+
# ===========================================================================
|
|
126
|
+
# Registered deprecators dispatch a task class's `deprecation` declaration.
|
|
127
|
+
# Built-ins: `:log`, `:warn`, `:error`. A callable receives `call(task)`.
|
|
128
|
+
#
|
|
129
|
+
# config.deprecators.register(:notify, proc do |task|
|
|
130
|
+
# Bugsnag.notify("Deprecated task invoked: #{task.class.name}")
|
|
131
|
+
# end)
|
|
132
|
+
|
|
111
133
|
# ===========================================================================
|
|
112
134
|
# Executors
|
|
113
135
|
# ===========================================================================
|
data/mkdocs.yml
CHANGED
|
@@ -195,6 +195,7 @@ nav:
|
|
|
195
195
|
- llms-full.txt: https://drexed.github.io/cmdx/llms-full.txt
|
|
196
196
|
- AI Skills: https://github.com/drexed/cmdx/blob/main/skills
|
|
197
197
|
- Ecosystem:
|
|
198
|
+
- cmdx-i18n: https://github.com/drexed/cmdx-i18n
|
|
198
199
|
- cmdx-rspec: https://github.com/drexed/cmdx-rspec
|
|
199
200
|
- Blog: blog/index.md
|
|
200
201
|
|