Skip to content

Commit 500c930

Browse files
committed
Trace now works via canonical invoke.
1 parent 927b7b7 commit 500c930

File tree

4 files changed

+154
-96
lines changed

4 files changed

+154
-96
lines changed

lib/trailblazer/developer/trace.rb

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,39 @@
11
module Trailblazer::Developer
22
module Trace
33
class << self
4-
# Public entry point to run an activity with tracing.
5-
# It returns the accumulated stack of Snapshots, along with the original return values.
6-
# Note that {Trace.invoke} does not do any rendering.
7-
def call(activity, (ctx, flow_options), **circuit_options)
8-
activity, (ctx, flow_options), circuit_options = Trace.arguments_for_call(activity, [ctx, flow_options], **circuit_options) # only run once for the entire circuit!
9-
10-
signal, (ctx, flow_options) = Trailblazer::Activity::TaskWrap.invoke(activity, [ctx, flow_options], **circuit_options)
11-
12-
return flow_options[:stack], signal, [ctx, flow_options]
13-
end
14-
15-
alias_method :invoke, :call
16-
17-
def arguments_for_call(activity, (options, original_flow_options), **original_circuit_options)
18-
default_flow_options = {
4+
# Follows the interface for options-compiler.
5+
# @private
6+
def invoke_options_compiler_step(activity, options, **)
7+
flow_options = {
198
stack: Trace::Stack.new,
209
before_snapshooter: Snapshot.method(:before_snapshooter),
2110
after_snapshooter: Snapshot.method(:after_snapshooter),
2211
value_snapshooter: Trace.value_snapshooter
2312
}
2413

25-
flow_options = {**default_flow_options, **Hash(original_flow_options)}
26-
27-
# TODO: we should, at this point, merge all runtime Ext() objects instead of overriding.
28-
default_circuit_options = {
14+
circuit_options = {
2915
wrap_runtime: ::Hash.new(Trace.task_wrap_extensions), # FIXME: this overrides existing {:wrap_runtime}.
3016
}
3117

32-
circuit_options = {**original_circuit_options, **default_circuit_options}
18+
# those will be deep_merged in invoke's options-compiler?
19+
{
20+
flow_options: flow_options,
21+
circuit_options: circuit_options
22+
}
23+
end
24+
25+
# Public entry point to run an activity with tracing.
26+
# It returns the accumulated stack of Snapshots, along with the original return values.
27+
# Note that {Trace.invoke} does not do any rendering.
3328

34-
return activity, [options, flow_options], circuit_options
29+
# DISCUSS: could this be a constant?
30+
# @public
31+
def options_for_canonical_invoke(adds_for_options_compiler: [], **options) # TODO: can be a constant.
32+
{
33+
adds_for_options_compiler: [
34+
[Trailblazer::Invoke::Options::HeuristicMerge.build(method(:invoke_options_compiler_step)), id: "developer.trace", append: nil],
35+
] + adds_for_options_compiler
36+
}
3537
end
3638
end
3739

test/test_helper.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
22
require "trailblazer/developer"
3+
require "trailblazer/core"
34

45
require "minitest/autorun"
56
require "pp"
67

7-
require "trailblazer/activity"
8-
require "trailblazer/activity/testing"
98
require "trailblazer/activity/dsl/linear"
9+
require "trailblazer/activity/testing"
1010
puts "Running in Ruby #{RUBY_VERSION}"
1111

1212
T = Trailblazer::Activity::Testing
@@ -16,6 +16,8 @@ def assert_equal(asserted, expected, *args)
1616
super(expected, asserted, *args)
1717
end
1818

19+
CU = Trailblazer::Core::Utils
20+
1921
Dev = Trailblazer::Developer
2022
include Trailblazer::Activity::Testing::Assertions
2123

test/trace_test.rb

Lines changed: 126 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,40 @@
11
require "test_helper"
22

3-
# Test {Trace.call} and {Trace::Present.call}
4-
class TraceTest < Minitest::Spec
5-
it "traces flat strategy" do
6-
stack, signal, (ctx, flow_options), _ = Dev::Trace.invoke(flat_activity, [{seq: []}, {flow: true}])
7-
8-
assert_equal signal.class.inspect, %{Trailblazer::Activity::End}
9-
10-
assert_equal ctx.inspect, %{{:seq=>[:B, :C]}}
11-
assert_equal flow_options[:flow].inspect, %{true}
12-
13-
output = Dev::Trace::Present.(stack)
14-
output = output.gsub(/0x\w+/, "").gsub(/0x\w+/, "").gsub(/@.+_test/, "")
15-
16-
assert_equal output, %{#<Class:>
17-
|-- Start.default
18-
|-- B
19-
|-- C
20-
`-- End.success}
3+
require "trailblazer/invoke"
4+
class TraceInvokeTest < Minitest::Spec
5+
let(:kernel) do
6+
Class.new do
7+
Trailblazer::Invoke.module!(self)
8+
end.new
219
end
2210

23-
it "traces flat activity" do
24-
activity = flat_activity.to_h[:activity]
25-
26-
stack, signal, (ctx, flow_options), _ = Dev::Trace.invoke(activity, [{seq: []}, {flow: true}])
27-
28-
assert_equal signal.class.inspect, %{Trailblazer::Activity::End}
11+
it "traces a flat activity" do
12+
signal, (ctx, flow_options), _ = kernel.__(
13+
flat_activity,
14+
{seq: []},
15+
**Trailblazer::Developer::Trace.options_for_canonical_invoke
16+
)
2917

30-
assert_equal ctx.inspect, %{{:seq=>[:B, :C]}}
31-
assert_equal flow_options[:flow].inspect, %{true}
18+
assert_equal signal.to_h[:semantic], :success
19+
assert_equal CU.inspect(ctx.to_h), %({:seq=>[:B, :C]})
3220

33-
output = Dev::Trace::Present.(stack)
21+
stack = flow_options[:stack]
22+
output = Trailblazer::Developer::Trace::Present.(stack)
3423
output = output.gsub(/0x\w+/, "").gsub(/0x\w+/, "").gsub(/@.+_test/, "")
3524

36-
assert_equal output, %{#<Trailblazer::Activity:>
25+
assert_equal output, %(#<Class:>
3726
|-- Start.default
3827
|-- B
3928
|-- C
40-
`-- End.success}
29+
`-- End.success)
4130
end
4231

43-
it "you can pass an explicit task label via {:label}" do
44-
stack, signal, (ctx, flow_options), _ = Dev::Trace.invoke(flat_activity, [{seq: []}, {}])
32+
# TODO: can we add more {:wrap_runtime}?
4533

46-
output = Dev::Trace::Present.(stack) do |trace_nodes:, **|
34+
it "{Present}: you can pass an explicit task label via {:label}" do
35+
signal, (ctx, flow_options), _ = kernel.__(flat_activity, {seq: []}, **Trailblazer::Developer::Trace.options_for_canonical_invoke)
36+
37+
output = Dev::Trace::Present.(flow_options[:stack]) do |trace_nodes:, **|
4738
{
4839
node_options: {
4940
trace_nodes[0] => {label: "#{flat_activity.class} (anonymous)"}
@@ -61,13 +52,9 @@ class TraceTest < Minitest::Spec
6152
it "nested tracing" do
6253
activity, sub_activity, _activity = Tracing.three_level_nested_activity(e_options: {Trailblazer::Activity::Railway.Out() => [:nil_value]})
6354

64-
stack, signal, (ctx, flow_options) = Dev::Trace.invoke(
65-
activity,
66-
[
67-
{seq: []},
68-
{flow: true}
69-
]
70-
)
55+
signal, (ctx, flow_options) = kernel.__(activity, {seq: []}, **Trailblazer::Developer::Trace.options_for_canonical_invoke)
56+
57+
stack = flow_options[:stack]
7158

7259
assert_equal ctx[:seq], [:a, :b, :c, :d, :e]
7360

@@ -109,8 +96,45 @@ class TraceTest < Minitest::Spec
10996
|-- e
11097
`-- End.success}
11198
end
99+
end
100+
101+
# Test {Trace.call} and {Trace::Present.call}
102+
class TraceTest < Minitest::Spec
103+
# This is for people who were using Developer::Trace.(MyActivity) to trace on their own.
104+
it "allows tracing by manually passing the options" do
105+
trace_args = Trailblazer::Developer::Trace.invoke_options_compiler_step(flat_activity, {})
106+
107+
signal, (ctx, flow_options), _ = Trailblazer::Activity::TaskWrap.invoke(
108+
flat_activity,
109+
[
110+
{seq: []},
111+
trace_args[:flow_options]
112+
],
113+
**trace_args[:circuit_options]
114+
)
115+
116+
assert_equal signal.to_h[:semantic], :success
117+
assert_equal CU.inspect(ctx.to_h), %({:seq=>[:B, :C]})
118+
119+
stack = flow_options[:stack]
120+
output = Trailblazer::Developer::Trace::Present.(stack)
121+
output = output.gsub(/0x\w+/, "").gsub(/0x\w+/, "").gsub(/@.+_test/, "")
122+
123+
assert_equal output, %(#<Class:>
124+
|-- Start.default
125+
|-- B
126+
|-- C
127+
`-- End.success)
128+
end
129+
end
112130

113-
# Test custom classes without explicit {#hash} implementation.
131+
# Test specific options such as {:snapshooter}.
132+
class TraceAPITest < Minitest::Spec
133+
let(:kernel) do
134+
Class.new { Trailblazer::Invoke.module!(self) }.new
135+
end
136+
137+
# Test custom classes without explicit {#hash} implementation.
114138
class User
115139
def initialize(id)
116140
@id = id
@@ -163,17 +187,14 @@ def authorize(ctx, current_user:, seq:, **)
163187
after_snapshooter: Snapshot.method(:after_snapshooter),
164188
}
165189

166-
activity = ::TraceTest::Endpoint
167-
168-
stack, signal, (ctx, flow_options) = Dev::Trace.invoke(
169-
activity,
170-
[
171-
{
172-
current_user: current_user = User.new(1),
173-
params: {name: "Q & I"},
174-
seq: [],
175-
},
176-
]
190+
signal, (ctx, flow_options) = kernel.__(
191+
Endpoint,
192+
{
193+
current_user: current_user = User.new(1),
194+
params: {name: "Q & I"},
195+
seq: [],
196+
},
197+
**Trailblazer::Developer::Trace.options_for_canonical_invoke
177198
)
178199

179200

@@ -197,7 +218,7 @@ def authorize(ctx, current_user:, seq:, **)
197218

198219

199220
# This is a unit test we might not need anymore:
200-
assert_equal stack[0].task, ::TraceTest::Endpoint
221+
assert_equal stack[0].task, Endpoint
201222
assert_snapshot versions, stack[0], current_user: 0, params: 0, seq: 0
202223

203224
assert_equal stack[1].task.inspect, %(#<Trailblazer::Activity::Start semantic=:default>)
@@ -218,7 +239,7 @@ def authorize(ctx, current_user:, seq:, **)
218239
assert_snapshot versions, stack[6], current_user: 0, params: 0, seq: 2
219240

220241
# Create {in}
221-
assert_equal stack[7].task, ::TraceTest::Endpoint::Create
242+
assert_equal stack[7].task, Endpoint::Create
222243
assert_snapshot versions, stack[7], current_user: 0, params: 0, seq: 2
223244

224245
# Create :model
@@ -238,7 +259,7 @@ def authorize(ctx, current_user:, seq:, **)
238259
assert_snapshot versions, stack[15], current_user: 0, params: 1, seq: 4, model: 0
239260

240261
# Create {out}
241-
assert_equal stack[16].task, ::TraceTest::Endpoint::Create
262+
assert_equal stack[16].task, Endpoint::Create
242263
assert_snapshot versions, stack[16], current_user: 0, params: 1, seq: 4, model: 0
243264

244265
# Endpoint End.success
@@ -273,8 +294,9 @@ def create(ctx, **)
273294
end
274295
end
275296

276-
stack, signal, (ctx, flow_options) = Dev::Trace.invoke(activity, [{}, {}])
297+
signal, (ctx, flow_options) = kernel.__(activity, {}, **Trailblazer::Developer::Trace.options_for_canonical_invoke)
277298

299+
stack = flow_options[:stack]
278300
nodes = stack.to_a
279301

280302
# :override/after
@@ -305,12 +327,26 @@ def create(ctx, **)
305327
end
306328
end
307329

308-
stack, signal, (ctx, flow_options) = Dev::Trace.invoke(activity, [{params: {}},
309-
{
310-
value_snapshooter: value_snapshooter
311-
}
312-
])
330+
trace_args_for_invoke = Trailblazer::Developer::Trace.options_for_canonical_invoke(
331+
adds_for_options_compiler: [
332+
[
333+
Trailblazer::Invoke::Options::HeuristicMerge.build(
334+
->(*) do
335+
{
336+
flow_options: {
337+
value_snapshooter: value_snapshooter
338+
}
339+
}
340+
end
341+
),
342+
id: "user.trace.value_snapshooter_options", append: nil
343+
]
344+
]
345+
)
313346

347+
signal, (ctx, flow_options) = kernel.__(activity, {params: {}}, **trace_args_for_invoke)
348+
349+
stack = flow_options[:stack]
314350
nodes = stack.to_a
315351

316352
# Op/after
@@ -326,8 +362,9 @@ def create(ctx, **)
326362
# We can also set it via {Trace.value_snapshooter}
327363
Trailblazer::Developer::Trace.instance_variable_set(:@value_snapshooter, value_snapshooter)
328364

329-
stack, signal, (ctx, flow_options) = Dev::Trace.invoke(activity, [{params: {}}, {}])
365+
signal, (ctx, flow_options) = kernel.__(activity, {params: {}}, **Trailblazer::Developer::Trace.options_for_canonical_invoke)
330366

367+
stack = flow_options[:stack]
331368
nodes = stack.to_a
332369

333370
# Op/after
@@ -342,25 +379,41 @@ def create(ctx, **)
342379
Trailblazer::Developer::Trace.instance_variable_set(:@value_snapshooter, Trailblazer::Developer::Trace::Snapshot::Value.build) # reset to original value.
343380
end
344381

345-
it "allows to inject custom :data_collector" do
346-
input_collector = ->(wrap_config, ((ctx, _), _)) { [{ ctx: ctx, something: :else }, {}] }
347-
output_collector = ->(wrap_config, ((ctx, _), _)) { [{ ctx: ctx, signal: wrap_config[:return_signal] }, {}] }
382+
it "allows to inject custom data collector" do
383+
input_collector = ->(wrap_ctx, ((ctx, _), _)) { [{ ctx: ctx.to_h, something: :else }, {}] }
384+
output_collector = ->(wrap_ctx, ((ctx, _), _)) { [{ ctx: ctx.to_h, signal: wrap_ctx[:return_signal] }, {}] }
385+
386+
trace_args_for_invoke = Trailblazer::Developer::Trace.options_for_canonical_invoke(
387+
adds_for_options_compiler: [
388+
[
389+
Trailblazer::Invoke::Options::HeuristicMerge.build(
390+
->(*) do
391+
{
392+
flow_options: {
393+
before_snapshooter: input_collector,
394+
after_snapshooter: output_collector,
395+
}
396+
}
397+
end
398+
),
399+
id: "user.trace.snapshooter_options", append: nil
400+
]
401+
]
402+
)
348403

349-
stack, signal, (ctx, _) = Dev::Trace.invoke(
404+
signal, (ctx, flow_options) = kernel.__(
350405
flat_activity,
351-
[
352406
{ seq: [] },
353-
{
354-
before_snapshooter: input_collector,
355-
after_snapshooter: output_collector,
356-
}
357-
]
407+
**trace_args_for_invoke
358408
)
359409

360410
assert_equal ctx[:seq], [:B, :C]
361411

362-
captured_input = stack.to_a[0]
363-
captured_output = stack.to_a[-1]
412+
stack = flow_options[:stack].to_a
413+
captured_input = stack[0]
414+
captured_output = stack[-1]
415+
416+
# pp stack
364417

365418
assert_equal captured_input.data, { ctx: { seq: [:B, :C] }, something: :else }
366419
assert_equal captured_output.data, { ctx: { seq: [:B, :C] }, signal: signal }

trailblazer-developer.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Gem::Specification.new do |spec|
2323
spec.add_development_dependency "minitest-line"
2424
spec.add_development_dependency "rake"
2525
spec.add_development_dependency "trailblazer-operation", ">= 0.10.0"
26+
spec.add_development_dependency "trailblazer-core-utils"
2627

2728
spec.add_dependency "trailblazer-activity-dsl-linear", ">= 1.2.0", "< 1.3.0"
2829
spec.add_dependency "hirb"

0 commit comments

Comments
 (0)