trailblazer-developer 0.0.28 → 0.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.
- checksums.yaml +4 -4
- data/CHANGES.md +75 -0
- data/Gemfile +2 -0
- data/lib/trailblazer/developer/debugger/normalizer.rb +60 -0
- data/lib/trailblazer/developer/debugger.rb +97 -0
- data/lib/trailblazer/developer/trace/node.rb +103 -0
- data/lib/trailblazer/developer/trace/parent_map.rb +32 -0
- data/lib/trailblazer/developer/trace/present.rb +50 -18
- data/lib/trailblazer/developer/trace/snapshot/value.rb +39 -0
- data/lib/trailblazer/developer/trace/snapshot/versions.rb +105 -0
- data/lib/trailblazer/developer/trace/snapshot.rb +71 -0
- data/lib/trailblazer/developer/trace/stack.rb +18 -5
- data/lib/trailblazer/developer/trace.rb +34 -59
- data/lib/trailblazer/developer/version.rb +1 -1
- data/lib/trailblazer/developer/wtf/renderer.rb +8 -8
- data/lib/trailblazer/developer/wtf.rb +47 -47
- data/lib/trailblazer/developer.rb +8 -3
- metadata +9 -5
- data/lib/trailblazer/developer/trace/debugger/normalizer.rb +0 -68
- data/lib/trailblazer/developer/trace/debugger.rb +0 -72
- data/lib/trailblazer/developer/trace/tree.rb +0 -86
@@ -0,0 +1,71 @@
|
|
1
|
+
module Trailblazer::Developer
|
2
|
+
module Trace
|
3
|
+
# WARNING: the interfaces here are subject to change, we're still experimenting
|
4
|
+
# with the architecture of tracing, and a healthy balance of performance/memory
|
5
|
+
# and clean design.
|
6
|
+
# A Snapshot comprises of data captured before of after a "step". This usually
|
7
|
+
# includes a ctx snapshot, variable versions and a returned signal for after-step
|
8
|
+
# snapshots.
|
9
|
+
#
|
10
|
+
# Note that {Before} and {After} are generic concepts know to Trace::Present and Debugger.
|
11
|
+
#
|
12
|
+
# Snapshot::After{
|
13
|
+
# signal: <End.Success>
|
14
|
+
# ctx_snapshot: Snapshot::Ctx{
|
15
|
+
# variable_versions: [:current_user, 0], [:model, 0]
|
16
|
+
# }
|
17
|
+
# }
|
18
|
+
class Snapshot < Struct.new(:task, :activity, :data)
|
19
|
+
Before = Class.new(Snapshot)
|
20
|
+
After = Class.new(Snapshot)
|
21
|
+
|
22
|
+
# This is called from {Trace.capture_args} and {Trace.capture_return} in the taskWrap.
|
23
|
+
def self.call(ctx_snapshooter, wrap_config, ((ctx, flow_options), circuit_options))
|
24
|
+
# DISCUSS: grab the {snapshooter} here from flow_options, instead of in Trace.capture_args?
|
25
|
+
changeset, new_versions = ctx_snapshooter.call(wrap_config, [[ctx, flow_options], circuit_options])
|
26
|
+
|
27
|
+
snapshot = new( # either Before or After.
|
28
|
+
wrap_config[:task],
|
29
|
+
circuit_options[:activity],
|
30
|
+
changeset
|
31
|
+
).freeze
|
32
|
+
|
33
|
+
return snapshot, new_versions
|
34
|
+
end
|
35
|
+
|
36
|
+
# Serialize all ctx variables before {call_task}.
|
37
|
+
# This is run just before {call_task}, after In().
|
38
|
+
def self.before_snapshooter(wrap_ctx, ((ctx, flow_options), _))
|
39
|
+
changeset, new_versions = snapshot_for(ctx, **flow_options)
|
40
|
+
|
41
|
+
data = {
|
42
|
+
ctx_variable_changeset: changeset,
|
43
|
+
}
|
44
|
+
|
45
|
+
return data, new_versions
|
46
|
+
end
|
47
|
+
|
48
|
+
# Serialize all ctx variables at the very end of taskWrap, after Out().
|
49
|
+
def self.after_snapshooter(wrap_ctx, _)
|
50
|
+
snapshot_before = wrap_ctx[:snapshot_before]
|
51
|
+
returned_ctx, flow_options = wrap_ctx[:return_args]
|
52
|
+
|
53
|
+
changeset, new_versions = snapshot_for(returned_ctx, **flow_options)
|
54
|
+
|
55
|
+
data = {
|
56
|
+
ctx_variable_changeset: changeset,
|
57
|
+
signal: wrap_ctx[:return_signal],
|
58
|
+
snapshot_before: snapshot_before, # add this so we know who belongs together.
|
59
|
+
}
|
60
|
+
|
61
|
+
return data, new_versions
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.snapshot_for(ctx, value_snapshooter:, stack:, **)
|
65
|
+
variable_versions = stack.variable_versions
|
66
|
+
|
67
|
+
variable_versions.changeset_for(ctx, value_snapshooter: value_snapshooter) # return {changeset, new_versions}
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -3,17 +3,30 @@ module Trailblazer
|
|
3
3
|
module Trace
|
4
4
|
# The stack is a linear one-dimensional array. Per traced task two {Trace::Captured}
|
5
5
|
# elements get pushed onto it (unless there's an Exception).
|
6
|
+
#
|
7
|
+
# The Stack object maintains the snapshots and the variable versions. It should probably
|
8
|
+
# be named "Trace" :D
|
9
|
+
# It is by design coupled to both Snapshot and Ctx::Versions.
|
6
10
|
class Stack
|
7
|
-
def initialize(
|
8
|
-
@
|
11
|
+
def initialize(snapshots = [], variable_versions = Snapshot::Versions.new)
|
12
|
+
@snapshots = snapshots
|
13
|
+
@variable_versions = variable_versions # DISCUSS: I dislike the coupling here to Stack, but introducting another object comprised of Stack and VariableVersions seems overkill.
|
9
14
|
end
|
10
15
|
|
11
|
-
|
12
|
-
|
16
|
+
attr_reader :variable_versions # TODO: the accessor sucks. But I guess to_h[:variable_versions] is slower.
|
17
|
+
|
18
|
+
def add!(snapshot, new_variable_versions)
|
19
|
+
# variable_versions is mutated in the snapshooter, that's
|
20
|
+
# why we don't have to re-set it here. I'm not a huge fan of mutating it
|
21
|
+
# in a deeply nested scenario but everything else we played with added huge amounts
|
22
|
+
# or runtime code.
|
23
|
+
@variable_versions.add_changes!(new_variable_versions)
|
24
|
+
|
25
|
+
@snapshots << snapshot
|
13
26
|
end
|
14
27
|
|
15
28
|
def to_a
|
16
|
-
@
|
29
|
+
@snapshots
|
17
30
|
end
|
18
31
|
end # Stack
|
19
32
|
end
|
@@ -1,10 +1,11 @@
|
|
1
1
|
module Trailblazer::Developer
|
2
2
|
module Trace
|
3
|
-
|
4
3
|
class << self
|
5
|
-
# Public entry point to
|
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.
|
6
7
|
def call(activity, (ctx, flow_options), **circuit_options)
|
7
|
-
activity, (ctx, flow_options), circuit_options = Trace.arguments_for_call(
|
8
|
+
activity, (ctx, flow_options), circuit_options = Trace.arguments_for_call(activity, [ctx, flow_options], **circuit_options) # only run once for the entire circuit!
|
8
9
|
|
9
10
|
signal, (ctx, flow_options) = Trailblazer::Activity::TaskWrap.invoke(activity, [ctx, flow_options], **circuit_options)
|
10
11
|
|
@@ -15,9 +16,10 @@ module Trailblazer::Developer
|
|
15
16
|
|
16
17
|
def arguments_for_call(activity, (options, original_flow_options), **original_circuit_options)
|
17
18
|
default_flow_options = {
|
18
|
-
stack:
|
19
|
-
|
20
|
-
|
19
|
+
stack: Trace::Stack.new,
|
20
|
+
before_snapshooter: Snapshot.method(:before_snapshooter),
|
21
|
+
after_snapshooter: Snapshot.method(:after_snapshooter),
|
22
|
+
value_snapshooter: Trace.value_snapshooter
|
21
23
|
}
|
22
24
|
|
23
25
|
flow_options = {**default_flow_options, **Hash(original_flow_options)}
|
@@ -32,79 +34,52 @@ module Trailblazer::Developer
|
|
32
34
|
end
|
33
35
|
end
|
34
36
|
|
37
|
+
@value_snapshooter = Trace::Snapshot::Value.build()
|
38
|
+
singleton_class.attr_reader :value_snapshooter # NOTE: this is semi-private.
|
39
|
+
|
35
40
|
module_function
|
36
|
-
|
37
|
-
# and before the TaskWrap is finished.
|
38
|
-
#
|
41
|
+
|
39
42
|
# @private
|
40
43
|
def task_wrap_extensions
|
41
|
-
|
42
|
-
[Trace.method(:capture_args), id: "task_wrap.capture_args", prepend: "task_wrap.call_task"],
|
43
|
-
[Trace.method(:capture_return), id: "task_wrap.capture_return", append: nil], # append to the very end of tW.
|
44
|
-
)
|
44
|
+
TASK_WRAP_EXTENSION
|
45
45
|
end
|
46
46
|
|
47
|
+
# Snapshot::Before and After are a generic concept of Trace, as
|
48
|
+
# they're the interface to Trace::Present, WTF, and Debugger.
|
49
|
+
|
47
50
|
# It's important to understand that {flow[:stack]} is mutated by design. This is needed so
|
48
51
|
# in case of exceptions we still have a "global" trace - unfortunately Ruby doesn't allow
|
49
52
|
# us a better way.
|
50
53
|
# taskWrap step to capture incoming arguments of a step.
|
51
|
-
|
52
|
-
|
54
|
+
#
|
55
|
+
# Note that we save the created {Snapshot::Before} in the wrap_ctx.
|
56
|
+
def capture_args(wrap_config, original_args)
|
57
|
+
flow_options = original_args[0][1]
|
53
58
|
|
54
|
-
|
59
|
+
snapshot, new_versions = Snapshot::Before.(flow_options[:before_snapshooter], wrap_config, original_args)
|
55
60
|
|
56
|
-
|
61
|
+
# We try to be generic here in the taskWrap snapshooting code, where details happen in Snapshot::Before/After and Stack#add!.
|
62
|
+
flow_options[:stack].add!(snapshot, new_versions)
|
57
63
|
|
58
|
-
return wrap_config, original_args
|
64
|
+
return wrap_config.merge(snapshot_before: snapshot), original_args
|
59
65
|
end
|
60
66
|
|
61
67
|
# taskWrap step to capture outgoing arguments from a step.
|
62
|
-
def capture_return(wrap_config, ((ctx,
|
63
|
-
original_args = [[ctx,
|
68
|
+
def capture_return(wrap_config, ((ctx, flow_options), circuit_options))
|
69
|
+
original_args = [[ctx, flow_options], circuit_options]
|
64
70
|
|
65
|
-
|
71
|
+
snapshot, new_versions = Snapshot::After.(flow_options[:after_snapshooter], wrap_config, original_args)
|
66
72
|
|
67
|
-
|
73
|
+
flow_options[:stack].add!(snapshot, new_versions)
|
68
74
|
|
69
75
|
return wrap_config, original_args
|
70
76
|
end
|
71
77
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
collected_data
|
79
|
-
).freeze
|
80
|
-
end
|
81
|
-
|
82
|
-
# Called in {#Captured}.
|
83
|
-
# DISCUSS: this is where to start for a new {Inspector} implementation.
|
84
|
-
def default_input_data_collector(wrap_config, ((ctx, _), _)) # DISCUSS: would it be faster to access ctx via {original_args[0][0]}?
|
85
|
-
# mutable, old_ctx = ctx.decompose
|
86
|
-
# mutable, old_ctx = ctx, nil
|
87
|
-
|
88
|
-
{
|
89
|
-
# ctx: ctx.to_h.freeze,
|
90
|
-
ctx_snapshot: ctx.to_h.collect { |k,v| [k, v.inspect] }.to_h,
|
91
|
-
} # TODO: proper snapshot!
|
92
|
-
end
|
93
|
-
|
94
|
-
# Called in {#Captured}.
|
95
|
-
def default_output_data_collector(wrap_config, ((ctx, _), _))
|
96
|
-
returned_ctx, _ = wrap_config[:return_args]
|
97
|
-
|
98
|
-
# FIXME: snapshot!
|
99
|
-
{
|
100
|
-
# ctx: ctx.to_h.freeze,
|
101
|
-
ctx_snapshot: returned_ctx.to_h.collect { |k,v| [k, v.inspect] }.to_h,
|
102
|
-
signal: wrap_config[:return_signal]
|
103
|
-
}
|
104
|
-
end
|
105
|
-
|
106
|
-
Captured = Struct.new(:task, :activity, :data)
|
107
|
-
Captured::Input = Class.new(Captured)
|
108
|
-
Captured::Output = Class.new(Captured)
|
78
|
+
# Insertions for the trace tasks that capture the arguments just before calling the task,
|
79
|
+
# and before the TaskWrap is finished.
|
80
|
+
TASK_WRAP_EXTENSION = Trailblazer::Activity::TaskWrap.Extension(
|
81
|
+
[Trace.method(:capture_args), id: "task_wrap.capture_args", prepend: "task_wrap.call_task"],
|
82
|
+
[Trace.method(:capture_return), id: "task_wrap.capture_return", append: nil], # append to the very end of tW.
|
83
|
+
)
|
109
84
|
end
|
110
85
|
end
|
@@ -14,21 +14,21 @@ module Trailblazer::Developer
|
|
14
14
|
|
15
15
|
module_function
|
16
16
|
|
17
|
-
# {options} can be {style: {#<
|
18
|
-
def call(
|
19
|
-
label = styled_label(
|
17
|
+
# {options} can be {style: {#<Debugger::Node> => [:red, :bold]}}
|
18
|
+
def call(debugger_trace:, debugger_node:, style: {}, **options)
|
19
|
+
label = styled_label(debugger_trace, debugger_node, style: style, **options)
|
20
20
|
|
21
21
|
[debugger_node.level, label]
|
22
22
|
end
|
23
23
|
|
24
|
-
def styled_label(
|
24
|
+
def styled_label(debugger_trace, debugger_node, color_map:, **options)
|
25
25
|
label = apply_style(debugger_node.label, debugger_node, **options)
|
26
26
|
|
27
|
-
%{#{fmt(label, color_map[
|
27
|
+
%{#{fmt(label, color_map[signal_of(debugger_node)])}}
|
28
28
|
end
|
29
29
|
|
30
30
|
def apply_style(label, debugger_node, style:, **)
|
31
|
-
return label unless styles = style[debugger_node.
|
31
|
+
return label unless styles = style[debugger_node.trace_node]
|
32
32
|
|
33
33
|
styles.each { |s| label = fmt(label, s) }
|
34
34
|
label
|
@@ -42,8 +42,8 @@ module Trailblazer::Developer
|
|
42
42
|
String.send(style, line)
|
43
43
|
end
|
44
44
|
|
45
|
-
def signal_of(
|
46
|
-
entity_signal =
|
45
|
+
def signal_of(debugger_node)
|
46
|
+
entity_signal = debugger_node.incomplete? ? nil : debugger_node.snapshot_after.data[:signal]
|
47
47
|
entity_klass = entity_signal.is_a?(Class) ? entity_signal : entity_signal.class
|
48
48
|
|
49
49
|
SIGNALS_MAP[entity_klass.name.to_sym]
|
@@ -16,62 +16,62 @@ module Trailblazer::Developer
|
|
16
16
|
# This allows to display the trace even when an exception happened
|
17
17
|
def invoke(activity, (ctx, flow_options), present_options: {}, **circuit_options)
|
18
18
|
flow_options ||= {}
|
19
|
+
local_present_options_block = ->(*) { {} }
|
19
20
|
|
20
21
|
stack = Trace::Stack.new # unfortunately, we need this mutable object before things break.
|
22
|
+
raise_exception = false
|
23
|
+
|
24
|
+
begin
|
25
|
+
complete_stack, signal, (ctx, flow_options) = Trace.invoke(
|
26
|
+
activity,
|
27
|
+
[ctx, flow_options.merge(stack: stack)],
|
28
|
+
**circuit_options
|
29
|
+
)
|
30
|
+
rescue
|
31
|
+
raise_exception = $! # TODO: will this show the very same stacktrace?
|
32
|
+
|
33
|
+
exception_source = Exception.find_exception_source(stack, $!)
|
34
|
+
complete_stack = stack
|
35
|
+
|
36
|
+
local_present_options_block = ->(trace_nodes:, **) {
|
37
|
+
exception_source_node = trace_nodes.reverse.find do |trace_node|
|
38
|
+
trace_node.snapshot_after == exception_source || trace_node.snapshot_before == exception_source
|
39
|
+
end
|
21
40
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
# However, what if an error happens in, say, an input filter? TODO: test this
|
37
|
-
|
38
|
-
complete_stack = Exception::Stack.complete(incomplete_stack) # TODO: only in case of exception!
|
39
|
-
|
40
|
-
puts Trace::Present.(
|
41
|
+
{
|
42
|
+
# we can hand in options per node, identified by their captured_input part.
|
43
|
+
node_options: {
|
44
|
+
exception_source_node => {data: {exception_source: true}}, # goes to {Debugger::Node.build}
|
45
|
+
},
|
46
|
+
style: {
|
47
|
+
exception_source_node => [:red, :bold]
|
48
|
+
},
|
49
|
+
}
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
# always render the trace.
|
54
|
+
output, returned_args = Trace::Present.(
|
41
55
|
complete_stack,
|
42
|
-
# we can hand in options per node, identified by their captured_input part.
|
43
|
-
node_options: {
|
44
|
-
exception_source => {data: {exception_source: true}}, # goes to {Debugger::Node.build}
|
45
|
-
},
|
46
|
-
|
47
56
|
renderer: Wtf::Renderer,
|
48
|
-
color_map: Wtf::Renderer::DEFAULT_COLOR_MAP.merge(
|
49
|
-
|
50
|
-
**present_options,
|
57
|
+
color_map: Wtf::Renderer::DEFAULT_COLOR_MAP.merge(flow_options[:color_map] || {}),
|
58
|
+
activity: activity,
|
59
|
+
**present_options,
|
60
|
+
&local_present_options_block
|
51
61
|
)
|
52
|
-
end
|
53
|
-
|
54
|
-
module Exception
|
55
|
-
# When an exception occurs the Stack instance is incomplete - it is missing Captured::Output instances
|
56
|
-
# for Inputs still open. This method adds the missing elements so the Trace::Tree algorithm doesn't crash.
|
57
|
-
module Stack
|
58
|
-
def self.complete(incomplete_stack)
|
59
|
-
processed = []
|
60
62
|
|
61
|
-
|
62
|
-
if captured.is_a?(Trace::Captured::Input)
|
63
|
-
processed << captured
|
64
|
-
else
|
65
|
-
processed.pop
|
66
|
-
end
|
67
|
-
end
|
63
|
+
puts output
|
68
64
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
end
|
73
|
-
end # Stack
|
65
|
+
raise raise_exception if raise_exception
|
66
|
+
return signal, [ctx, flow_options], circuit_options, output, returned_args
|
67
|
+
end
|
74
68
|
|
69
|
+
module Exception
|
70
|
+
def self.find_exception_source(stack, exception)
|
71
|
+
# in 99%, exception_source is a {Snapshot::Before}.
|
72
|
+
exception_source = stack.to_a.last # DISCUSS: in most cases, this is where the problem has happened.
|
73
|
+
# However, what if an error happens in, say, an input filter? TODO: test this
|
74
|
+
end
|
75
75
|
end
|
76
76
|
end
|
77
77
|
end
|
@@ -9,14 +9,19 @@ end
|
|
9
9
|
require "trailblazer/activity"
|
10
10
|
require "trailblazer/developer/wtf"
|
11
11
|
require "trailblazer/developer/wtf/renderer"
|
12
|
+
require "trailblazer/developer/trace/snapshot"
|
13
|
+
require "trailblazer/developer/trace/snapshot/value"
|
14
|
+
require "trailblazer/developer/trace/snapshot/versions"
|
12
15
|
require "trailblazer/developer/trace"
|
13
16
|
require "trailblazer/developer/trace/stack"
|
14
|
-
require "trailblazer/developer/trace/
|
17
|
+
require "trailblazer/developer/trace/node"
|
18
|
+
require "trailblazer/developer/trace/parent_map"
|
15
19
|
require "trailblazer/developer/trace/present"
|
16
|
-
require "trailblazer/developer/
|
20
|
+
require "trailblazer/developer/debugger"
|
17
21
|
require "trailblazer/developer/render/circuit"
|
18
22
|
require "trailblazer/developer/render/linear"
|
19
23
|
require "trailblazer/developer/render/task_wrap"
|
20
24
|
require "trailblazer/developer/introspect" # TODO: might get removed, again.
|
21
|
-
require "trailblazer/developer/
|
25
|
+
require "trailblazer/developer/debugger/normalizer"
|
22
26
|
require "trailblazer/developer/introspect/graph"
|
27
|
+
Trailblazer::Developer::Trace::Debugger = Trailblazer::Developer::Debugger # FIXME: deprecate constant!
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trailblazer-developer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Sutterer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-06-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -131,17 +131,21 @@ files:
|
|
131
131
|
- bin/console
|
132
132
|
- bin/setup
|
133
133
|
- lib/trailblazer/developer.rb
|
134
|
+
- lib/trailblazer/developer/debugger.rb
|
135
|
+
- lib/trailblazer/developer/debugger/normalizer.rb
|
134
136
|
- lib/trailblazer/developer/introspect.rb
|
135
137
|
- lib/trailblazer/developer/introspect/graph.rb
|
136
138
|
- lib/trailblazer/developer/render/circuit.rb
|
137
139
|
- lib/trailblazer/developer/render/linear.rb
|
138
140
|
- lib/trailblazer/developer/render/task_wrap.rb
|
139
141
|
- lib/trailblazer/developer/trace.rb
|
140
|
-
- lib/trailblazer/developer/trace/
|
141
|
-
- lib/trailblazer/developer/trace/
|
142
|
+
- lib/trailblazer/developer/trace/node.rb
|
143
|
+
- lib/trailblazer/developer/trace/parent_map.rb
|
142
144
|
- lib/trailblazer/developer/trace/present.rb
|
145
|
+
- lib/trailblazer/developer/trace/snapshot.rb
|
146
|
+
- lib/trailblazer/developer/trace/snapshot/value.rb
|
147
|
+
- lib/trailblazer/developer/trace/snapshot/versions.rb
|
143
148
|
- lib/trailblazer/developer/trace/stack.rb
|
144
|
-
- lib/trailblazer/developer/trace/tree.rb
|
145
149
|
- lib/trailblazer/developer/version.rb
|
146
150
|
- lib/trailblazer/developer/wtf.rb
|
147
151
|
- lib/trailblazer/developer/wtf/renderer.rb
|
@@ -1,68 +0,0 @@
|
|
1
|
-
module Trailblazer
|
2
|
-
module Developer
|
3
|
-
module Trace
|
4
|
-
module Debugger
|
5
|
-
# @private
|
6
|
-
# Public entry point to add Debugger::Node normalizer steps.
|
7
|
-
def self.add_normalizer_step!(step, id:, normalizer: Normalizer::PIPELINES.last, **options)
|
8
|
-
task = Normalizer.Task(step)
|
9
|
-
|
10
|
-
# We have a TaskWrap::Pipeline (a very simple style of "activity" used for normalizers) and
|
11
|
-
# add another step using the "friendly interface" from {Activity::Adds}.
|
12
|
-
options = {append: nil} unless options.any?
|
13
|
-
|
14
|
-
pipeline_extension = Activity::TaskWrap::Extension.build([task, id: id, **options])
|
15
|
-
|
16
|
-
Normalizer::PIPELINES << pipeline_extension.(normalizer)
|
17
|
-
end
|
18
|
-
|
19
|
-
module Normalizer
|
20
|
-
def self.Task(user_step) # TODO: we could keep this in the {activity} gem.
|
21
|
-
Activity::TaskWrap::Pipeline::TaskAdapter.for_step(user_step, option: false) # we don't need Option as we don't have ciruit_options here, and no {:exec_context}
|
22
|
-
end
|
23
|
-
|
24
|
-
# Default steps for the Debugger::Node options pipeline, following the step-interface.
|
25
|
-
module Default
|
26
|
-
def self.compile_id(ctx, activity:, task:, **)
|
27
|
-
ctx[:compile_id] = Activity::Introspect.Nodes(activity, task: task)[:id]
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.compile_path(ctx, parent_map:, captured_node:, **)
|
31
|
-
ctx[:compile_path] = Trace::Tree::ParentMap.path_for(parent_map, captured_node)
|
32
|
-
end
|
33
|
-
|
34
|
-
def self.runtime_id(ctx, compile_id:, **)
|
35
|
-
ctx[:runtime_id] = compile_id
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.runtime_path(ctx, runtime_id:, compile_path:, **)
|
39
|
-
return ctx[:runtime_path] = compile_path if compile_path.empty? # FIXME: this currently only applies to root.
|
40
|
-
|
41
|
-
ctx[:runtime_path] = compile_path[0..-2] + [runtime_id]
|
42
|
-
end
|
43
|
-
|
44
|
-
def self.label(ctx, label: nil, runtime_id:, **)
|
45
|
-
ctx[:label] = label || runtime_id
|
46
|
-
end
|
47
|
-
|
48
|
-
def self.data(ctx, data: {}, **)
|
49
|
-
ctx[:data] = data
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
default_steps = {
|
54
|
-
compile_id: Normalizer.Task(Default.method(:compile_id)),
|
55
|
-
compile_path: Normalizer.Task(Default.method(:compile_path)),
|
56
|
-
runtime_id: Normalizer.Task(Default.method(:runtime_id)),
|
57
|
-
runtime_path: Normalizer.Task(Default.method(:runtime_path)),
|
58
|
-
label: Normalizer.Task(Default.method(:label)),
|
59
|
-
data: Normalizer.Task(Default.method(:data)),
|
60
|
-
}.
|
61
|
-
collect { |id, task| Activity::TaskWrap::Pipeline.Row(id, task) }
|
62
|
-
|
63
|
-
PIPELINES = [Activity::TaskWrap::Pipeline.new(default_steps)] # we do mutate this constant at compile-time. Maybe # DISCUSS and find a better way.
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
@@ -1,72 +0,0 @@
|
|
1
|
-
module Trailblazer
|
2
|
-
module Developer
|
3
|
-
module Trace
|
4
|
-
module Debugger
|
5
|
-
class Node < Struct.new(:captured_node, :task, :activity, :compile_id, :compile_path, :runtime_id, :runtime_path, :label, :data, :captured_input, :captured_output, :level, keyword_init: true)
|
6
|
-
# The idea is to only work with {Activity} instances on this level, as that's the runtime concept.
|
7
|
-
|
8
|
-
# TODO: class, "type",
|
9
|
-
# which track, return signal, etc
|
10
|
-
|
11
|
-
|
12
|
-
# we always key options for specific nodes by Stack::Captured::Input, so we don't confuse activities if they were called multiple times.
|
13
|
-
def self.build(tree, enumerable_tree, node_options: {}, normalizer: Debugger::Normalizer::PIPELINES.last, **options_for_nodes)
|
14
|
-
parent_map = Trace::Tree::ParentMap.build(tree).to_h # DISCUSS: can we use {enumerable_tree} for {ParentMap}?
|
15
|
-
|
16
|
-
|
17
|
-
container_activity = enumerable_tree[0].captured_input.activity # TODO: any other way to grab the container_activity? Maybe via {activity.container_activity}?
|
18
|
-
|
19
|
-
top_activity = enumerable_tree[0].captured_input.task
|
20
|
-
|
21
|
-
# DISCUSS: this might change if we introduce a new Node type for Trace.
|
22
|
-
debugger_nodes = enumerable_tree[0..-1].collect do |node|
|
23
|
-
activity = node.captured_input.activity
|
24
|
-
task = node.captured_input.task
|
25
|
-
# it's possible to pass per-node options, like {label: "Yo!"} via {:node_options[<captured_input>]}
|
26
|
-
options = node_options[node.captured_input] || {}
|
27
|
-
|
28
|
-
|
29
|
-
options_for_debugger_node, _ = normalizer.(
|
30
|
-
{
|
31
|
-
captured_node: node,
|
32
|
-
task: task,
|
33
|
-
activity: activity,
|
34
|
-
parent_map: parent_map,
|
35
|
-
**options
|
36
|
-
},
|
37
|
-
[]
|
38
|
-
)
|
39
|
-
|
40
|
-
options_for_debugger_node = options_for_debugger_node.slice(*(options_for_debugger_node.keys - [:parent_map]))
|
41
|
-
|
42
|
-
# these attributes are not changing with the presentation
|
43
|
-
Debugger::Node.new(
|
44
|
-
captured_node: node,
|
45
|
-
activity: activity,
|
46
|
-
task: task,
|
47
|
-
|
48
|
-
level: node.level,
|
49
|
-
captured_input: node.captured_input,
|
50
|
-
captured_output: node.captured_output,
|
51
|
-
|
52
|
-
**options_for_debugger_node,
|
53
|
-
).freeze
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def self.build_for_stack(stack, **options_for_debugger_nodes)
|
58
|
-
tree, processed = Trace.Tree(stack.to_a)
|
59
|
-
|
60
|
-
enumerable_tree = Trace::Tree.Enumerable(tree)
|
61
|
-
|
62
|
-
Debugger::Node.build(
|
63
|
-
tree,
|
64
|
-
enumerable_tree,
|
65
|
-
**options_for_debugger_nodes,
|
66
|
-
)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end # Debugger
|
70
|
-
end # Trace
|
71
|
-
end
|
72
|
-
end
|