trailblazer-activity 0.9.1 → 0.9.4
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 +12 -0
- data/lib/trailblazer/activity.rb +0 -4
- data/lib/trailblazer/activity/testing.rb +75 -0
- data/lib/trailblazer/activity/version.rb +1 -1
- metadata +3 -6
- data/lib/trailblazer/activity/introspect.rb +0 -72
- data/lib/trailblazer/activity/present.rb +0 -63
- data/lib/trailblazer/activity/trace.rb +0 -144
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0b971b7f8960520b05bcc99a2fe0561056d9ec244cf9faeb072e131604e0bc12
|
|
4
|
+
data.tar.gz: ec6e1d2c4b2de4d1edfce435c08029a061df61b1b770ad174c005733238269cb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5de05a662519033f02c6e58595d502e80970b08abe7bd87caa7ff23c6acae6490498453bcf8584b8ea6dcba14ef6f00298b1aeca75c5405f8e281e4d206e44e9
|
|
7
|
+
data.tar.gz: 35c9f6d275c0da422e0597529424f005af4e44a344c63c0ca8595d87a44ad8c474be21c5e9c985d1f1a01b2d1ff0b2020da06edcc29e9871af9c174c1766d7cb
|
data/CHANGES.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
# 0.9.4
|
|
2
|
+
|
|
3
|
+
Move some test helpers to `Activity::Testing` to export them to other gems
|
|
4
|
+
|
|
5
|
+
# 0.9.3
|
|
6
|
+
|
|
7
|
+
Remove introspection modules, it'll also be part of the `Dev` tools now.
|
|
8
|
+
|
|
9
|
+
# 0.9.2
|
|
10
|
+
|
|
11
|
+
Remove tracing modules, it'll be part of the `Dev` tools now.
|
|
12
|
+
|
|
1
13
|
# 0.9.1
|
|
2
14
|
|
|
3
15
|
Use `context-0.9.1`.
|
data/lib/trailblazer/activity.rb
CHANGED
|
@@ -49,10 +49,6 @@ require "trailblazer/activity/task_wrap/runner"
|
|
|
49
49
|
require "trailblazer/activity/task_wrap/variable_mapping"
|
|
50
50
|
require "trailblazer/activity/task_wrap/inject"
|
|
51
51
|
|
|
52
|
-
require "trailblazer/activity/trace"
|
|
53
|
-
require "trailblazer/activity/present"
|
|
54
|
-
|
|
55
|
-
require "trailblazer/activity/introspect"
|
|
56
52
|
require "trailblazer/option"
|
|
57
53
|
require "trailblazer/context"
|
|
58
54
|
require "trailblazer/activity/task_builder"
|
|
@@ -47,10 +47,85 @@ module Trailblazer
|
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
module Assertions
|
|
50
|
+
Activity = Trailblazer::Activity
|
|
51
|
+
Inter = Trailblazer::Activity::Schema::Intermediate
|
|
52
|
+
Schema = Trailblazer::Activity::Schema
|
|
53
|
+
TaskWrap = Trailblazer::Activity::TaskWrap
|
|
54
|
+
|
|
55
|
+
module Implementing
|
|
56
|
+
extend Activity::Testing.def_tasks(:a, :b, :c, :d, :f, :g)
|
|
57
|
+
|
|
58
|
+
Start = Activity::Start.new(semantic: :default)
|
|
59
|
+
Failure = Activity::End(:failure)
|
|
60
|
+
Success = Activity::End(:success)
|
|
61
|
+
end
|
|
62
|
+
|
|
50
63
|
def Cct(activity)
|
|
51
64
|
cct = Trailblazer::Developer::Render::Circuit.(activity)
|
|
52
65
|
end
|
|
53
66
|
|
|
67
|
+
# TODO: Remove this once all it's references are removed
|
|
68
|
+
def implementing
|
|
69
|
+
Implementing
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def flat_activity
|
|
73
|
+
return @_flat_activity if defined?(@_flat_activity)
|
|
74
|
+
|
|
75
|
+
intermediate = Inter.new(
|
|
76
|
+
{
|
|
77
|
+
Inter::TaskRef("Start.default") => [Inter::Out(:success, :B)],
|
|
78
|
+
Inter::TaskRef(:B, additional: true) => [Inter::Out(:success, :C)],
|
|
79
|
+
Inter::TaskRef(:C) => [Inter::Out(:success, "End.success")],
|
|
80
|
+
Inter::TaskRef("End.success", stop_event: true) => [Inter::Out(:success, nil)]
|
|
81
|
+
},
|
|
82
|
+
["End.success"],
|
|
83
|
+
["Start.default"], # start
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
implementation = {
|
|
87
|
+
"Start.default" => Schema::Implementation::Task(st = Implementing::Start, [Activity::Output(Activity::Right, :success)], []),
|
|
88
|
+
:B => Schema::Implementation::Task(b = Implementing.method(:b), [Activity::Output(Activity::Right, :success)], []),
|
|
89
|
+
:C => Schema::Implementation::Task(c = Implementing.method(:c), [Activity::Output(Activity::Right, :success)], []),
|
|
90
|
+
"End.success" => Schema::Implementation::Task(_es = Implementing::Success, [Activity::Output(Implementing::Success, :success)], []), # DISCUSS: End has one Output, signal is itself?
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
schema = Inter.(intermediate, implementation)
|
|
94
|
+
|
|
95
|
+
@_flat_activity = Activity.new(schema)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def nested_activity
|
|
99
|
+
return @_nested_activity if defined?(@_nested_activity)
|
|
100
|
+
|
|
101
|
+
intermediate = Inter.new(
|
|
102
|
+
{
|
|
103
|
+
Inter::TaskRef("Start.default") => [Inter::Out(:success, :B)],
|
|
104
|
+
Inter::TaskRef(:B, more: true) => [Inter::Out(:success, :D)],
|
|
105
|
+
Inter::TaskRef(:D) => [Inter::Out(:success, :E)],
|
|
106
|
+
Inter::TaskRef(:E) => [Inter::Out(:success, "End.success")],
|
|
107
|
+
Inter::TaskRef("End.success", stop_event: true) => [Inter::Out(:success, nil)]
|
|
108
|
+
},
|
|
109
|
+
["End.success"],
|
|
110
|
+
["Start.default"] # start
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
implementation = {
|
|
114
|
+
"Start.default" => Schema::Implementation::Task(st = Implementing::Start, [Activity::Output(Activity::Right, :success)], []),
|
|
115
|
+
:B => Schema::Implementation::Task(b = Implementing.method(:b), [Activity::Output(Activity::Right, :success)], []),
|
|
116
|
+
:D => Schema::Implementation::Task(c = bc, [Activity::Output(Implementing::Success, :success)], []),
|
|
117
|
+
:E => Schema::Implementation::Task(e = Implementing.method(:f), [Activity::Output(Activity::Right, :success)], []),
|
|
118
|
+
"End.success" => Schema::Implementation::Task(_es = Implementing::Success, [Activity::Output(Implementing::Success, :success)], []), # DISCUSS: End has one Output, signal is itself?
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
schema = Inter.(intermediate, implementation)
|
|
122
|
+
|
|
123
|
+
@_nested_activity = Activity.new(schema)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
alias_method :bc, :flat_activity
|
|
127
|
+
alias_method :bde, :nested_activity
|
|
128
|
+
|
|
54
129
|
# Tests {:circuit} and {:outputs} fields so far.
|
|
55
130
|
def assert_process_for(process, *args)
|
|
56
131
|
semantics, circuit = args[0..-2], args[-1]
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: trailblazer-activity
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.9.
|
|
4
|
+
version: 0.9.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nick Sutterer
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2019-09-
|
|
11
|
+
date: 2019-09-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: trailblazer-context
|
|
@@ -108,8 +108,6 @@ files:
|
|
|
108
108
|
- lib/trailblazer/activity.rb
|
|
109
109
|
- lib/trailblazer/activity/circuit.rb
|
|
110
110
|
- lib/trailblazer/activity/config.rb
|
|
111
|
-
- lib/trailblazer/activity/introspect.rb
|
|
112
|
-
- lib/trailblazer/activity/present.rb
|
|
113
111
|
- lib/trailblazer/activity/schema.rb
|
|
114
112
|
- lib/trailblazer/activity/schema/implementation.rb
|
|
115
113
|
- lib/trailblazer/activity/schema/intermediate.rb
|
|
@@ -122,7 +120,6 @@ files:
|
|
|
122
120
|
- lib/trailblazer/activity/task_wrap/runner.rb
|
|
123
121
|
- lib/trailblazer/activity/task_wrap/variable_mapping.rb
|
|
124
122
|
- lib/trailblazer/activity/testing.rb
|
|
125
|
-
- lib/trailblazer/activity/trace.rb
|
|
126
123
|
- lib/trailblazer/activity/version.rb
|
|
127
124
|
- trailblazer-activity.gemspec
|
|
128
125
|
homepage: http://trailblazer.to
|
|
@@ -145,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
145
142
|
version: '0'
|
|
146
143
|
requirements: []
|
|
147
144
|
rubyforge_project:
|
|
148
|
-
rubygems_version: 2.7.
|
|
145
|
+
rubygems_version: 2.7.3
|
|
149
146
|
signing_key:
|
|
150
147
|
specification_version: 4
|
|
151
148
|
summary: Runtime code for Trailblazer activities.
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
module Trailblazer
|
|
2
|
-
class Activity
|
|
3
|
-
# The Introspect API provides inflections for `Activity` instances.
|
|
4
|
-
# It abstracts internals about circuits and provides a convenient API to third-parties such as
|
|
5
|
-
# tracing, rendering an activity, or finding particular tasks.
|
|
6
|
-
module Introspect
|
|
7
|
-
def self.Graph(*args)
|
|
8
|
-
Graph.new(*args)
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
# TODO: order of step/fail/pass in Node would be cool to have
|
|
12
|
-
|
|
13
|
-
# @private This API is still under construction.
|
|
14
|
-
class Graph
|
|
15
|
-
def initialize(activity)
|
|
16
|
-
@activity = activity
|
|
17
|
-
@schema = activity.to_h or raise
|
|
18
|
-
@circuit = @schema[:circuit]
|
|
19
|
-
@map = @circuit.to_h[:map]
|
|
20
|
-
@configs = @schema[:nodes]
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def find(id=nil, &block)
|
|
24
|
-
return find_by_id(id) unless block_given?
|
|
25
|
-
find_with_block(&block)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def collect(strategy: :circuit, &block)
|
|
29
|
-
@map.keys.each_with_index.collect { |task, i| yield find_with_block { |node| node.task==task }, i }
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def stop_events
|
|
33
|
-
@circuit.to_h[:end_events]
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
private
|
|
37
|
-
|
|
38
|
-
def find_by_id(id)
|
|
39
|
-
node = @configs.find { |node| node.id == id } or return
|
|
40
|
-
node_for(node)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def find_with_block(&block)
|
|
44
|
-
existing = @configs.find { |node| yield Node(node.task, node.id, node.outputs, node.data) } or return
|
|
45
|
-
|
|
46
|
-
node_for(existing)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def node_for(node_attributes)
|
|
50
|
-
Node(node_attributes.task, node_attributes.id, node_attributes.outputs, outgoings_for(node_attributes), node_attributes.data)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def Node(*args)
|
|
54
|
-
Node.new(*args).freeze
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
Node = Struct.new(:task, :id, :outputs, :outgoings, :data)
|
|
58
|
-
Outgoing = Struct.new(:output, :task)
|
|
59
|
-
|
|
60
|
-
def outgoings_for(node)
|
|
61
|
-
outputs = node.outputs
|
|
62
|
-
connections = @map[node.task]
|
|
63
|
-
|
|
64
|
-
connections.collect do |signal, target|
|
|
65
|
-
output = outputs.find { |out| out.signal == signal }
|
|
66
|
-
Outgoing.new(output, target)
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end #Introspect
|
|
71
|
-
end
|
|
72
|
-
end
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
module Trailblazer
|
|
2
|
-
class Activity
|
|
3
|
-
# Task < Array
|
|
4
|
-
# [ input, ..., output ]
|
|
5
|
-
|
|
6
|
-
module Trace
|
|
7
|
-
# TODO: make this simpler.
|
|
8
|
-
module Present
|
|
9
|
-
module_function
|
|
10
|
-
|
|
11
|
-
INDENTATION = " |".freeze
|
|
12
|
-
STEP_PREFIX = "-- ".freeze
|
|
13
|
-
|
|
14
|
-
def default_renderer(task_node:, **)
|
|
15
|
-
[ task_node[:level], %{#{task_node[:name]}} ]
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def call(stack, level: 1, tree: [], renderer: method(:default_renderer), **options)
|
|
19
|
-
tree(stack.to_a, level, tree: tree, renderer: renderer, **options)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def tree(stack, level, tree:, renderer:, **options)
|
|
23
|
-
tree_for(stack, level, options.merge(tree: tree))
|
|
24
|
-
|
|
25
|
-
nodes = tree.each_with_index.map do |task_node, position|
|
|
26
|
-
renderer.(task_node: task_node, position: position, tree: tree)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
render_tree_for(nodes)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def render_tree_for(nodes)
|
|
33
|
-
nodes.map { |level, node|
|
|
34
|
-
indentation = INDENTATION * (level -1)
|
|
35
|
-
indentation = indentation[0...-1] + "`" if level == 1 || /End./.match(node) # start or end step
|
|
36
|
-
indentation + STEP_PREFIX + node
|
|
37
|
-
}.join("\n")
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def tree_for(stack, level, tree:, **options)
|
|
41
|
-
stack.each do |lvl| # always a Stack::Task[input, ..., output]
|
|
42
|
-
input, output, nested = Trace::Level.input_output_nested_for_level(lvl)
|
|
43
|
-
|
|
44
|
-
task = input.task
|
|
45
|
-
|
|
46
|
-
graph = Introspect::Graph(input.activity)
|
|
47
|
-
|
|
48
|
-
name = (node = graph.find { |node| node[:task] == task }) ? node[:id] : task
|
|
49
|
-
name ||= task # FIXME: bullshit
|
|
50
|
-
|
|
51
|
-
tree << { level: level, input: input, output: output, name: name, **options }
|
|
52
|
-
|
|
53
|
-
if nested.any? # nesting
|
|
54
|
-
tree_for(nested, level + 1, options.merge(tree: tree))
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
tree
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
end
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
module Trailblazer
|
|
2
|
-
class Activity
|
|
3
|
-
module Trace
|
|
4
|
-
class << self
|
|
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
|
-
|
|
16
|
-
def arguments_for_call(activity, (options, flow_options), **circuit_options)
|
|
17
|
-
tracing_flow_options = {
|
|
18
|
-
stack: Trace::Stack.new,
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
tracing_circuit_options = {
|
|
22
|
-
wrap_runtime: ::Hash.new(Trace.merge_plan), # DISCUSS: this overrides existing {:wrap_runtime}.
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return activity, [ options, tracing_flow_options.merge(flow_options) ], circuit_options.merge(tracing_circuit_options)
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
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
|
|
40
|
-
|
|
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)
|
|
44
|
-
|
|
45
|
-
return wrap_config, original_args
|
|
46
|
-
end
|
|
47
|
-
|
|
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]
|
|
51
|
-
|
|
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!
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
return wrap_config, original_args
|
|
60
|
-
end
|
|
61
|
-
|
|
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)
|
|
73
|
-
end
|
|
74
|
-
|
|
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
|
|
97
|
-
def inspect
|
|
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
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
# Mutable/stateful per design. We want a (global) stack!
|
|
113
|
-
class Stack
|
|
114
|
-
def initialize
|
|
115
|
-
@nested = Level.new
|
|
116
|
-
@stack = [ @nested ]
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def indent!
|
|
120
|
-
current << indented = Level.new
|
|
121
|
-
@stack << indented
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def unindent!
|
|
125
|
-
@stack.pop
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def <<(args)
|
|
129
|
-
current << args
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
def to_a
|
|
133
|
-
@nested
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
private
|
|
137
|
-
|
|
138
|
-
def current
|
|
139
|
-
@stack.last
|
|
140
|
-
end
|
|
141
|
-
end # Stack
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
end
|