ntl-orchestra 0.9.2 → 0.9.3
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/.travis.yml +3 -0
- data/Gemfile +1 -1
- data/README.md +91 -90
- data/lib/orchestra/conductor.rb +6 -6
- data/lib/orchestra/dsl/object_adapter.rb +16 -16
- data/lib/orchestra/dsl/operations.rb +35 -35
- data/lib/orchestra/dsl/{nodes.rb → steps.rb} +7 -9
- data/lib/orchestra/execution.rb +158 -0
- data/lib/orchestra/operation.rb +16 -16
- data/lib/orchestra/recording/playback.rb +47 -0
- data/lib/orchestra/recording.rb +5 -45
- data/lib/orchestra/run_list.rb +49 -49
- data/lib/orchestra/{node → step}/output.rb +8 -8
- data/lib/orchestra/{node.rb → step.rb} +22 -25
- data/lib/orchestra/thread_pool.rb +3 -3
- data/lib/orchestra/version.rb +1 -1
- data/lib/orchestra.rb +2 -2
- data/test/examples/fizz_buzz.rb +5 -5
- data/test/examples/invitation_service.rb +9 -9
- data/test/integration/multithreading_test.rb +5 -5
- data/test/integration/recording_telemetry_test.rb +3 -6
- data/test/integration/replayable_operation_test.rb +4 -4
- data/test/lib/console.rb +1 -1
- data/test/support/telemetry_recorder.rb +7 -7
- data/test/unit/dsl_test.rb +26 -26
- data/test/unit/object_adapter_test.rb +14 -14
- data/test/unit/operation_test.rb +45 -45
- data/test/unit/run_list_test.rb +12 -12
- data/test/unit/step_test.rb +122 -0
- data/test/unit/thread_pool_test.rb +2 -2
- metadata +15 -13
- data/lib/orchestra/performance.rb +0 -137
- data/test/unit/node_test.rb +0 -122
data/lib/orchestra/run_list.rb
CHANGED
@@ -1,52 +1,52 @@
|
|
1
1
|
module Orchestra
|
2
2
|
class RunList
|
3
|
-
def self.build
|
3
|
+
def self.build steps, result, input_names
|
4
4
|
builder = Builder.new result, input_names
|
5
|
-
builder.merge!
|
5
|
+
builder.merge! steps
|
6
6
|
builder.build
|
7
7
|
end
|
8
8
|
|
9
9
|
include Enumerable
|
10
10
|
|
11
|
-
def initialize
|
12
|
-
@
|
13
|
-
@
|
11
|
+
def initialize steps
|
12
|
+
@steps = steps
|
13
|
+
@steps.freeze
|
14
14
|
freeze
|
15
15
|
end
|
16
16
|
|
17
17
|
def each &block
|
18
18
|
return to_enum :each unless block_given?
|
19
|
-
@
|
19
|
+
@steps.each &block
|
20
20
|
end
|
21
21
|
|
22
|
-
def
|
23
|
-
@
|
22
|
+
def step_names
|
23
|
+
@steps.keys
|
24
24
|
end
|
25
25
|
|
26
26
|
def dependencies
|
27
|
-
optional =
|
28
|
-
required =
|
27
|
+
optional = collect_from_steps :optional_dependencies
|
28
|
+
required = collect_from_steps :required_dependencies
|
29
29
|
(optional + required).uniq
|
30
30
|
end
|
31
31
|
|
32
32
|
def optional_dependencies
|
33
|
-
|
33
|
+
collect_from_steps :optional_dependencies
|
34
34
|
end
|
35
35
|
|
36
36
|
def provisions
|
37
|
-
|
37
|
+
collect_from_steps :provisions
|
38
38
|
end
|
39
39
|
|
40
40
|
def required_dependencies
|
41
|
-
required_deps =
|
41
|
+
required_deps = collect_from_steps :required_dependencies
|
42
42
|
required_deps - optional_dependencies - provisions
|
43
43
|
end
|
44
44
|
|
45
45
|
private
|
46
46
|
|
47
|
-
def
|
48
|
-
set = @
|
49
|
-
deps =
|
47
|
+
def collect_from_steps method_name
|
48
|
+
set = @steps.each_with_object Set.new do |(_, step), set|
|
49
|
+
deps = step.public_send method_name
|
50
50
|
deps.each &set.method(:<<)
|
51
51
|
end
|
52
52
|
set.to_a.tap &:sort!
|
@@ -57,89 +57,89 @@ module Orchestra
|
|
57
57
|
|
58
58
|
def initialize result, input_names = []
|
59
59
|
@input_names = input_names
|
60
|
-
@
|
60
|
+
@steps_hash = {}
|
61
61
|
@required = [result]
|
62
62
|
@result = result
|
63
63
|
freeze
|
64
64
|
end
|
65
65
|
|
66
|
-
def merge!
|
67
|
-
|
68
|
-
self[name] =
|
66
|
+
def merge! steps
|
67
|
+
steps.each do |name, step|
|
68
|
+
self[name] = step
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
|
-
def []= name,
|
73
|
-
@
|
72
|
+
def []= name, step
|
73
|
+
@steps_hash[name] = step
|
74
74
|
end
|
75
75
|
|
76
|
-
def
|
77
|
-
@
|
76
|
+
def step_names
|
77
|
+
@steps_hash.keys
|
78
78
|
end
|
79
79
|
|
80
|
-
def
|
81
|
-
@
|
80
|
+
def steps
|
81
|
+
@steps_hash.values
|
82
82
|
end
|
83
83
|
|
84
84
|
def build
|
85
85
|
sort!
|
86
86
|
prune!
|
87
|
-
RunList.new @
|
87
|
+
RunList.new @steps_hash
|
88
88
|
end
|
89
89
|
|
90
90
|
def sort!
|
91
|
-
sorter = Sorter.new @
|
91
|
+
sorter = Sorter.new @steps_hash
|
92
92
|
sorter.sort!
|
93
93
|
end
|
94
94
|
|
95
95
|
def prune!
|
96
|
-
|
97
|
-
removed.<< remove
|
98
|
-
require
|
96
|
+
steps.reverse_each.with_object [] do |step, removed|
|
97
|
+
removed.<< remove step and next unless required? step
|
98
|
+
require step
|
99
99
|
end
|
100
100
|
end
|
101
101
|
|
102
|
-
def remove
|
103
|
-
@
|
104
|
-
|
102
|
+
def remove step
|
103
|
+
@steps_hash.reject! do |_, n| n == step end
|
104
|
+
step
|
105
105
|
end
|
106
106
|
|
107
|
-
def require
|
107
|
+
def require step
|
108
108
|
supplied_by_input = input_names.method :include?
|
109
|
-
deps =
|
109
|
+
deps = step.required_dependencies.reject &supplied_by_input
|
110
110
|
@required.concat deps
|
111
111
|
true
|
112
112
|
end
|
113
113
|
|
114
|
-
def required?
|
114
|
+
def required? step
|
115
115
|
required = @required.method :include?
|
116
|
-
|
116
|
+
step.provisions.any? &required
|
117
117
|
end
|
118
118
|
|
119
119
|
class Sorter
|
120
120
|
include TSort
|
121
121
|
|
122
|
-
def initialize
|
123
|
-
@
|
122
|
+
def initialize steps_hash
|
123
|
+
@steps = steps_hash
|
124
124
|
end
|
125
125
|
|
126
126
|
def sort!
|
127
127
|
build_dependency_tree
|
128
128
|
tsort.each do |name|
|
129
|
-
@
|
129
|
+
@steps[name] = @steps.delete name
|
130
130
|
end
|
131
131
|
rescue TSort::Cyclic
|
132
132
|
raise CircularDependencyError.new
|
133
133
|
end
|
134
134
|
|
135
135
|
def build_dependency_tree
|
136
|
-
@hsh = @
|
137
|
-
hsh[name] = build_dependencies_for
|
136
|
+
@hsh = @steps.each_with_object Hash.new do |(name, step), hsh|
|
137
|
+
hsh[name] = build_dependencies_for step
|
138
138
|
end
|
139
139
|
end
|
140
140
|
|
141
|
-
def build_dependencies_for
|
142
|
-
|
141
|
+
def build_dependencies_for step
|
142
|
+
step.required_dependencies.each_with_object Set.new do |dep, set|
|
143
143
|
provider = provider_for dep
|
144
144
|
set << provider if provider
|
145
145
|
end
|
@@ -155,15 +155,15 @@ module Orchestra
|
|
155
155
|
end
|
156
156
|
|
157
157
|
def provider_for dep
|
158
|
-
@
|
159
|
-
provisions = effective_provisions_for
|
158
|
+
@steps.each do |name, step|
|
159
|
+
provisions = effective_provisions_for step, dep
|
160
160
|
return name if provisions.include? dep
|
161
161
|
end
|
162
162
|
nil
|
163
163
|
end
|
164
164
|
|
165
|
-
def effective_provisions_for
|
166
|
-
|
165
|
+
def effective_provisions_for step, dep
|
166
|
+
step.optional_dependencies | step.provisions
|
167
167
|
end
|
168
168
|
end
|
169
169
|
end
|
@@ -1,25 +1,25 @@
|
|
1
1
|
module Orchestra
|
2
|
-
class
|
2
|
+
class Step
|
3
3
|
class Output
|
4
|
-
attr :hsh, :
|
4
|
+
attr :hsh, :step, :raw
|
5
5
|
|
6
|
-
def self.process
|
7
|
-
instance = new
|
6
|
+
def self.process step, raw
|
7
|
+
instance = new step, raw
|
8
8
|
instance.massage
|
9
9
|
instance.hsh
|
10
10
|
end
|
11
11
|
|
12
|
-
def initialize
|
13
|
-
@
|
12
|
+
def initialize step, raw
|
13
|
+
@step = step
|
14
14
|
@raw = raw
|
15
15
|
end
|
16
16
|
|
17
17
|
def provisions
|
18
|
-
|
18
|
+
step.provisions
|
19
19
|
end
|
20
20
|
|
21
21
|
def collection?
|
22
|
-
|
22
|
+
step.collection?
|
23
23
|
end
|
24
24
|
|
25
25
|
def massage
|
@@ -1,7 +1,5 @@
|
|
1
1
|
module Orchestra
|
2
|
-
class
|
3
|
-
autoload :Output, "orchestra/node/output"
|
4
|
-
|
2
|
+
class Step
|
5
3
|
attr :collection, :dependencies, :provisions
|
6
4
|
|
7
5
|
def initialize args = {}
|
@@ -27,16 +25,15 @@ module Orchestra
|
|
27
25
|
collection ? true : false
|
28
26
|
end
|
29
27
|
|
30
|
-
def
|
31
|
-
|
32
|
-
Performance::Movement.perform self, performance
|
28
|
+
def execute input = {}
|
29
|
+
Execution.execute_step self, input
|
33
30
|
end
|
34
31
|
|
35
32
|
def process raw_output
|
36
33
|
Output.process self, raw_output
|
37
34
|
end
|
38
35
|
|
39
|
-
class
|
36
|
+
class ObjectStep < Step
|
40
37
|
attr :adapter
|
41
38
|
|
42
39
|
def initialize adapter, args = {}
|
@@ -53,32 +50,32 @@ module Orchestra
|
|
53
50
|
end
|
54
51
|
end
|
55
52
|
|
56
|
-
class
|
53
|
+
class InlineStep < Step
|
57
54
|
def self.build &block
|
58
|
-
builder = DSL::
|
59
|
-
DSL::
|
60
|
-
builder.
|
55
|
+
builder = DSL::Steps::Builder.new
|
56
|
+
DSL::Steps::Context.evaluate builder, &block
|
57
|
+
builder.build_step
|
61
58
|
end
|
62
59
|
|
63
|
-
attr :context_class, :defaults, :
|
60
|
+
attr :context_class, :defaults, :execute_block
|
64
61
|
|
65
62
|
def initialize args = {}
|
66
63
|
@defaults = args.delete :defaults do {} end
|
67
|
-
@
|
68
|
-
args.delete :
|
64
|
+
@execute_block = args.fetch :execute_block
|
65
|
+
args.delete :execute_block
|
69
66
|
super args
|
70
67
|
@context_class = build_execution_context_class
|
71
68
|
validate!
|
72
69
|
end
|
73
70
|
|
74
71
|
def validate!
|
75
|
-
unless
|
76
|
-
raise ArgumentError, "expected inline
|
72
|
+
unless execute_block
|
73
|
+
raise ArgumentError, "expected inline step to define a execute block"
|
77
74
|
end
|
78
75
|
end
|
79
76
|
|
80
77
|
def build_execution_context_class
|
81
|
-
context = Class.new
|
78
|
+
context = Class.new InlineContext
|
82
79
|
context.class_exec dependencies, collection do |deps, collection|
|
83
80
|
deps.each do |dep| define_dependency dep end
|
84
81
|
alias_method :fetch_collection, collection if collection
|
@@ -88,7 +85,7 @@ module Orchestra
|
|
88
85
|
|
89
86
|
def build_context input
|
90
87
|
state = apply_defaults input
|
91
|
-
execution_context = context_class.new state,
|
88
|
+
execution_context = context_class.new state, execute_block
|
92
89
|
end
|
93
90
|
|
94
91
|
def apply_defaults input
|
@@ -103,7 +100,7 @@ module Orchestra
|
|
103
100
|
defaults.keys
|
104
101
|
end
|
105
102
|
|
106
|
-
class
|
103
|
+
class InlineContext
|
107
104
|
def self.define_dependency dep
|
108
105
|
define_method dep do
|
109
106
|
ivar = "@#{dep}"
|
@@ -112,16 +109,16 @@ module Orchestra
|
|
112
109
|
end
|
113
110
|
end
|
114
111
|
|
115
|
-
def initialize state,
|
116
|
-
@
|
112
|
+
def initialize state, execute_block
|
113
|
+
@__execute_block__ = execute_block
|
117
114
|
@__state__ = state
|
118
115
|
end
|
119
116
|
|
120
|
-
def
|
121
|
-
if @
|
122
|
-
instance_exec &@
|
117
|
+
def execute item = nil
|
118
|
+
if @__execute_block__.arity == 0
|
119
|
+
instance_exec &@__execute_block__
|
123
120
|
else
|
124
|
-
instance_exec item, &@
|
121
|
+
instance_exec item, &@__execute_block__
|
125
122
|
end
|
126
123
|
end
|
127
124
|
end
|
@@ -28,7 +28,7 @@ module Orchestra
|
|
28
28
|
job
|
29
29
|
end
|
30
30
|
|
31
|
-
def
|
31
|
+
def execute &work
|
32
32
|
job = enqueue &work
|
33
33
|
job.wait
|
34
34
|
end
|
@@ -99,7 +99,7 @@ module Orchestra
|
|
99
99
|
def thread_loop
|
100
100
|
Thread.current.abort_on_exception = false
|
101
101
|
until (job = queue.pop) == :terminate
|
102
|
-
job.
|
102
|
+
job.execute
|
103
103
|
Thread.pass
|
104
104
|
end
|
105
105
|
rescue => error
|
@@ -127,7 +127,7 @@ module Orchestra
|
|
127
127
|
not @output_queue.empty?
|
128
128
|
end
|
129
129
|
|
130
|
-
def
|
130
|
+
def execute
|
131
131
|
@output_queue.push block.call
|
132
132
|
end
|
133
133
|
|
data/lib/orchestra/version.rb
CHANGED
data/lib/orchestra.rb
CHANGED
@@ -10,8 +10,8 @@ module Orchestra
|
|
10
10
|
Configuration.module_eval &block
|
11
11
|
end
|
12
12
|
|
13
|
-
def
|
14
|
-
Conductor.new.
|
13
|
+
def execute operation, inputs = {}
|
14
|
+
Conductor.new.execute operation, inputs
|
15
15
|
end
|
16
16
|
|
17
17
|
def replay_recording operation, store, input = {}
|
data/test/examples/fizz_buzz.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
module Examples
|
2
2
|
FizzBuzz = Orchestra::Operation.new do
|
3
|
-
|
3
|
+
step :make_array do
|
4
4
|
depends_on :up_to
|
5
5
|
provides :array
|
6
|
-
|
6
|
+
execute do
|
7
7
|
up_to.times.to_a
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
|
11
|
+
step :apply_fizzbuzz do
|
12
12
|
iterates_over :array
|
13
13
|
provides :fizzbuzz
|
14
|
-
|
14
|
+
execute do |num|
|
15
15
|
next if num == 0 # filter 0 from the output
|
16
16
|
str = ''
|
17
17
|
str << "Fizz" if num % 3 == 0
|
@@ -24,7 +24,7 @@ module Examples
|
|
24
24
|
finally :print do
|
25
25
|
depends_on :io
|
26
26
|
iterates_over :fizzbuzz
|
27
|
-
|
27
|
+
execute do |str|
|
28
28
|
io.puts str
|
29
29
|
end
|
30
30
|
end
|
@@ -3,28 +3,28 @@ module Examples
|
|
3
3
|
DEFAULT_MESSAGE = "I would really love for you to try out MyApp."
|
4
4
|
ROBOT_FOLLOWER_THRESHHOLD = 500
|
5
5
|
|
6
|
-
|
6
|
+
step :fetch_followers do
|
7
7
|
depends_on :account_name, :http
|
8
8
|
provides :followers
|
9
|
-
|
9
|
+
execute do
|
10
10
|
json = http.get "flutter.io", "/users/#{account_name}/followers"
|
11
11
|
JSON.parse json
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
|
15
|
+
step :fetch_blacklist do
|
16
16
|
depends_on :db
|
17
17
|
provides :blacklist
|
18
|
-
|
18
|
+
execute do
|
19
19
|
rows = db.execute "SELECT account_name FROM blacklists"
|
20
20
|
rows.map do |row| row.fetch 0 end
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
|
24
|
+
step :remove_blacklisted_followers do
|
25
25
|
depends_on :blacklist
|
26
26
|
modifies :followers
|
27
|
-
|
27
|
+
execute do
|
28
28
|
followers.reject! do |follower|
|
29
29
|
account_name = follower.fetch 'username'
|
30
30
|
blacklist.include? account_name
|
@@ -32,10 +32,10 @@ module Examples
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
-
|
35
|
+
step :filter_robots do
|
36
36
|
depends_on :http
|
37
37
|
modifies :followers, :collection => true
|
38
|
-
|
38
|
+
execute do |follower|
|
39
39
|
account_name = follower.fetch 'username'
|
40
40
|
json = http.get "flutter.io", "/users/#{account_name}"
|
41
41
|
account = JSON.load json
|
@@ -48,7 +48,7 @@ module Examples
|
|
48
48
|
finally :deliver_emails do
|
49
49
|
depends_on :smtp, :message => DEFAULT_MESSAGE
|
50
50
|
iterates_over :followers
|
51
|
-
|
51
|
+
execute do |follower|
|
52
52
|
email = follower.fetch 'email_address'
|
53
53
|
smtp.deliver message, :to => email
|
54
54
|
end
|
@@ -3,10 +3,10 @@ class MultithreadingTest < Minitest::Test
|
|
3
3
|
|
4
4
|
def setup
|
5
5
|
@operation = Orchestra::Operation.new do
|
6
|
-
|
6
|
+
step :map_thread_ids do
|
7
7
|
iterates_over :list
|
8
8
|
provides :thread_ids
|
9
|
-
|
9
|
+
execute do |item|
|
10
10
|
raise CustomError, "blow up" if item == :blow_up
|
11
11
|
Thread.current.object_id
|
12
12
|
end
|
@@ -22,12 +22,12 @@ class MultithreadingTest < Minitest::Test
|
|
22
22
|
def test_multithreading
|
23
23
|
list = (1..1000).to_a
|
24
24
|
|
25
|
-
thread_ids = @conductor.
|
25
|
+
thread_ids = @conductor.execute @operation, :list => list
|
26
26
|
|
27
27
|
assert_equal(
|
28
28
|
@conductor.thread_count,
|
29
29
|
thread_ids.uniq.size,
|
30
|
-
"
|
30
|
+
"execution must be spread across threads",
|
31
31
|
)
|
32
32
|
end
|
33
33
|
|
@@ -36,7 +36,7 @@ class MultithreadingTest < Minitest::Test
|
|
36
36
|
list[23] = :blow_up
|
37
37
|
|
38
38
|
assert_raises CustomError do
|
39
|
-
@conductor.
|
39
|
+
@conductor.execute @operation, :list => list
|
40
40
|
end
|
41
41
|
end
|
42
42
|
end
|
@@ -3,7 +3,7 @@ class RecordingTelemetryTest < Minitest::Test
|
|
3
3
|
output = StringIO.new
|
4
4
|
telemetry = {}
|
5
5
|
|
6
|
-
|
6
|
+
execute_with_telemetry telemetry, output
|
7
7
|
|
8
8
|
assert_equal_telemetry expected_telemetry, telemetry
|
9
9
|
end
|
@@ -73,14 +73,11 @@ class RecordingTelemetryTest < Minitest::Test
|
|
73
73
|
}
|
74
74
|
end
|
75
75
|
|
76
|
-
def
|
76
|
+
def execute_with_telemetry telemetry, io
|
77
77
|
conductor = Orchestra::Conductor.new :io => io
|
78
78
|
|
79
79
|
conductor.add_observer TelemetryRecorder.new telemetry
|
80
80
|
|
81
|
-
conductor.
|
82
|
-
Examples::FizzBuzz,
|
83
|
-
:up_to => 16,
|
84
|
-
)
|
81
|
+
conductor.execute Examples::FizzBuzz, :up_to => 16
|
85
82
|
end
|
86
83
|
end
|
@@ -2,8 +2,8 @@ class ReplayableOperationTest < Minitest::Test
|
|
2
2
|
include Examples::InvitationService::TestSetup
|
3
3
|
|
4
4
|
def test_replaying_an_operation_from_a_previous_recording
|
5
|
-
#
|
6
|
-
recording =
|
5
|
+
# Execute the operation against real services, saving a recording
|
6
|
+
recording = execute_for_real
|
7
7
|
|
8
8
|
# Write the recording out to a file. In this case, a StringIO is used for
|
9
9
|
# simplicity, and we serialize into JSON
|
@@ -29,7 +29,7 @@ class ReplayableOperationTest < Minitest::Test
|
|
29
29
|
|
30
30
|
private
|
31
31
|
|
32
|
-
def
|
32
|
+
def execute_for_real
|
33
33
|
mock_smtp = build_example_smtp
|
34
34
|
db = build_example_database
|
35
35
|
stub_followers_request
|
@@ -48,6 +48,6 @@ class ReplayableOperationTest < Minitest::Test
|
|
48
48
|
|
49
49
|
db.close
|
50
50
|
|
51
|
-
recording
|
51
|
+
recording
|
52
52
|
end
|
53
53
|
end
|
data/test/lib/console.rb
CHANGED
@@ -15,7 +15,7 @@ develompent environments that rely on binstubs.
|
|
15
15
|
The example operations located in `test/examples` are loaded for you. To try one
|
16
16
|
out:
|
17
17
|
|
18
|
-
[1] pry(Orchestra)> Orchestra.
|
18
|
+
[1] pry(Orchestra)> Orchestra.execute Examples::FizzBuzz, :up_to => 16, :io => $stdout
|
19
19
|
|
20
20
|
See the README.md as well as the examples themselves for more information. You
|
21
21
|
will need to reload the examples after raking (or invoking reload!)
|
@@ -16,11 +16,11 @@ class TelemetryRecorder
|
|
16
16
|
|
17
17
|
def handle_operation_entered operation_name, input
|
18
18
|
return if embedded?
|
19
|
-
@
|
19
|
+
@steps = Hash.new do |hsh, key| hsh[key] = {} end
|
20
20
|
@store.update(
|
21
21
|
:input => input,
|
22
|
-
:movements => @
|
23
|
-
:
|
22
|
+
:movements => @steps,
|
23
|
+
:operation_name => operation_name,
|
24
24
|
:service_calls => [],
|
25
25
|
)
|
26
26
|
@embedded = true
|
@@ -31,12 +31,12 @@ class TelemetryRecorder
|
|
31
31
|
@embedded = false
|
32
32
|
end
|
33
33
|
|
34
|
-
def
|
35
|
-
@
|
34
|
+
def handle_step_entered name, input
|
35
|
+
@steps[name][:input] = input
|
36
36
|
end
|
37
37
|
|
38
|
-
def
|
39
|
-
@
|
38
|
+
def handle_step_exited name, output
|
39
|
+
@steps[name][:output] = output
|
40
40
|
end
|
41
41
|
|
42
42
|
def handle_error_raised error
|