trailblazer-activity 0.7.1 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop-https---raw-githubusercontent-com-trailblazer-meta-master-rubocop-yml +101 -0
- data/.rubocop.yml +4 -13
- data/.rubocop_todo.yml +474 -476
- data/.travis.yml +3 -2
- data/CHANGES.md +10 -0
- data/Gemfile +5 -4
- data/README.md +2 -0
- data/Rakefile +1 -1
- data/lib/trailblazer/activity.rb +29 -92
- data/lib/trailblazer/activity/circuit.rb +74 -0
- data/lib/trailblazer/activity/config.rb +4 -6
- data/lib/trailblazer/activity/introspect.rb +33 -129
- data/lib/trailblazer/activity/present.rb +14 -39
- data/lib/trailblazer/activity/schema.rb +13 -0
- data/lib/trailblazer/activity/schema/implementation.rb +10 -0
- data/lib/trailblazer/activity/schema/intermediate.rb +94 -0
- data/lib/trailblazer/activity/structures.rb +43 -43
- data/lib/trailblazer/activity/task_wrap.rb +29 -16
- data/lib/trailblazer/activity/task_wrap/call_task.rb +4 -4
- data/lib/trailblazer/activity/task_wrap/inject.rb +37 -0
- data/lib/trailblazer/activity/task_wrap/pipeline.rb +55 -0
- data/lib/trailblazer/activity/task_wrap/runner.rb +10 -19
- data/lib/trailblazer/activity/task_wrap/variable_mapping.rb +25 -97
- data/lib/trailblazer/activity/testing.rb +64 -22
- data/lib/trailblazer/activity/trace.rb +88 -41
- data/lib/trailblazer/activity/version.rb +4 -2
- data/trailblazer-activity.gemspec +5 -9
- metadata +18 -55
- data/lib/trailblazer/activity/dsl/add_task.rb +0 -22
- data/lib/trailblazer/activity/dsl/helper.rb +0 -68
- data/lib/trailblazer/activity/dsl/magnetic.rb +0 -36
- data/lib/trailblazer/activity/dsl/magnetic/builder.rb +0 -101
- data/lib/trailblazer/activity/dsl/magnetic/builder/default_normalizer.rb +0 -26
- data/lib/trailblazer/activity/dsl/magnetic/builder/fast_track.rb +0 -118
- data/lib/trailblazer/activity/dsl/magnetic/builder/normalizer.rb +0 -113
- data/lib/trailblazer/activity/dsl/magnetic/builder/path.rb +0 -105
- data/lib/trailblazer/activity/dsl/magnetic/builder/railway.rb +0 -97
- data/lib/trailblazer/activity/dsl/magnetic/builder/state.rb +0 -58
- data/lib/trailblazer/activity/dsl/magnetic/finalizer.rb +0 -51
- data/lib/trailblazer/activity/dsl/magnetic/generate.rb +0 -62
- data/lib/trailblazer/activity/dsl/magnetic/merge.rb +0 -16
- data/lib/trailblazer/activity/dsl/magnetic/process_options.rb +0 -76
- data/lib/trailblazer/activity/dsl/magnetic/structure/alterations.rb +0 -44
- data/lib/trailblazer/activity/dsl/magnetic/structure/plus_poles.rb +0 -85
- data/lib/trailblazer/activity/dsl/magnetic/structure/polarization.rb +0 -23
- data/lib/trailblazer/activity/dsl/record.rb +0 -11
- data/lib/trailblazer/activity/dsl/schema/dependencies.rb +0 -46
- data/lib/trailblazer/activity/dsl/schema/sequence.rb +0 -46
- data/lib/trailblazer/activity/dsl/strategy/build_state.rb +0 -32
- data/lib/trailblazer/activity/dsl/strategy/fast_track.rb +0 -24
- data/lib/trailblazer/activity/dsl/strategy/path.rb +0 -26
- data/lib/trailblazer/activity/dsl/strategy/plan.rb +0 -36
- data/lib/trailblazer/activity/dsl/strategy/railway.rb +0 -23
- data/lib/trailblazer/activity/interface.rb +0 -16
- data/lib/trailblazer/activity/task_wrap/merge.rb +0 -23
- data/lib/trailblazer/activity/task_wrap/trace.rb +0 -44
- data/lib/trailblazer/circuit.rb +0 -71
@@ -1,4 +1,4 @@
|
|
1
|
-
class Trailblazer::Activity
|
1
|
+
class Trailblazer::Activity
|
2
2
|
module TaskWrap
|
3
3
|
# The runner is passed into Activity#call( runner: Runner ) and is called for every task in the circuit.
|
4
4
|
# It runs the TaskWrap per task.
|
@@ -12,18 +12,15 @@ class Trailblazer::Activity < Module
|
|
12
12
|
def self.call(task, args, circuit_options)
|
13
13
|
wrap_ctx = { task: task }
|
14
14
|
|
15
|
-
# this
|
16
|
-
|
15
|
+
# this pipeline is "wrapped around" the actual `task`.
|
16
|
+
task_wrap_pipeline = merge_static_with_runtime(task, circuit_options) || raise
|
17
17
|
|
18
18
|
# We save all original args passed into this Runner.call, because we want to return them later after this wrap
|
19
19
|
# is finished.
|
20
20
|
original_args = [ args, circuit_options ]
|
21
21
|
|
22
22
|
# call the wrap {Activity} around the task.
|
23
|
-
|
24
|
-
[ wrap_ctx, original_args ], # we omit circuit_options here on purpose, so the wrapping activity uses the default, plain Runner.
|
25
|
-
{}
|
26
|
-
)
|
23
|
+
wrap_ctx, _ = task_wrap_pipeline.(wrap_ctx, original_args) # we omit circuit_options here on purpose, so the wrapping activity uses the default, plain Runner.
|
27
24
|
|
28
25
|
# don't return the wrap's end signal, but the one from call_task.
|
29
26
|
# return all original_args for the next "real" task in the circuit (this includes circuit_options).
|
@@ -39,24 +36,18 @@ class Trailblazer::Activity < Module
|
|
39
36
|
# unnecessary computations at `call`-time since steps might not even be executed.
|
40
37
|
# TODO: make this faster.
|
41
38
|
def self.merge_static_with_runtime(task, wrap_runtime:, **circuit_options)
|
42
|
-
|
43
|
-
task,
|
44
|
-
circuit_options
|
45
|
-
) # find static wrap for this specific task, or default wrap activity.
|
39
|
+
static_wrap = TaskWrap.wrap_static_for(task, circuit_options) # find static wrap for this specific task [, or default wrap activity].
|
46
40
|
|
47
41
|
# Apply runtime alterations.
|
48
|
-
# Grab the additional wirings for the particular `task` from `wrap_runtime
|
49
|
-
wrap_runtime[task] ?
|
42
|
+
# Grab the additional wirings for the particular `task` from `wrap_runtime`.
|
43
|
+
(dynamic_wrap = wrap_runtime[task]) ? dynamic_wrap.(static_wrap) : static_wrap
|
50
44
|
end
|
51
45
|
end # Runner
|
52
46
|
|
53
47
|
# Retrieve the static wrap config from {activity}.
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
def self.wrap_static_for(task, activity:, default_activity: TaskWrap.initial_activity, **)
|
58
|
-
wrap_static = activity[:wrap_static] || {}
|
59
|
-
wrap_static[task] || default_activity
|
48
|
+
def self.wrap_static_for(task, activity:, **)
|
49
|
+
wrap_static = activity[:wrap_static]
|
50
|
+
wrap_static[task] or raise "#{task}"
|
60
51
|
end
|
61
52
|
end
|
62
53
|
end
|
@@ -1,6 +1,4 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
class Trailblazer::Activity < Module
|
1
|
+
class Trailblazer::Activity
|
4
2
|
module TaskWrap
|
5
3
|
# Creates taskWrap steps to map variables before and after the actual step.
|
6
4
|
# We hook into the Normalizer, process `:input` and `:output` directives and
|
@@ -9,55 +7,25 @@ class Trailblazer::Activity < Module
|
|
9
7
|
# Note that the two options are not the only way to create filters, you can use the
|
10
8
|
# more low-level {Scoped()} etc., too, and write your own filter logic.
|
11
9
|
module VariableMapping
|
12
|
-
# DSL step for Magnetic::Normalizer.
|
13
|
-
# Translates `:input` and `:output` into VariableMapping taskWrap extensions.
|
14
|
-
def self.normalizer_step_for_input_output(ctx, *)
|
15
|
-
options, io_config = Magnetic::Options.normalize( ctx[:options], [:input, :output] )
|
16
|
-
|
17
|
-
return if io_config.empty?
|
18
|
-
|
19
|
-
ctx[:options] = options # without :input and :output
|
20
|
-
ctx[:options] = options.merge(Trailblazer::Activity::TaskWrap::VariableMapping(io_config) => true)
|
21
|
-
end
|
22
|
-
|
23
10
|
# The taskWrap extension that's included into the static taskWrap for a task.
|
24
|
-
def self.
|
25
|
-
Trailblazer::
|
26
|
-
|
27
|
-
Module.new do
|
28
|
-
extend Path::Plan()
|
11
|
+
def self.Extension(input, output, id: input.object_id)
|
12
|
+
input = Trailblazer::Option(input)
|
13
|
+
output = Trailblazer::Option(output)
|
29
14
|
|
30
|
-
|
31
|
-
|
32
|
-
end
|
33
|
-
)
|
15
|
+
Trailblazer::Activity::TaskWrap::Extension(
|
16
|
+
merge: merge_for(input, output, id: id),
|
34
17
|
)
|
35
18
|
end
|
36
|
-
end
|
37
19
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
20
|
+
# DISCUSS: do we want the automatic wrapping of {input} and {output}?
|
21
|
+
def self.merge_for(input, output, id:) # TODO: rename
|
22
|
+
[
|
23
|
+
[TaskWrap::Pipeline.method(:insert_before), "task_wrap.call_task", ["task_wrap.input", TaskWrap::Input.new(input, id: id)]],
|
24
|
+
[TaskWrap::Pipeline.method(:insert_after), "task_wrap.call_task", ["task_wrap.output", TaskWrap::Output.new(output, id: id)]],
|
25
|
+
]
|
44
26
|
end
|
45
27
|
end
|
46
28
|
|
47
|
-
|
48
|
-
# Returns an Extension instance to be thrown into the `step` DSL arguments.
|
49
|
-
def self.VariableMapping(input:, output:)
|
50
|
-
input = Input.new(
|
51
|
-
Input::Scoped.new(
|
52
|
-
Trailblazer::Option::KW( filter_for(input) ) ) )
|
53
|
-
|
54
|
-
output = Output.new(
|
55
|
-
Output::Unscoped.new(
|
56
|
-
Trailblazer::Option::KW( filter_for(output) ) ) )
|
57
|
-
|
58
|
-
VariableMapping.extension_for(input, output)
|
59
|
-
end
|
60
|
-
|
61
29
|
# TaskWrap step to compute the incoming {Context} for the wrapped task.
|
62
30
|
# This allows renaming, filtering, hiding, of the options passed into the wrapped task.
|
63
31
|
#
|
@@ -67,23 +35,25 @@ class Trailblazer::Activity < Module
|
|
67
35
|
|
68
36
|
# Calls your {@filter} and replaces the original ctx with your returned one.
|
69
37
|
class Input
|
70
|
-
def initialize(filter)
|
38
|
+
def initialize(filter, id:)
|
71
39
|
@filter = filter
|
40
|
+
@id = id
|
72
41
|
end
|
73
42
|
|
43
|
+
# {input.call()} is invoked in the circuit.
|
74
44
|
# `original_args` are the actual args passed to the wrapped task: [ [options, ..], circuit_options ]
|
75
45
|
#
|
76
|
-
def call(
|
46
|
+
def call(wrap_ctx, original_args)
|
77
47
|
# let user compute new ctx for the wrapped task.
|
78
48
|
input_ctx = apply_filter(*original_args)
|
79
49
|
|
80
|
-
wrap_ctx = wrap_ctx.merge( vm_original_ctx: original_args[0][0] ) # remember the original ctx
|
81
|
-
|
82
50
|
# decompose the original_args since we want to modify them.
|
83
51
|
(original_ctx, original_flow_options), original_circuit_options = original_args
|
84
52
|
|
53
|
+
wrap_ctx = wrap_ctx.merge(@id => original_ctx) # remember the original ctx by the key {@id}.
|
54
|
+
|
85
55
|
# instead of the original Context, pass on the filtered `input_ctx` in the wrap.
|
86
|
-
return
|
56
|
+
return wrap_ctx, [[input_ctx, original_flow_options], original_circuit_options]
|
87
57
|
end
|
88
58
|
|
89
59
|
private
|
@@ -91,71 +61,29 @@ class Trailblazer::Activity < Module
|
|
91
61
|
def apply_filter((ctx, original_flow_options), original_circuit_options)
|
92
62
|
@filter.( ctx, original_circuit_options ) # returns {new_ctx}.
|
93
63
|
end
|
94
|
-
|
95
|
-
class Scoped
|
96
|
-
def initialize(filter)
|
97
|
-
@filter = filter
|
98
|
-
end
|
99
|
-
|
100
|
-
def call(original_ctx, circuit_options)
|
101
|
-
Trailblazer::Context( # TODO: make this interchangeable so we can work on faster contexts?
|
102
|
-
@filter.(original_ctx, **circuit_options)
|
103
|
-
)
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
module DSL
|
109
|
-
# The returned filter compiles a new hash for Scoped/Unscoped that only contains
|
110
|
-
# the desired i/o variables.
|
111
|
-
def self.filter_from_dsl(map)
|
112
|
-
hsh = DSL.hash_for(map)
|
113
|
-
|
114
|
-
->(incoming_ctx, kwargs) { Hash[hsh.collect { |from_name, to_name| [to_name, incoming_ctx[from_name]] }] }
|
115
|
-
end
|
116
|
-
|
117
|
-
def self.hash_for(ary)
|
118
|
-
return ary if ary.instance_of?(::Hash)
|
119
|
-
Hash[ary.collect { |name| [name, name] }]
|
120
|
-
end
|
121
64
|
end
|
122
65
|
|
123
66
|
# TaskWrap step to compute the outgoing {Context} from the wrapped task.
|
124
67
|
# This allows renaming, filtering, hiding, of the options returned from the wrapped task.
|
125
68
|
class Output
|
126
|
-
def initialize(filter)
|
69
|
+
def initialize(filter, id:)
|
127
70
|
@filter = filter
|
71
|
+
@id = id
|
128
72
|
end
|
129
73
|
|
130
74
|
# Runs your filter and replaces the ctx in `wrap_ctx[:return_args]` with the filtered one.
|
131
|
-
def call(
|
75
|
+
def call(wrap_ctx, original_args)
|
132
76
|
(original_ctx, original_flow_options), original_circuit_options = original_args
|
133
77
|
|
134
|
-
returned_ctx, _ = wrap_ctx[:return_args]
|
135
|
-
original_ctx = wrap_ctx[:
|
78
|
+
returned_ctx, _ = wrap_ctx[:return_args] # this is the Context returned from {call}ing the wrapped user task.
|
79
|
+
original_ctx = wrap_ctx[@id] # grab the original ctx from before which was set in the {:input} filter.
|
136
80
|
# let user compute the output.
|
137
81
|
output_ctx = @filter.(original_ctx, returned_ctx, **original_circuit_options)
|
138
82
|
|
139
83
|
wrap_ctx = wrap_ctx.merge( return_args: [output_ctx, original_flow_options] )
|
140
84
|
|
141
85
|
# and then pass on the "new" context.
|
142
|
-
return
|
143
|
-
end
|
144
|
-
|
145
|
-
private
|
146
|
-
|
147
|
-
# Merge the resulting {@filter.()} hash back into the original ctx.
|
148
|
-
# DISCUSS: do we need the original_ctx as a filter argument?
|
149
|
-
class Unscoped
|
150
|
-
def initialize(filter)
|
151
|
-
@filter = filter
|
152
|
-
end
|
153
|
-
|
154
|
-
def call(original_ctx, new_ctx, **circuit_options)
|
155
|
-
original_ctx.merge(
|
156
|
-
@filter.(new_ctx, **circuit_options)
|
157
|
-
)
|
158
|
-
end
|
86
|
+
return wrap_ctx, original_args
|
159
87
|
end
|
160
88
|
end
|
161
89
|
end # Wrap
|
@@ -1,32 +1,74 @@
|
|
1
1
|
# DISCUSS: move to trailblazer-activity-test ?
|
2
2
|
|
3
3
|
# Helpers to quickly create steps and tasks.
|
4
|
-
module Trailblazer
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
4
|
+
module Trailblazer
|
5
|
+
module Activity::Testing
|
6
|
+
# Creates a module with one step method for each name.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# extend T.def_steps(:create, :save)
|
10
|
+
def self.def_steps(*names)
|
11
|
+
Module.new do
|
12
|
+
module_function
|
13
|
+
names.each do |name|
|
14
|
+
define_method(name) do | ctx, ** |
|
15
|
+
ctx[:seq] << name
|
16
|
+
ctx.key?(name) ? ctx[name] : true
|
17
|
+
end
|
15
18
|
end
|
16
19
|
end
|
17
20
|
end
|
18
|
-
end
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
22
|
+
# Creates a method instance with a task interface.
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
# task task: T.def_task(:create)
|
26
|
+
def self.def_task(name)
|
27
|
+
Module.new do
|
28
|
+
define_singleton_method(name) do | (ctx, flow_options), ** |
|
29
|
+
ctx[:seq] << name
|
30
|
+
return Activity::Right, [ctx, flow_options]
|
31
|
+
end
|
32
|
+
end.method(name)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.def_tasks(*names)
|
36
|
+
Module.new do
|
37
|
+
module_function
|
38
|
+
names.each do |name|
|
39
|
+
define_method(name) do | (ctx, flow_options), ** |
|
40
|
+
ctx[:seq] << name
|
41
|
+
result = ctx.key?(name) ? ctx[name] : true
|
42
|
+
|
43
|
+
return (result ? Activity::Right : Activity::Left), [ctx, flow_options]
|
44
|
+
end
|
45
|
+
end
|
29
46
|
end
|
30
|
-
end
|
47
|
+
end
|
48
|
+
|
49
|
+
module Assertions
|
50
|
+
def Cct(activity)
|
51
|
+
cct = Trailblazer::Developer::Render::Circuit.(activity)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Tests {:circuit} and {:outputs} fields so far.
|
55
|
+
def assert_process_for(process, *args)
|
56
|
+
semantics, circuit = args[0..-2], args[-1]
|
57
|
+
|
58
|
+
inspects = semantics.collect { |semantic| %{#<struct Trailblazer::Activity::Output signal=#<Trailblazer::Activity::End semantic=#{semantic.inspect}>, semantic=#{semantic.inspect}>} }
|
59
|
+
|
60
|
+
process.to_h[:outputs].inspect.must_equal %{[#{inspects.join(", ")}]}
|
61
|
+
|
62
|
+
assert_circuit(process, circuit)
|
63
|
+
|
64
|
+
process
|
65
|
+
end
|
66
|
+
|
67
|
+
def assert_circuit(schema, circuit)
|
68
|
+
cct = Cct(schema)
|
69
|
+
cct = cct.gsub("#<Trailblazer::Activity::TaskBuilder::Task user_proc=", "<*")
|
70
|
+
cct.must_equal %{#{circuit}}
|
71
|
+
end
|
72
|
+
end
|
31
73
|
end
|
32
74
|
end
|
@@ -1,77 +1,123 @@
|
|
1
1
|
module Trailblazer
|
2
|
-
class Activity
|
3
|
-
# and the order and nesting.
|
4
|
-
#
|
5
|
-
# stack, _ = Trailblazer::Activity::Trace.(activity, activity[:Start], { id: 1 })
|
6
|
-
# puts Trailblazer::Activity::Present.tree(stack) # renders the trail.
|
7
|
-
#
|
8
|
-
# Hooks into the taskWrap.
|
2
|
+
class Activity
|
9
3
|
module Trace
|
10
4
|
class << self
|
11
|
-
# {
|
12
|
-
|
13
|
-
|
5
|
+
# Public entry point to activate tracing when running {activity}.
|
6
|
+
def call(activity, (ctx, flow_options), circuit_options={})
|
7
|
+
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
|
+
signal, (ctx, flow_options) = Activity::TaskWrap.invoke(activity, [ctx, flow_options], circuit_options)
|
10
|
+
|
11
|
+
return flow_options[:stack], signal, [ctx, flow_options]
|
12
|
+
end
|
13
|
+
|
14
|
+
alias_method :invoke, :call
|
15
|
+
|
14
16
|
def arguments_for_call(activity, (options, flow_options), **circuit_options)
|
15
17
|
tracing_flow_options = {
|
16
|
-
stack:
|
18
|
+
stack: Trace::Stack.new,
|
17
19
|
}
|
18
20
|
|
19
21
|
tracing_circuit_options = {
|
20
|
-
wrap_runtime: ::Hash.new(Trace.
|
22
|
+
wrap_runtime: ::Hash.new(Trace.merge_plan), # DISCUSS: this overrides existing {:wrap_runtime}.
|
21
23
|
}
|
22
24
|
|
23
25
|
return activity, [ options, tracing_flow_options.merge(flow_options) ], circuit_options.merge(tracing_circuit_options)
|
24
26
|
end
|
27
|
+
end
|
25
28
|
|
26
|
-
|
27
|
-
|
29
|
+
module_function
|
30
|
+
# Insertions for the trace tasks that capture the arguments just before calling the task,
|
31
|
+
# and before the TaskWrap is finished.
|
32
|
+
#
|
33
|
+
# @private
|
34
|
+
def merge_plan
|
35
|
+
TaskWrap::Pipeline::Merge.new(
|
36
|
+
[TaskWrap::Pipeline.method(:insert_before), "task_wrap.call_task", ["task_wrap.capture_args", Trace.method(:capture_args)]],
|
37
|
+
[TaskWrap::Pipeline.method(:append), nil, ["task_wrap.capture_return", Trace.method(:capture_return)]],
|
38
|
+
)
|
39
|
+
end
|
28
40
|
|
29
|
-
|
30
|
-
|
41
|
+
# taskWrap step to capture incoming arguments of a step.
|
42
|
+
def capture_args(wrap_config, original_args)
|
43
|
+
original_args = capture_for(wrap_config[:task], *original_args)
|
31
44
|
|
32
|
-
|
33
|
-
|
45
|
+
return wrap_config, original_args
|
46
|
+
end
|
34
47
|
|
35
|
-
|
48
|
+
# taskWrap step to capture outgoing arguments from a step.
|
49
|
+
def capture_return(wrap_config, original_args)
|
50
|
+
(original_options, original_flow_options, _) = original_args[0]
|
36
51
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
# @private
|
43
|
-
def wirings
|
44
|
-
Module.new do
|
45
|
-
extend Activity::Path::Plan()
|
46
|
-
|
47
|
-
task TaskWrap::Trace.method(:capture_args), id: "task_wrap.capture_args", before: "task_wrap.call_task"
|
48
|
-
task TaskWrap::Trace.method(:capture_return), id: "task_wrap.capture_return", before: "End.success", group: :end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
+
original_flow_options[:stack] << Entity::Output.new(
|
53
|
+
wrap_config[:task], {}, wrap_config[:return_signal]
|
54
|
+
).freeze
|
55
|
+
|
56
|
+
original_flow_options[:stack].unindent!
|
52
57
|
|
53
|
-
|
54
|
-
|
58
|
+
|
59
|
+
return wrap_config, original_args
|
55
60
|
end
|
56
61
|
|
57
|
-
|
62
|
+
# It's important to understand that {flow[:stack]} is mutated by design. This is needed so
|
63
|
+
# in case of exceptions we still have a "global" trace - unfortunately Ruby doesn't allow
|
64
|
+
# us a better way.
|
65
|
+
def capture_for(task, (ctx, flow), activity:, **circuit_options)
|
66
|
+
flow[:stack].indent!
|
67
|
+
|
68
|
+
flow[:stack] << Entity::Input.new(
|
69
|
+
task, activity, [ctx, ctx.inspect]
|
70
|
+
).freeze
|
71
|
+
|
72
|
+
return [ctx, flow], circuit_options.merge(activity: activity)
|
58
73
|
end
|
59
74
|
|
60
|
-
|
75
|
+
# Structures used in {capture_args} and {capture_return}.
|
76
|
+
# These get pushed onto one {Level} in a {Stack}.
|
77
|
+
#
|
78
|
+
# Level[
|
79
|
+
# Level[ ==> this is a scalar task
|
80
|
+
# Entity::Input
|
81
|
+
# Entity::Output
|
82
|
+
# ]
|
83
|
+
# Level[ ==> nested task
|
84
|
+
# Entity::Input
|
85
|
+
# Level[
|
86
|
+
# Entity::Input
|
87
|
+
# Entity::Output
|
88
|
+
# ]
|
89
|
+
# Entity::Output
|
90
|
+
# ]
|
91
|
+
# ]
|
92
|
+
Entity = Struct.new(:task, :activity, :data)
|
93
|
+
Entity::Input = Class.new(Entity)
|
94
|
+
Entity::Output = Class.new(Entity)
|
95
|
+
|
96
|
+
class Level < Array
|
61
97
|
def inspect
|
62
|
-
%{<
|
98
|
+
%{<Level>#{super}}
|
99
|
+
end
|
100
|
+
|
101
|
+
# @param level {Trace::Level}
|
102
|
+
def self.input_output_nested_for_level(level)
|
103
|
+
input = level[0]
|
104
|
+
output = level[-1]
|
105
|
+
|
106
|
+
output, nested = output.is_a?(Entity::Output) ? [output, level-[input, output]] : [nil, level[1..-1]]
|
107
|
+
|
108
|
+
return input, output, nested
|
63
109
|
end
|
64
110
|
end
|
65
111
|
|
66
112
|
# Mutable/stateful per design. We want a (global) stack!
|
67
113
|
class Stack
|
68
114
|
def initialize
|
69
|
-
@nested =
|
115
|
+
@nested = Level.new
|
70
116
|
@stack = [ @nested ]
|
71
117
|
end
|
72
118
|
|
73
119
|
def indent!
|
74
|
-
current << indented =
|
120
|
+
current << indented = Level.new
|
75
121
|
@stack << indented
|
76
122
|
end
|
77
123
|
|
@@ -88,6 +134,7 @@ module Trailblazer
|
|
88
134
|
end
|
89
135
|
|
90
136
|
private
|
137
|
+
|
91
138
|
def current
|
92
139
|
@stack.last
|
93
140
|
end
|