trailblazer-developer 0.0.8 → 0.0.13
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/README.md +1 -1
- data/lib/trailblazer/developer.rb +3 -0
- data/lib/trailblazer/developer/CHANGES.md +24 -0
- data/lib/trailblazer/developer/generate.rb +24 -12
- data/lib/trailblazer/developer/render/circuit.rb +2 -2
- data/lib/trailblazer/developer/trace.rb +44 -26
- data/lib/trailblazer/developer/trace/focusable.rb +80 -0
- data/lib/trailblazer/developer/trace/inspector.rb +48 -0
- data/lib/trailblazer/developer/trace/present.rb +24 -22
- data/lib/trailblazer/developer/version.rb +1 -1
- data/lib/trailblazer/developer/wtf.rb +54 -71
- data/lib/trailblazer/developer/wtf/renderer.rb +78 -0
- data/trailblazer-developer.gemspec +1 -0
- metadata +20 -5
- data/lib/trailblazer/developer/activity.rb +0 -51
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dcc1a32164a4c3e26c3d72450e416d8c3259002f2ce94782c808e4c041cb7847
|
|
4
|
+
data.tar.gz: fb0bede8fcfed4c533dec425f9f92b8188fd4df6ce82c39543d21d7440b7c1fc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d6661e0492929cf37bbf40afbbd61d79308f113005040a61515801d72b7731adce960245d618ccb5a7db4903e082f5ae1b2a60fe139006f978e4f13ab02cf4a7
|
|
7
|
+
data.tar.gz: b7b1581c7733ca0066eafb8877c06d9ca7175854c56d8fa62509f384881c772a84b7dce4ba4dacd547710d4a3c1ed04da9dc9d15f7571e8852fb8c11778d4f35
|
data/README.md
CHANGED
|
@@ -4,7 +4,7 @@ _Developer tools for Trailblazers._
|
|
|
4
4
|
|
|
5
5
|
## Documentation
|
|
6
6
|
|
|
7
|
-
Find the complete documentation on the project website: [http://
|
|
7
|
+
Find the complete documentation on the project website: [http://2019.trailblazer.to/2.1/docs/developer.html]
|
|
8
8
|
|
|
9
9
|
## Summary
|
|
10
10
|
|
|
@@ -7,8 +7,11 @@ module Trailblazer
|
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
require "trailblazer/developer/wtf"
|
|
10
|
+
require "trailblazer/developer/wtf/renderer"
|
|
10
11
|
require "trailblazer/developer/trace"
|
|
11
12
|
require "trailblazer/developer/trace/present"
|
|
13
|
+
require "trailblazer/developer/trace/focusable"
|
|
14
|
+
require "trailblazer/developer/trace/inspector"
|
|
12
15
|
require "trailblazer/developer/generate"
|
|
13
16
|
require "trailblazer/developer/render/circuit"
|
|
14
17
|
require "trailblazer/developer/render/linear"
|
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
# 0.0.13
|
|
2
|
+
|
|
3
|
+
* Allow focusing on selected variables for all steps in wtf
|
|
4
|
+
* Fix declarative deprecation warning
|
|
5
|
+
|
|
6
|
+
# 0.0.12
|
|
7
|
+
|
|
8
|
+
* Revert Hash#compact usage to support Rubies <= 2.4
|
|
9
|
+
|
|
10
|
+
# 0.0.11
|
|
11
|
+
|
|
12
|
+
* Allow injecting custom data collector in Trace API, to collect custom input/output ctx of task nodes.
|
|
13
|
+
* Allow focusing on specfic steps and ctx variables in Dev.wtf?
|
|
14
|
+
* Allow custom inspection while tracing using Inspector definations
|
|
15
|
+
|
|
16
|
+
# 0.0.10
|
|
17
|
+
|
|
18
|
+
* Make Generate::Pipeline an activity for better customization/extendability.
|
|
19
|
+
|
|
20
|
+
# 0.0.9
|
|
21
|
+
|
|
22
|
+
* `Render.strip` is now a class method.
|
|
23
|
+
* Added the `.type` field to the `data` field in `Generate`.
|
|
24
|
+
|
|
1
25
|
# 0.0.8
|
|
2
26
|
|
|
3
27
|
* Fix `Introspect` references.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require "representable/hash"
|
|
2
|
+
require "trailblazer/activity/dsl/linear" # Railway.
|
|
2
3
|
|
|
3
4
|
module Trailblazer
|
|
4
5
|
module Developer
|
|
@@ -16,7 +17,7 @@ module Trailblazer
|
|
|
16
17
|
collection :elements, class: Element do
|
|
17
18
|
property :id
|
|
18
19
|
property :type
|
|
19
|
-
collection :linksTo, class: Arrow, default: [] do
|
|
20
|
+
collection :linksTo, class: Arrow, default: -> { [] } do
|
|
20
21
|
property :target
|
|
21
22
|
property :label
|
|
22
23
|
property :message
|
|
@@ -30,26 +31,27 @@ module Trailblazer
|
|
|
30
31
|
end
|
|
31
32
|
|
|
32
33
|
def call(hash)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
compute_intermediate(elements)
|
|
34
|
+
signal, (ctx, _) = Activity::TaskWrap.invoke(Pipeline, hash: hash)
|
|
35
|
+
ctx[:intermediate]
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
def transform_from_hash(
|
|
39
|
-
parser.new(OpenStruct.new).from_hash(hash).elements
|
|
38
|
+
def transform_from_hash(ctx, hash:, parser: Representer::Activity, **)
|
|
39
|
+
ctx[:elements] = parser.new(OpenStruct.new).from_hash(hash).elements
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
-
def find_start_events(elements)
|
|
43
|
-
elements.find_all { |el| el.type == "Event" }
|
|
42
|
+
def find_start_events(ctx, elements:, **)
|
|
43
|
+
ctx[:start_events] = elements.find_all { |el| el.type == "Event" }
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
def compute_intermediate(
|
|
47
|
-
start_events = find_start_events.(elements)
|
|
46
|
+
def compute_intermediate(ctx, elements:, start_events:, **)
|
|
48
47
|
end_events = elements.find_all { |el| el.type == "EndEventTerminate" } # DISCUSS: is it really called TERMINATE?
|
|
49
48
|
|
|
50
49
|
inter = Activity::Schema::Intermediate
|
|
51
50
|
|
|
52
|
-
wiring = elements.collect { |el|
|
|
51
|
+
wiring = elements.collect { |el|
|
|
52
|
+
data = data_for(el)
|
|
53
|
+
|
|
54
|
+
[inter.TaskRef(el.id, data), el.linksTo.collect { |arrow| inter.Out(semantic_for(arrow.to_h), arrow.target) } ] }
|
|
53
55
|
wiring = Hash[wiring]
|
|
54
56
|
|
|
55
57
|
# end events need this stupid special handling
|
|
@@ -63,11 +65,15 @@ module Trailblazer
|
|
|
63
65
|
])
|
|
64
66
|
# pp wiring
|
|
65
67
|
|
|
66
|
-
inter.new(wiring, end_events.collect(&:id), start_events.collect(&:id))
|
|
68
|
+
ctx[:intermediate] = inter.new(wiring, end_events.collect(&:id), start_events.collect(&:id))
|
|
67
69
|
end
|
|
68
70
|
|
|
69
71
|
# private
|
|
70
72
|
|
|
73
|
+
def data_for(element)
|
|
74
|
+
{type: element.type}.merge(element.data)
|
|
75
|
+
end
|
|
76
|
+
|
|
71
77
|
# We currently use the {:label} field of an arrow to encode an output semantic.
|
|
72
78
|
# The {:symbol_style} part will be filtered out as semantic. Defaults to {:success}.
|
|
73
79
|
def semantic_for(label:nil, **)
|
|
@@ -79,6 +85,12 @@ module Trailblazer
|
|
|
79
85
|
def extract_semantic(label)
|
|
80
86
|
label.to_sym
|
|
81
87
|
end
|
|
88
|
+
|
|
89
|
+
class Pipeline < Trailblazer::Activity::Railway
|
|
90
|
+
step Generate.method(:transform_from_hash), id: :transform_from_hash
|
|
91
|
+
step Generate.method(:find_start_events), id: :find_start_events
|
|
92
|
+
step Generate.method(:compute_intermediate), id: :compute_intermediate
|
|
93
|
+
end
|
|
82
94
|
end
|
|
83
95
|
end
|
|
84
96
|
end
|
|
@@ -42,13 +42,13 @@ module Trailblazer
|
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
def inspect_end(task)
|
|
45
|
-
class_name = strip(task.class)
|
|
45
|
+
class_name = Render::Circuit.strip(task.class)
|
|
46
46
|
options = task.to_h
|
|
47
47
|
|
|
48
48
|
"#<#{class_name}/#{options[:semantic].inspect}>"
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
-
def strip(string)
|
|
51
|
+
def self.strip(string)
|
|
52
52
|
string.to_s.sub("Trailblazer::Activity::", "")
|
|
53
53
|
end
|
|
54
54
|
end
|
|
@@ -17,16 +17,23 @@ module Trailblazer::Developer
|
|
|
17
17
|
|
|
18
18
|
alias_method :invoke, :call
|
|
19
19
|
|
|
20
|
-
def arguments_for_call(activity, (options,
|
|
21
|
-
|
|
20
|
+
def arguments_for_call(activity, (options, original_flow_options), **original_circuit_options)
|
|
21
|
+
default_flow_options = {
|
|
22
22
|
stack: Trace::Stack.new,
|
|
23
|
+
|
|
24
|
+
input_data_collector: Trace.method(:default_input_data_collector),
|
|
25
|
+
output_data_collector: Trace.method(:default_output_data_collector),
|
|
23
26
|
}
|
|
24
27
|
|
|
25
|
-
|
|
28
|
+
flow_options = { **default_flow_options, **Hash( original_flow_options ) }
|
|
29
|
+
|
|
30
|
+
default_circuit_options = {
|
|
26
31
|
wrap_runtime: ::Hash.new(Trace.merge_plan), # DISCUSS: this overrides existing {:wrap_runtime}.
|
|
27
32
|
}
|
|
28
33
|
|
|
29
|
-
|
|
34
|
+
circuit_options = { **original_circuit_options, **default_circuit_options }
|
|
35
|
+
|
|
36
|
+
return activity, [ options, flow_options ], circuit_options
|
|
30
37
|
end
|
|
31
38
|
end
|
|
32
39
|
|
|
@@ -42,38 +49,45 @@ module Trailblazer::Developer
|
|
|
42
49
|
)
|
|
43
50
|
end
|
|
44
51
|
|
|
52
|
+
# It's important to understand that {flow[:stack]} is mutated by design. This is needed so
|
|
53
|
+
# in case of exceptions we still have a "global" trace - unfortunately Ruby doesn't allow
|
|
54
|
+
# us a better way.
|
|
45
55
|
# taskWrap step to capture incoming arguments of a step.
|
|
46
|
-
def capture_args(wrap_config,
|
|
47
|
-
|
|
56
|
+
def capture_args(wrap_config, ((ctx, flow), circuit_options))
|
|
57
|
+
flow[:stack].indent!
|
|
48
58
|
|
|
49
|
-
|
|
59
|
+
flow[:stack] << Entity::Input.new(
|
|
60
|
+
wrap_config[:task],
|
|
61
|
+
circuit_options[:activity],
|
|
62
|
+
flow[:input_data_collector].call(wrap_config, [ctx, flow], circuit_options)
|
|
63
|
+
).freeze
|
|
64
|
+
|
|
65
|
+
return wrap_config, [[ctx, flow], circuit_options]
|
|
50
66
|
end
|
|
51
67
|
|
|
52
68
|
# taskWrap step to capture outgoing arguments from a step.
|
|
53
|
-
def capture_return(wrap_config,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
69
|
+
def capture_return(wrap_config, ((ctx, flow), circuit_options))
|
|
70
|
+
flow[:stack] << Entity::Output.new(
|
|
71
|
+
wrap_config[:task],
|
|
72
|
+
{},
|
|
73
|
+
flow[:output_data_collector].call(wrap_config, [ctx, flow], circuit_options)
|
|
58
74
|
).freeze
|
|
59
75
|
|
|
60
|
-
|
|
76
|
+
flow[:stack].unindent!
|
|
61
77
|
|
|
62
|
-
|
|
63
|
-
return wrap_config, original_args
|
|
78
|
+
return wrap_config, [[ctx, flow], circuit_options]
|
|
64
79
|
end
|
|
65
80
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
flow[:stack].indent!
|
|
81
|
+
def default_input_data_collector(wrap_config, (ctx, _), circuit_options)
|
|
82
|
+
graph = Trailblazer::Activity::Introspect::Graph(circuit_options[:activity])
|
|
83
|
+
task = wrap_config[:task]
|
|
84
|
+
name = (node = graph.find { |node| node[:task] == task }) ? node[:id] : task
|
|
71
85
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
).freeze
|
|
86
|
+
{ ctx: ctx, task_name: name }
|
|
87
|
+
end
|
|
75
88
|
|
|
76
|
-
|
|
89
|
+
def default_output_data_collector(wrap_config, (ctx, _), _)
|
|
90
|
+
{ ctx: ctx, signal: wrap_config[:return_signal] }
|
|
77
91
|
end
|
|
78
92
|
|
|
79
93
|
# Structures used in {capture_args} and {capture_return}.
|
|
@@ -115,6 +129,8 @@ module Trailblazer::Developer
|
|
|
115
129
|
|
|
116
130
|
# Mutable/stateful per design. We want a (global) stack!
|
|
117
131
|
class Stack
|
|
132
|
+
attr_reader :top
|
|
133
|
+
|
|
118
134
|
def initialize
|
|
119
135
|
@nested = Level.new
|
|
120
136
|
@stack = [ @nested ]
|
|
@@ -129,8 +145,10 @@ module Trailblazer::Developer
|
|
|
129
145
|
@stack.pop
|
|
130
146
|
end
|
|
131
147
|
|
|
132
|
-
def <<(
|
|
133
|
-
|
|
148
|
+
def <<(entity)
|
|
149
|
+
@top = entity
|
|
150
|
+
|
|
151
|
+
current << entity
|
|
134
152
|
end
|
|
135
153
|
|
|
136
154
|
def to_a
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
require 'hirb'
|
|
2
|
+
|
|
3
|
+
module Trailblazer
|
|
4
|
+
module Developer
|
|
5
|
+
module Trace
|
|
6
|
+
|
|
7
|
+
module Focusable
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
# Get inspect of {focus_on.variables} or current {ctx}
|
|
11
|
+
def capture_variables_from(ctx, focus_on:, inspector: Trace::Inspector, **flow_options)
|
|
12
|
+
# ctx keys to be captured, for example [:current_user, :model, ....]
|
|
13
|
+
variables = (selected = focus_on[:variables]).any? ? selected : ctx.keys
|
|
14
|
+
|
|
15
|
+
variables.each_with_object({}) do |variable, result|
|
|
16
|
+
if variable.is_a?(Proc) # To allow deep key access from ctx
|
|
17
|
+
result[:Custom] = inspector.(variable.call(ctx), **flow_options)
|
|
18
|
+
else
|
|
19
|
+
result[variable] = inspector.(ctx[variable], **flow_options)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Generate Hirb's vertical table nodes from captured ctx of each step
|
|
25
|
+
# |-- some step name
|
|
26
|
+
# | |-- ********** Input **********
|
|
27
|
+
# message: "WTF!"
|
|
28
|
+
# seq: []
|
|
29
|
+
# | `-- ********** Output **********
|
|
30
|
+
# message: "WTF!"
|
|
31
|
+
# seq: [:a]
|
|
32
|
+
def tree_nodes_for(level, input:, output:, **options)
|
|
33
|
+
# TODO: Reverting `Hash#compact` usage as it is not supported in Ruby <= 2.4
|
|
34
|
+
# Once the support is droped, revert actual code with below and remove entity check.
|
|
35
|
+
# input_output_nodes = { Input: input, Output: output }.compact.collect do |table_header, entity|
|
|
36
|
+
|
|
37
|
+
input_output_nodes = { Input: input, Output: output }.collect do |table_header, entity|
|
|
38
|
+
next unless entity
|
|
39
|
+
next unless Array( entity.data[:focused_variables] ).any?
|
|
40
|
+
|
|
41
|
+
table = vertical_table_for(entity.data[:focused_variables], table_header: table_header)
|
|
42
|
+
Present::TreeNodes::Node.new(level + 1, table, input, output, options).freeze
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
input_output_nodes.compact
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @private
|
|
49
|
+
def vertical_table_for(focused_variables, table_header:)
|
|
50
|
+
patched_vertical_table.render(
|
|
51
|
+
Array[ focused_variables ],
|
|
52
|
+
description: nil,
|
|
53
|
+
table_header: table_header, # Custom option, not from Hirb
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Overrding `Hirb::Helpers::VerticalTable#render_rows` because there is no option
|
|
58
|
+
# to customize vertical table's row header :(
|
|
59
|
+
# We need it to print if given entity is Input/Output
|
|
60
|
+
#
|
|
61
|
+
# @private
|
|
62
|
+
def patched_vertical_table
|
|
63
|
+
table = Class.new(Hirb::Helpers::VerticalTable)
|
|
64
|
+
|
|
65
|
+
table.send(:define_method, :render_rows) do
|
|
66
|
+
longest_header = Hirb::String.size (@headers.values.sort_by {|e| Hirb::String.size(e) }.last || '')
|
|
67
|
+
stars = "*" * [(longest_header + (longest_header / 2)), 3].max
|
|
68
|
+
|
|
69
|
+
@rows.map do |row|
|
|
70
|
+
"#{stars} #{@options[:table_header]} #{stars}\n" +
|
|
71
|
+
@fields.map{ |f| "#{Hirb::String.rjust(@headers[f], longest_header)}: #{row[f]}" }.join("\n")
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
table
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module Trailblazer
|
|
2
|
+
module Developer
|
|
3
|
+
module Trace
|
|
4
|
+
|
|
5
|
+
# This module does the inspection of given `ctx` with deep traversal.
|
|
6
|
+
# It only gets called when focusing is going on (i.e. focus_on API).
|
|
7
|
+
module Inspector
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
def call(value, default_inspector: method(:default_inspector), **)
|
|
11
|
+
return hash_inspector(value, default_inspector: default_inspector) if value.is_a?(Hash)
|
|
12
|
+
return array_inspector(value, default_inspector: default_inspector) if value.is_a?(Array)
|
|
13
|
+
|
|
14
|
+
default_inspector.(value)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def hash_inspector(value, default_inspector:)
|
|
18
|
+
Hash[
|
|
19
|
+
value.collect do |key, nested_value|
|
|
20
|
+
[key, call(nested_value, default_inspector: default_inspector)]
|
|
21
|
+
end
|
|
22
|
+
]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def array_inspector(value, default_inspector:)
|
|
26
|
+
value.collect do |nested_value|
|
|
27
|
+
call(nested_value, default_inspector: default_inspector)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# To avoid additional query that AR::Relation#inspect makes,
|
|
32
|
+
# we're calling AR::Relation#to_sql to get plain SQL string instead.
|
|
33
|
+
def activerecord_relation_inspector(value)
|
|
34
|
+
{ query: value.to_sql }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def default_inspector(value)
|
|
38
|
+
if defined?(ActiveRecord) && value.is_a?(ActiveRecord::Relation)
|
|
39
|
+
return activerecord_relation_inspector(value)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
value.inspect
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
require '
|
|
1
|
+
require 'hirb'
|
|
2
2
|
|
|
3
3
|
module Trailblazer::Developer
|
|
4
4
|
module Trace
|
|
5
5
|
module Present
|
|
6
6
|
module_function
|
|
7
7
|
|
|
8
|
-
INDENTATION = " |".freeze
|
|
9
|
-
STEP_PREFIX = "-- ".freeze
|
|
10
|
-
|
|
11
8
|
def default_renderer(task_node:, **)
|
|
12
|
-
[ task_node
|
|
9
|
+
[ task_node.level, %{#{task_node.value}} ]
|
|
13
10
|
end
|
|
14
11
|
|
|
15
12
|
def call(stack, level: 1, tree: [], renderer: method(:default_renderer), **options)
|
|
@@ -23,29 +20,14 @@ module Trailblazer::Developer
|
|
|
23
20
|
renderer.(task_node: task_node, position: position, tree: tree)
|
|
24
21
|
end
|
|
25
22
|
|
|
26
|
-
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def render_tree_for(nodes)
|
|
30
|
-
nodes.map { |level, node|
|
|
31
|
-
indentation = INDENTATION * (level -1)
|
|
32
|
-
indentation = indentation[0...-1] + "`" if level == 1 || /End./.match(node) # start or end step
|
|
33
|
-
indentation + STEP_PREFIX + node
|
|
34
|
-
}.join("\n")
|
|
23
|
+
Hirb::Console.format_output(nodes, class: :tree, type: :directory, multi_line_nodes: true)
|
|
35
24
|
end
|
|
36
25
|
|
|
37
26
|
def tree_for(stack, level, tree:, **options)
|
|
38
27
|
stack.each do |lvl| # always a Stack::Task[input, ..., output]
|
|
39
28
|
input, output, nested = Trace::Level.input_output_nested_for_level(lvl)
|
|
40
29
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
graph = Trailblazer::Activity::Introspect::Graph(input.activity)
|
|
44
|
-
|
|
45
|
-
name = (node = graph.find { |node| node[:task] == task }) ? node[:id] : task
|
|
46
|
-
name ||= task # FIXME: bullshit
|
|
47
|
-
|
|
48
|
-
tree << { level: level, input: input, output: output, name: name, **options }
|
|
30
|
+
tree.push(*TreeNodes.for(level, options.merge(input: input, output: output)))
|
|
49
31
|
|
|
50
32
|
if nested.any? # nesting
|
|
51
33
|
tree_for(nested, level + 1, options.merge(tree: tree))
|
|
@@ -54,6 +36,26 @@ module Trailblazer::Developer
|
|
|
54
36
|
tree
|
|
55
37
|
end
|
|
56
38
|
end
|
|
39
|
+
|
|
40
|
+
module TreeNodes
|
|
41
|
+
Node = Struct.new(:level, :value, :input, :output, :options) do
|
|
42
|
+
# Allow access to any custom key from options, eg. color_map
|
|
43
|
+
def method_missing(name, *)
|
|
44
|
+
options[name]
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
module_function
|
|
49
|
+
|
|
50
|
+
def for(level, input:, output:, **options)
|
|
51
|
+
nodes = Array[ Node.new(level, input.data[:task_name], input, output, options).freeze ]
|
|
52
|
+
|
|
53
|
+
focused_nodes = Trace::Focusable.tree_nodes_for(level, input: input, output: output, **options)
|
|
54
|
+
nodes += focused_nodes if focused_nodes.length > 0
|
|
55
|
+
|
|
56
|
+
nodes
|
|
57
|
+
end
|
|
58
|
+
end
|
|
57
59
|
end
|
|
58
60
|
end
|
|
59
61
|
end
|
|
@@ -12,95 +12,78 @@ module Trailblazer::Developer
|
|
|
12
12
|
module Wtf
|
|
13
13
|
module_function
|
|
14
14
|
|
|
15
|
-
COLOR_MAP = { pass: :green, fail: :brown }
|
|
16
|
-
|
|
17
|
-
SIGNALS_MAP = {
|
|
18
|
-
'Trailblazer::Activity::Right': :pass,
|
|
19
|
-
'Trailblazer::Activity::FastTrack::PassFast': :pass,
|
|
20
|
-
|
|
21
|
-
'Trailblazer::Activity::Left': :fail,
|
|
22
|
-
'Trailblazer::Activity::FastTrack::FailFast': :fail,
|
|
23
|
-
}
|
|
24
|
-
|
|
25
15
|
# Run {activity} with tracing enabled and inject a mutable {Stack} instance.
|
|
26
16
|
# This allows to display the trace even when an exception happened
|
|
27
|
-
def invoke(activity, (ctx, flow_options), *
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
# this instance gets mutated with every step. unfortunately, there is
|
|
31
|
-
# no other way in Ruby to keep the trace even when an exception was thrown.
|
|
32
|
-
stack = Trace::Stack.new
|
|
33
|
-
|
|
34
|
-
_returned_stack, *returned = Trace.invoke(
|
|
35
|
-
activity,
|
|
36
|
-
[
|
|
37
|
-
ctx,
|
|
38
|
-
flow_options.merge(stack: stack)
|
|
39
|
-
],
|
|
40
|
-
*args
|
|
17
|
+
def invoke(activity, (ctx, flow_options), *circuit_options)
|
|
18
|
+
activity, (ctx, flow_options), circuit_options = Wtf.arguments_for_trace(
|
|
19
|
+
activity, [ctx, flow_options], *circuit_options
|
|
41
20
|
)
|
|
42
21
|
|
|
43
|
-
|
|
22
|
+
_returned_stack, signal, (ctx, flow_options) = Trace.invoke(
|
|
23
|
+
activity, [ctx, flow_options], *circuit_options
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
return signal, [ctx, flow_options], circuit_options
|
|
44
27
|
ensure
|
|
45
28
|
puts Trace::Present.(
|
|
46
|
-
stack,
|
|
47
|
-
renderer:
|
|
48
|
-
color_map:
|
|
29
|
+
flow_options[:stack],
|
|
30
|
+
renderer: Wtf::Renderer,
|
|
31
|
+
color_map: Wtf::Renderer::DEFAULT_COLOR_MAP.merge( flow_options[:color_map] || {} ),
|
|
49
32
|
)
|
|
50
33
|
end
|
|
51
34
|
|
|
52
|
-
def
|
|
53
|
-
|
|
35
|
+
def arguments_for_trace(activity, (ctx, original_flow_options), **circuit_options)
|
|
36
|
+
default_flow_options = {
|
|
37
|
+
# this instance gets mutated with every step. unfortunately, there is
|
|
38
|
+
# no other way in Ruby to keep the trace even when an exception was thrown.
|
|
39
|
+
stack: Trace::Stack.new,
|
|
54
40
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
41
|
+
input_data_collector: method(:trace_input_data_collector),
|
|
42
|
+
output_data_collector: method(:trace_output_data_collector),
|
|
43
|
+
}
|
|
58
44
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
end
|
|
45
|
+
# Merge default options with flow_options as an order of precedence
|
|
46
|
+
flow_options = { **default_flow_options, **Hash( original_flow_options ) }
|
|
62
47
|
|
|
63
|
-
|
|
48
|
+
# Normalize `focus_on` param to
|
|
49
|
+
# 1. Wrap step and variable names into an array if not already
|
|
50
|
+
flow_options[:focus_on] = {
|
|
51
|
+
steps: Array( flow_options.dig(:focus_on, :steps) ),
|
|
52
|
+
variables: Array( flow_options.dig(:focus_on, :variables) ),
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return activity, [ ctx, flow_options ], circuit_options
|
|
64
56
|
end
|
|
65
57
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
58
|
+
# Overring default input and output data collectors to collect/capture
|
|
59
|
+
# 1. inspect of focusable variables for given focusable step
|
|
60
|
+
# 2. Or inspect of focused variables for all steps
|
|
61
|
+
def trace_input_data_collector(wrap_config, (ctx, flow_options), circuit_options)
|
|
62
|
+
data = Trace.default_input_data_collector(wrap_config, [ctx, flow_options], circuit_options)
|
|
63
|
+
|
|
64
|
+
if Wtf.caputure_variables?(step_name: data[:task_name], **flow_options)
|
|
65
|
+
data[:focused_variables] = Trace::Focusable.capture_variables_from(ctx, **flow_options)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
data
|
|
69
69
|
end
|
|
70
70
|
|
|
71
|
-
def
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
def trace_output_data_collector(wrap_config, (ctx, flow_options), circuit_options)
|
|
72
|
+
data = Trace.default_output_data_collector(wrap_config, [ctx, flow_options], circuit_options)
|
|
73
|
+
input = flow_options[:stack].top
|
|
74
|
+
|
|
75
|
+
if Wtf.caputure_variables?(step_name: input.data[:task_name], **flow_options)
|
|
76
|
+
data[:focused_variables] = Trace::Focusable.capture_variables_from(ctx, **flow_options)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
data
|
|
74
80
|
end
|
|
75
81
|
|
|
76
|
-
#
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
def black(str); "\e[30m#{str}\e[0m" end
|
|
82
|
-
def red(str); "\e[31m#{str}\e[0m" end
|
|
83
|
-
def green(str); "\e[32m#{str}\e[0m" end
|
|
84
|
-
def brown(str); "\e[33m#{str}\e[0m" end
|
|
85
|
-
def blue(str); "\e[34m#{str}\e[0m" end
|
|
86
|
-
def magenta(str); "\e[35m#{str}\e[0m" end
|
|
87
|
-
def cyan(str); "\e[36m#{str}\e[0m" end
|
|
88
|
-
def gray(str); "\e[37m#{str}\e[0m" end
|
|
89
|
-
|
|
90
|
-
def bg_black(str); "\e[40m#{str}\e[0m" end
|
|
91
|
-
def bg_red(str); "\e[41m#{str}\e[0m" end
|
|
92
|
-
def bg_green(str); "\e[42m#{str}\e[0m" end
|
|
93
|
-
def bg_brown(str); "\e[43m#{str}\e[0m" end
|
|
94
|
-
def bg_blue(str); "\e[44m#{str}\e[0m" end
|
|
95
|
-
def bg_magenta(str); "\e[45m#{str}\e[0m" end
|
|
96
|
-
def bg_cyan(str); "\e[46m#{str}\e[0m" end
|
|
97
|
-
def bg_gray(str); "\e[47m#{str}\e[0m" end
|
|
98
|
-
|
|
99
|
-
def bold(str); "\e[1m#{str}\e[22m" end
|
|
100
|
-
def italic(str); "\e[3m#{str}\e[23m" end
|
|
101
|
-
def underline(str); "\e[4m#{str}\e[24m" end
|
|
102
|
-
def blink(str); "\e[5m#{str}\e[25m" end
|
|
103
|
-
def reverse_color(str); "\e[7m#{str}\e[27m" end
|
|
82
|
+
# private
|
|
83
|
+
def caputure_variables?(step_name:, focus_on:, **)
|
|
84
|
+
return true if focus_on[:steps].include?(step_name) # For given step
|
|
85
|
+
return true if focus_on[:steps].empty? && focus_on[:variables].any? # For selected vars but all steps
|
|
86
|
+
return false
|
|
104
87
|
end
|
|
105
88
|
end
|
|
106
89
|
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
module Trailblazer::Developer
|
|
2
|
+
module Wtf
|
|
3
|
+
|
|
4
|
+
module Renderer
|
|
5
|
+
DEFAULT_COLOR_MAP = { pass: :green, fail: :brown }
|
|
6
|
+
|
|
7
|
+
SIGNALS_MAP = {
|
|
8
|
+
:'Trailblazer::Activity::Right' => :pass,
|
|
9
|
+
:'Trailblazer::Activity::FastTrack::PassFast' => :pass,
|
|
10
|
+
|
|
11
|
+
:'Trailblazer::Activity::Left' => :fail,
|
|
12
|
+
:'Trailblazer::Activity::FastTrack::FailFast' => :fail,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module_function
|
|
16
|
+
|
|
17
|
+
# tree: Array of Trace::TreeNodes::Node
|
|
18
|
+
# task_node - current Trace::TreeNodes::Node to render
|
|
19
|
+
# position - task_node's position in tree
|
|
20
|
+
def call(tree:, task_node:, position:)
|
|
21
|
+
value = value_for(tree, task_node, position)
|
|
22
|
+
[task_node.level, value]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def value_for(tree, task_node, position)
|
|
26
|
+
if task_node.output.nil? && tree[position.next].nil? # i.e. when exception raised
|
|
27
|
+
return %{#{fmt(fmt(task_node.value, :red), :bold)}}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
if task_node.output.nil? # i.e. on entry/exit point of activity
|
|
31
|
+
return %{#{task_node.value}}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
%{#{fmt(task_node.value, task_node.color_map[ signal_of(task_node) ])}}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def fmt(line, style)
|
|
38
|
+
return line unless style
|
|
39
|
+
String.send(style, line)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def signal_of(task_node)
|
|
43
|
+
entity_signal = task_node.output.data[:signal]
|
|
44
|
+
entity_klass = entity_signal.is_a?(Class) ? entity_signal : entity_signal.class
|
|
45
|
+
|
|
46
|
+
SIGNALS_MAP[entity_klass.name.to_sym]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Stolen from https://stackoverflow.com/questions/1489183/colorized-ruby-output
|
|
50
|
+
#
|
|
51
|
+
# TODO: this is just prototyping
|
|
52
|
+
module String
|
|
53
|
+
module_function
|
|
54
|
+
def black(str); "\e[30m#{str}\e[0m" end
|
|
55
|
+
def red(str); "\e[31m#{str}\e[0m" end
|
|
56
|
+
def green(str); "\e[32m#{str}\e[0m" end
|
|
57
|
+
def brown(str); "\e[33m#{str}\e[0m" end
|
|
58
|
+
def blue(str); "\e[34m#{str}\e[0m" end
|
|
59
|
+
def magenta(str); "\e[35m#{str}\e[0m" end
|
|
60
|
+
def cyan(str); "\e[36m#{str}\e[0m" end
|
|
61
|
+
def gray(str); "\e[37m#{str}\e[0m" end
|
|
62
|
+
|
|
63
|
+
def bg_black(str); "\e[40m#{str}\e[0m" end
|
|
64
|
+
def bg_red(str); "\e[41m#{str}\e[0m" end
|
|
65
|
+
def bg_green(str); "\e[42m#{str}\e[0m" end
|
|
66
|
+
def bg_brown(str); "\e[43m#{str}\e[0m" end
|
|
67
|
+
def bg_blue(str); "\e[44m#{str}\e[0m" end
|
|
68
|
+
def bg_magenta(str); "\e[45m#{str}\e[0m" end
|
|
69
|
+
def bg_cyan(str); "\e[46m#{str}\e[0m" end
|
|
70
|
+
def bg_gray(str); "\e[47m#{str}\e[0m" end
|
|
71
|
+
|
|
72
|
+
def bold(str); "\e[1m#{str}\e[22m" end
|
|
73
|
+
def italic(str); "\e[3m#{str}\e[23m" end
|
|
74
|
+
def underline(str); "\e[4m#{str}\e[24m" end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
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.0.13
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nick Sutterer
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2020-06-27 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -128,6 +128,20 @@ dependencies:
|
|
|
128
128
|
- - ">="
|
|
129
129
|
- !ruby/object:Gem::Version
|
|
130
130
|
version: '0'
|
|
131
|
+
- !ruby/object:Gem::Dependency
|
|
132
|
+
name: hirb
|
|
133
|
+
requirement: !ruby/object:Gem::Requirement
|
|
134
|
+
requirements:
|
|
135
|
+
- - ">="
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: '0'
|
|
138
|
+
type: :runtime
|
|
139
|
+
prerelease: false
|
|
140
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
141
|
+
requirements:
|
|
142
|
+
- - ">="
|
|
143
|
+
- !ruby/object:Gem::Version
|
|
144
|
+
version: '0'
|
|
131
145
|
description: 'Developer tools for Trailblazer: debugger, tracing, visual editor integration.'
|
|
132
146
|
email:
|
|
133
147
|
- apotonick@gmail.com
|
|
@@ -147,15 +161,17 @@ files:
|
|
|
147
161
|
- gems.local.rb
|
|
148
162
|
- lib/trailblazer/developer.rb
|
|
149
163
|
- lib/trailblazer/developer/CHANGES.md
|
|
150
|
-
- lib/trailblazer/developer/activity.rb
|
|
151
164
|
- lib/trailblazer/developer/client.rb
|
|
152
165
|
- lib/trailblazer/developer/generate.rb
|
|
153
166
|
- lib/trailblazer/developer/render/circuit.rb
|
|
154
167
|
- lib/trailblazer/developer/render/linear.rb
|
|
155
168
|
- lib/trailblazer/developer/trace.rb
|
|
169
|
+
- lib/trailblazer/developer/trace/focusable.rb
|
|
170
|
+
- lib/trailblazer/developer/trace/inspector.rb
|
|
156
171
|
- lib/trailblazer/developer/trace/present.rb
|
|
157
172
|
- lib/trailblazer/developer/version.rb
|
|
158
173
|
- lib/trailblazer/developer/wtf.rb
|
|
174
|
+
- lib/trailblazer/developer/wtf/renderer.rb
|
|
159
175
|
- lib/trailblazer/diagram/bpmn.rb
|
|
160
176
|
- trailblazer-developer.gemspec
|
|
161
177
|
homepage: http://trailblazer.to
|
|
@@ -177,8 +193,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
177
193
|
- !ruby/object:Gem::Version
|
|
178
194
|
version: '0'
|
|
179
195
|
requirements: []
|
|
180
|
-
|
|
181
|
-
rubygems_version: 2.7.6
|
|
196
|
+
rubygems_version: 3.0.3
|
|
182
197
|
signing_key:
|
|
183
198
|
specification_version: 4
|
|
184
199
|
summary: Developer tools for Trailblazer.
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
module Trailblazer
|
|
2
|
-
module Developer
|
|
3
|
-
# Transforms an {Activity::Graph} into an abstract data structure that represents the graph via a
|
|
4
|
-
# well-defined API. The goal is to decouple graph internals from the presentation layer.
|
|
5
|
-
# The {Model} is usually passed into Renderer and Layouter, to render the bpmn:Diagram XML or JSON.
|
|
6
|
-
#
|
|
7
|
-
# It returns {Model} with {Task}s and {Flow}s.
|
|
8
|
-
module Activity
|
|
9
|
-
module Graph
|
|
10
|
-
class << self
|
|
11
|
-
Model = Struct.new(:id, :start_events, :end_events, :task, :sequence_flow)
|
|
12
|
-
Task = Struct.new(:id, :name, :options, :incoming, :outgoing)
|
|
13
|
-
# DISCUSS: direction ATM is the "condition" for the BPMN rendering.
|
|
14
|
-
Flow = Struct.new(:id, :sourceRef, :targetRef, :direction)
|
|
15
|
-
|
|
16
|
-
# @param Graph an object implementing the Activity::Graph interface
|
|
17
|
-
# @return Model Generic representation of the graph, ready for rendering.
|
|
18
|
-
def self.to_model(graph, id: "some-process") # rubocop:disable Metrics/AbcSize
|
|
19
|
-
start_events = graph.find_all("Start.default") # FIXME. this is a static assumption.
|
|
20
|
-
end_events = graph.find_all { |node| graph.successors(node).size.zero? }
|
|
21
|
-
tasks = graph.find_all { |_node| true }
|
|
22
|
-
tasks -= start_events
|
|
23
|
-
tasks -= end_events
|
|
24
|
-
|
|
25
|
-
# transform nodes into BPMN elements.
|
|
26
|
-
start_events = start_events.collect do |evt|
|
|
27
|
-
Task.new(evt[:id], evt[:id], evt, Incomings(graph, evt), Outgoings(graph, evt))
|
|
28
|
-
end
|
|
29
|
-
end_events = end_events.collect do |evt|
|
|
30
|
-
Task.new(evt[:id], evt[:id], evt, Incomings(graph, evt), Outgoings(graph, evt))
|
|
31
|
-
end
|
|
32
|
-
tasks = tasks.collect { |evt| Task.new(evt[:id], evt[:id], evt, Incomings(graph, evt), Outgoings(graph, evt)) }
|
|
33
|
-
edges = (start_events + end_events + tasks).collect { |task| [task.incoming, task.outgoing] }.flatten(2).uniq
|
|
34
|
-
|
|
35
|
-
Model.new(id, start_events, end_events, tasks, edges)
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
private
|
|
39
|
-
|
|
40
|
-
def Outgoings(graph, source)
|
|
41
|
-
graph.successors(source).collect { |target, edge| Flow.new(edge[:id], source[:id], target[:id], edge[:_wrapped]) }
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def Incomings(graph, target)
|
|
45
|
-
graph.predecessors(target).collect { |source, edge| Flow.new(edge[:id], source[:id], target[:id], edge[:_wrapped]) }
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|