aws-flow 1.0.5 → 1.0.6
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.
- data/lib/aws/decider.rb +0 -9
- data/lib/aws/decider/async_decider.rb +13 -12
- data/lib/aws/decider/decider.rb +2 -2
- data/lib/aws/decider/generic_client.rb +4 -0
- data/lib/aws/decider/task_poller.rb +5 -2
- data/lib/aws/decider/version.rb +1 -1
- data/lib/aws/decider/workflow_client.rb +17 -3
- data/lib/aws/flow.rb +29 -0
- data/lib/aws/flow/async_backtrace.rb +134 -0
- data/lib/aws/flow/async_scope.rb +195 -0
- data/lib/aws/flow/begin_rescue_ensure.rb +386 -0
- data/lib/aws/flow/fiber.rb +77 -0
- data/lib/aws/flow/flow_utils.rb +50 -0
- data/lib/aws/flow/future.rb +115 -0
- data/lib/aws/flow/implementation.rb +151 -0
- data/lib/aws/flow/simple_dfa.rb +85 -0
- data/lib/aws/flow/tasks.rb +405 -0
- data/test/aws/async_backtrace_spec.rb +41 -0
- data/test/aws/async_scope_spec.rb +118 -0
- data/test/aws/begin_rescue_ensure_spec.rb +665 -0
- data/test/aws/decider_spec.rb +155 -35
- data/test/aws/external_task_spec.rb +197 -0
- data/test/aws/factories.rb +30 -1
- data/test/aws/fiber_condition_variable_spec.rb +163 -0
- data/test/aws/fiber_spec.rb +78 -0
- data/test/aws/flow_spec.rb +255 -0
- data/test/aws/future_spec.rb +210 -0
- data/test/aws/integration_spec.rb +83 -39
- data/test/aws/rubyflow.rb +22 -0
- data/test/aws/simple_dfa_spec.rb +63 -0
- data/test/aws/spec_helper.rb +11 -5
- metadata +23 -3
data/test/aws/decider_spec.rb
CHANGED
@@ -30,6 +30,14 @@ class TrivialConverter
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
+
class FakeLogger
|
34
|
+
attr_accessor :level
|
35
|
+
def info(s); end
|
36
|
+
def debug(s); end
|
37
|
+
def warn(s); end
|
38
|
+
def error(s); end
|
39
|
+
end
|
40
|
+
|
33
41
|
class FakePage
|
34
42
|
def initialize(object); @object = object; end
|
35
43
|
def page; @object; end
|
@@ -1095,7 +1103,7 @@ describe "FakeHistory" do
|
|
1095
1103
|
BadWorkflow.trace.should == [:start, :middle, :end]
|
1096
1104
|
end
|
1097
1105
|
|
1098
|
-
it "makes sure that
|
1106
|
+
it "makes sure that raising an error properly fails a workflow" do
|
1099
1107
|
class SynchronousWorkflowTaskPoller < WorkflowTaskPoller
|
1100
1108
|
def get_decision_tasks
|
1101
1109
|
fake_workflow_type = FakeWorkflowType.new(nil, "BadWorkflow.entry_point", "1")
|
@@ -1185,6 +1193,85 @@ describe "FakeHistory" do
|
|
1185
1193
|
worker.start
|
1186
1194
|
swf_client.trace.first[:decisions].first[:start_timer_decision_attributes][:start_to_fire_timeout].should == "5"
|
1187
1195
|
end
|
1196
|
+
|
1197
|
+
it "ensures that CompleteWorkflowExecutionFailed is correctly handled" do
|
1198
|
+
class FakeAttribute
|
1199
|
+
def initialize(data)
|
1200
|
+
@data = data
|
1201
|
+
end
|
1202
|
+
def method_missing(method_name, *args, &block)
|
1203
|
+
if @data.keys.include? method_name
|
1204
|
+
return @data[method_name]
|
1205
|
+
end
|
1206
|
+
super
|
1207
|
+
end
|
1208
|
+
end
|
1209
|
+
|
1210
|
+
class SynchronousWorkflowTaskPoller < WorkflowTaskPoller
|
1211
|
+
def get_decision_tasks
|
1212
|
+
fake_workflow_type = FakeWorkflowType.new(nil, "CompleteWorkflowExecutionFailedWorkflow.entry_point", "1")
|
1213
|
+
TestHistoryWrapper.new(fake_workflow_type,
|
1214
|
+
[
|
1215
|
+
TestHistoryEvent.new("WorkflowExecutionStarted", 1, {}),
|
1216
|
+
TestHistoryEvent.new("DecisionTaskScheduled", 2, {}),
|
1217
|
+
TestHistoryEvent.new("DecisionTaskStarted", 3, {}),
|
1218
|
+
TestHistoryEvent.new("DecisionTaskCompleted", 4, {}),
|
1219
|
+
TestHistoryEvent.new("ActivityTaskScheduled", 5, {:activity_id => "Activity1"}),
|
1220
|
+
TestHistoryEvent.new("ActivityTaskScheduled", 6, {:activity_id => "Activity2"}),
|
1221
|
+
TestHistoryEvent.new("ActivityTaskStarted", 7, {}),
|
1222
|
+
TestHistoryEvent.new("ActivityTaskFailed", 8, {:scheduled_event_id => 5}),
|
1223
|
+
TestHistoryEvent.new("DecisionTaskScheduled", 9, {}),
|
1224
|
+
TestHistoryEvent.new("ActivityTaskStarted", 10, {}),
|
1225
|
+
TestHistoryEvent.new("ActivityTaskFailed", 11, {:scheduled_event_id => 6}),
|
1226
|
+
TestHistoryEvent.new("DecisionTaskStarted", 12, {}),
|
1227
|
+
TestHistoryEvent.new("DecisionTaskCompleted", 13, {}),
|
1228
|
+
TestHistoryEvent.new("RequestCancelActivityTaskFailed", 14, FakeAttribute.new({:activity_id => "Activity2"}) ) ,
|
1229
|
+
TestHistoryEvent.new("CompleteWorkflowExecutionFailed", 15, {}),
|
1230
|
+
TestHistoryEvent.new("DecisionTaskScheduled", 16, {}),
|
1231
|
+
])
|
1232
|
+
end
|
1233
|
+
end
|
1234
|
+
workflow_type_object = double("workflow_type", :name => "CompleteWorkflowExecutionFailedWorkflow.entry_point", :start_execution => "" )
|
1235
|
+
domain = FakeDomain.new(workflow_type_object)
|
1236
|
+
|
1237
|
+
class CompleteWorkflowExecutionFailedActivity
|
1238
|
+
extend Activity
|
1239
|
+
activity :run_activity1
|
1240
|
+
def run_activity1; raise StandardError; end
|
1241
|
+
end
|
1242
|
+
class CompleteWorkflowExecutionFailedWorkflow
|
1243
|
+
extend Workflows
|
1244
|
+
workflow(:entry_point) { {:version => "1"} }
|
1245
|
+
activity_client(:activity) { {:version => "1", :prefix_name => "CompleteWorkflowExecutionFailedActivity" } }
|
1246
|
+
def entry_point
|
1247
|
+
child_futures = []
|
1248
|
+
error_handler do |t|
|
1249
|
+
t.begin do
|
1250
|
+
child_futures << activity.send_async(:run_activity1)
|
1251
|
+
child_futures << activity.send_async(:run_activity1)
|
1252
|
+
wait_for_all(child_futures)
|
1253
|
+
end
|
1254
|
+
t.rescue(Exception) do |error|
|
1255
|
+
end
|
1256
|
+
t.ensure do
|
1257
|
+
end
|
1258
|
+
end
|
1259
|
+
end
|
1260
|
+
end
|
1261
|
+
swf_client = FakeServiceClient.new
|
1262
|
+
task_list = "CompleteWorkflowExecutionFailedWorkflow_tasklist"
|
1263
|
+
my_workflow_factory = workflow_factory(swf_client, domain) do |options|
|
1264
|
+
options.workflow_name = "CompleteWorkflowExecutionFailedWorkflow"
|
1265
|
+
options.execution_start_to_close_timeout = 3600
|
1266
|
+
options.task_list = task_list
|
1267
|
+
end
|
1268
|
+
worker = SynchronousWorkflowWorker.new(swf_client, domain, task_list)
|
1269
|
+
worker.add_workflow_implementation(CompleteWorkflowExecutionFailedWorkflow)
|
1270
|
+
my_workflow = my_workflow_factory.get_client
|
1271
|
+
workflow_execution = my_workflow.start_execution
|
1272
|
+
worker.start
|
1273
|
+
swf_client.trace.first[:decisions].first[:decision_type].should == "CompleteWorkflowExecution"
|
1274
|
+
end
|
1188
1275
|
end
|
1189
1276
|
|
1190
1277
|
describe "Misc tests" do
|
@@ -1224,6 +1311,33 @@ describe "Misc tests" do
|
|
1224
1311
|
end
|
1225
1312
|
|
1226
1313
|
|
1314
|
+
it "ensures that using send_async doesn't mutate the original hash" do
|
1315
|
+
class GenericClientTest < GenericClient
|
1316
|
+
def call_options(*args, &options)
|
1317
|
+
options.call
|
1318
|
+
end
|
1319
|
+
end
|
1320
|
+
# Instead of setting up the fiber, just pretend we're internal
|
1321
|
+
module Utilities
|
1322
|
+
class << self
|
1323
|
+
alias_method :old_is_external, :is_external
|
1324
|
+
def is_external
|
1325
|
+
return false
|
1326
|
+
end
|
1327
|
+
end
|
1328
|
+
end
|
1329
|
+
generic_client = GenericClientTest.new
|
1330
|
+
previous_hash = {:key => :value}
|
1331
|
+
previous_hash_copy = previous_hash.dup
|
1332
|
+
generic_client.send_async(:call_options) { previous_hash }
|
1333
|
+
# Put is_external back before we have a chance of failing
|
1334
|
+
module Utilities
|
1335
|
+
class << self
|
1336
|
+
alias_method :is_external, :old_is_external
|
1337
|
+
end
|
1338
|
+
end
|
1339
|
+
previous_hash.should == previous_hash_copy
|
1340
|
+
end
|
1227
1341
|
end
|
1228
1342
|
|
1229
1343
|
describe FlowConstants do
|
@@ -1280,40 +1394,43 @@ class TestActivityWorker < ActivityWorker
|
|
1280
1394
|
|
1281
1395
|
attr_accessor :executor
|
1282
1396
|
def initialize(service, domain, task_list, forking_executor, *args, &block)
|
1283
|
-
super(service, domain, task_list, *args)
|
1397
|
+
super(service, domain, task_list, *args, &block)
|
1284
1398
|
@executor = forking_executor
|
1285
1399
|
end
|
1286
1400
|
end
|
1287
1401
|
|
1288
1402
|
describe ActivityWorker do
|
1289
1403
|
|
1290
|
-
it "will test whether the ActivityWorker shuts down cleanly when an interrupt is received" do
|
1291
|
-
|
1292
|
-
|
1293
|
-
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
1297
|
-
|
1298
|
-
|
1299
|
-
|
1300
|
-
|
1301
|
-
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
1305
|
-
|
1306
|
-
|
1307
|
-
|
1308
|
-
|
1309
|
-
|
1310
|
-
|
1311
|
-
|
1312
|
-
|
1313
|
-
|
1314
|
-
|
1315
|
-
|
1316
|
-
#
|
1404
|
+
# it "will test whether the ActivityWorker shuts down cleanly when an interrupt is received" do
|
1405
|
+
|
1406
|
+
# task_list = "TestWorkflow_tasklist"
|
1407
|
+
# service = FakeServiceClient.new
|
1408
|
+
# workflow_type_object = double("workflow_type", :name => "TestWorkflow.entry_point", :start_execution => "" )
|
1409
|
+
# domain = FakeDomain.new(workflow_type_object)
|
1410
|
+
# forking_executor = ForkingExecutor.new
|
1411
|
+
# activity_worker = TestActivityWorker.new(service, domain, task_list, forking_executor) { {:logger => FakeLogger.new} }
|
1412
|
+
|
1413
|
+
# activity_worker.add_activities_implementation(TestActivity)
|
1414
|
+
# # Starts the activity worker in a forked process. Also, attaches an at_exit
|
1415
|
+
# # handler to the process. When the process exits, the handler checks whether
|
1416
|
+
# # the executor's internal is_shutdown variable is set correctly or not.
|
1417
|
+
# pid = fork do
|
1418
|
+
# at_exit {
|
1419
|
+
# activity_worker.executor.is_shutdown.should == true
|
1420
|
+
# }
|
1421
|
+
# activity_worker.start true
|
1422
|
+
# end
|
1423
|
+
# # Adding a sleep to let things get setup correctly (not ideal but going with
|
1424
|
+
# # this for now)
|
1425
|
+
# #sleep 1
|
1426
|
+
# # Send an interrupt to the child process
|
1427
|
+
# Process.kill("INT", pid)
|
1428
|
+
# status = Process.waitall
|
1429
|
+
# status[0][1].success?.should be_true
|
1430
|
+
# end
|
1431
|
+
|
1432
|
+
# This method will take a long time to run, allowing us to test our shutdown
|
1433
|
+
# scenarios
|
1317
1434
|
def dumb_fib(n)
|
1318
1435
|
n < 1 ? 1 : dumb_fib(n - 1) + dumb_fib(n - 2)
|
1319
1436
|
end
|
@@ -1324,20 +1441,23 @@ describe ActivityWorker do
|
|
1324
1441
|
workflow_type_object = double("workflow_type", :name => "TestWorkflow.entry_point", :start_execution => "" )
|
1325
1442
|
domain = FakeDomain.new(workflow_type_object)
|
1326
1443
|
forking_executor = ForkingExecutor.new
|
1327
|
-
activity_worker = TestActivityWorker.new(service, domain, task_list, forking_executor)
|
1444
|
+
activity_worker = TestActivityWorker.new(service, domain, task_list, forking_executor) { {:logger => FakeLogger.new} }
|
1328
1445
|
|
1329
1446
|
activity_worker.add_activities_implementation(TestActivity)
|
1330
|
-
# Starts the activity worker in a forked process. Also, executes a task
|
1331
|
-
#
|
1332
|
-
#
|
1447
|
+
# Starts the activity worker in a forked process. Also, executes a task
|
1448
|
+
# using the forking executor of the activity worker. The executor will
|
1449
|
+
# create a child process to run that task. The task (dumb_fib) is
|
1450
|
+
# purposefully designed to be long running so that we can test our shutdown
|
1451
|
+
# scenario.
|
1333
1452
|
pid = fork do
|
1334
1453
|
activity_worker.executor.execute {
|
1335
1454
|
dumb_fib(1000)
|
1336
1455
|
}
|
1337
1456
|
activity_worker.start true
|
1338
1457
|
end
|
1339
|
-
# Adding a sleep to let things get setup correctly (not idea but going with
|
1340
|
-
|
1458
|
+
# Adding a sleep to let things get setup correctly (not idea but going with
|
1459
|
+
# this for now)
|
1460
|
+
sleep 3
|
1341
1461
|
# Send 2 interrupts to the child process
|
1342
1462
|
2.times { Process.kill("INT", pid); sleep 3 }
|
1343
1463
|
status = Process.waitall
|
@@ -0,0 +1,197 @@
|
|
1
|
+
##
|
2
|
+
# Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License").
|
5
|
+
# You may not use this file except in compliance with the License.
|
6
|
+
# A copy of the License is located at
|
7
|
+
#
|
8
|
+
# http://aws.amazon.com/apache2.0
|
9
|
+
#
|
10
|
+
# or in the "license" file accompanying this file. This file is distributed
|
11
|
+
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
12
|
+
# express or implied. See the License for the specific language governing
|
13
|
+
# permissions and limitations under the License.
|
14
|
+
##
|
15
|
+
|
16
|
+
describe "ExternalTask#alive?" do
|
17
|
+
let(:scope) { AsyncScope.new }
|
18
|
+
let(:simple_unit) do
|
19
|
+
ExternalTask.new(:parent => scope ) do |t|
|
20
|
+
t.initiate_task { |h| 2 + 2; h.complete }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
it "tells you that ExternalTask is alive before the ExternalTask starts" do
|
24
|
+
simple_unit.alive?.should == true
|
25
|
+
end
|
26
|
+
it "tells you that ExternalTask is not alive once ExternalTask has exited" do
|
27
|
+
task_context = TaskContext.new(:parent => scope.get_closest_containing_scope, :task => simple_unit)
|
28
|
+
simple_unit.resume
|
29
|
+
simple_unit.alive?.should == false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe ExternalTask do
|
34
|
+
let(:trace) { [] }
|
35
|
+
|
36
|
+
before(:each) do
|
37
|
+
|
38
|
+
class ExternalTaskException < Exception; end
|
39
|
+
@this_scope = AsyncScope.new() do
|
40
|
+
end
|
41
|
+
@this_task = ExternalTask.new(:parent => @this_scope) do |t|
|
42
|
+
t.cancellation_handler { |h, cause| h.fail(cause) }
|
43
|
+
t.initiate_task { |h| trace << :task_started; h.complete }
|
44
|
+
end
|
45
|
+
task_context = TaskContext.new(:parent => @this_scope.get_closest_containing_scope, :task => @this_task)
|
46
|
+
@this_scope << @this_task
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should complete okay" do
|
50
|
+
|
51
|
+
@this_scope.eventLoop
|
52
|
+
trace.should == [:task_started]
|
53
|
+
end
|
54
|
+
|
55
|
+
it "cancelling should cause an exception to be raised" do
|
56
|
+
@this_task.cancel(ExternalTaskException.new)
|
57
|
+
expect { @this_scope.eventLoop }.to raise_error ExternalTaskException
|
58
|
+
end
|
59
|
+
|
60
|
+
it "shouldn't cancel an already completed task" do
|
61
|
+
@this_scope.eventLoop
|
62
|
+
@this_task.cancel(Exception)
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "cancelling an external task without a cancellation handler" do
|
66
|
+
before(:each) do
|
67
|
+
@this_scope = AsyncScope.new() do
|
68
|
+
|
69
|
+
end
|
70
|
+
@this_task = ExternalTask.new() do |t|
|
71
|
+
t.initiate_task { |h| trace << :task_started; h.complete }
|
72
|
+
end
|
73
|
+
@this_scope << @this_task
|
74
|
+
end
|
75
|
+
it "ensures that calling cancel results in no error" do
|
76
|
+
task_context = TaskContext.new(:parent => @this_scope.get_closest_containing_scope, :task => @this_task)
|
77
|
+
@this_task.cancel(ExternalTaskException)
|
78
|
+
end
|
79
|
+
it "ensures that attempting to run a cancelled task has no effect" do
|
80
|
+
task_context = TaskContext.new(:parent => @this_scope.get_closest_containing_scope, :task => @this_task)
|
81
|
+
@this_task.cancel(ExternalTaskException)
|
82
|
+
@this_scope.eventLoop
|
83
|
+
trace.should == []
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
it "ensures that raising in the cancellation handler is handled " do
|
88
|
+
scope = AsyncScope.new
|
89
|
+
external_task = ExternalTask.new() do |t|
|
90
|
+
t.initiate_task { |h| h.complete }
|
91
|
+
t.cancellation_handler { |h, cause| raise "Oh no!" }
|
92
|
+
end
|
93
|
+
scope << external_task
|
94
|
+
task_context = TaskContext.new(:parent => scope.get_closest_containing_scope, :task => external_task)
|
95
|
+
external_task.cancel(Exception)
|
96
|
+
expect { scope.eventLoop }.to raise_error /Oh no!/
|
97
|
+
end
|
98
|
+
|
99
|
+
it "ensures that cancelling a completed task is handled" do
|
100
|
+
@this_scope.eventLoop
|
101
|
+
@this_task.cancel(Exception)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "ensures that cancelling a cancelled task is handled" do
|
105
|
+
@this_scope.eventLoop
|
106
|
+
@this_task.cancel(Exception)
|
107
|
+
@this_task.cancel(Exception)
|
108
|
+
end
|
109
|
+
|
110
|
+
it "ensures that failing a task after completion raises an error" do
|
111
|
+
@this_scope = AsyncScope.new() do
|
112
|
+
end
|
113
|
+
@this_task = ExternalTask.new() do |t|
|
114
|
+
t.cancellation_handler { |h| h.fail }
|
115
|
+
t.initiate_task { |h| trace << :task_started; h.complete; h.fail(Exception) }
|
116
|
+
end
|
117
|
+
task_context = TaskContext.new(:parent => @this_scope.get_closest_containing_scope, :task => @this_task)
|
118
|
+
@this_scope << @this_task
|
119
|
+
expect { @this_scope.eventLoop }.to raise_error /Already completed/
|
120
|
+
end
|
121
|
+
|
122
|
+
it "ensures that completing an external_task allows the asyncScope to move forward" do
|
123
|
+
@handle = nil
|
124
|
+
@this_scope = AsyncScope.new() do
|
125
|
+
external_task do |t|
|
126
|
+
t.cancellation_handler {|h| h.fail}
|
127
|
+
t.initiate_task {|h| @handle = h }
|
128
|
+
end
|
129
|
+
end
|
130
|
+
@this_scope.eventLoop
|
131
|
+
@this_scope.is_complete?.should == false
|
132
|
+
@handle.complete
|
133
|
+
@this_scope.eventLoop
|
134
|
+
@this_scope.is_complete?.should == true
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe "external_task function" do
|
139
|
+
|
140
|
+
it "ensures that cancelling will raise the externalTasks cancellation handler" do
|
141
|
+
trace = []
|
142
|
+
this_scope = AsyncScope.new() do
|
143
|
+
trace << :async_start
|
144
|
+
external_task do |t|
|
145
|
+
t.cancellation_handler {|h, cause| trace << :cancellation_handler; h.complete }
|
146
|
+
t.initiate_task { |h| trace << :external_task}
|
147
|
+
end
|
148
|
+
task { trace << :task; raise IllegalStateException }
|
149
|
+
trace << :async_done
|
150
|
+
end
|
151
|
+
expect { this_scope.eventLoop }.to raise_error IllegalStateException
|
152
|
+
trace.should == [:async_start, :async_done, :external_task, :task, :cancellation_handler]
|
153
|
+
end
|
154
|
+
|
155
|
+
it "tests explicit cancellation of BRE cancelling externalTask correctly" do
|
156
|
+
handle_future = Future.new
|
157
|
+
trace = []
|
158
|
+
|
159
|
+
scope = AsyncScope.new do
|
160
|
+
trace << :async_start
|
161
|
+
bre = error_handler do |t|
|
162
|
+
t.begin do
|
163
|
+
external_task do |t|
|
164
|
+
trace << :external_task;
|
165
|
+
t.cancellation_handler {|h, cause| trace << :cancellation_handler; h.complete}
|
166
|
+
t.initiate_task { |h| handle_future.set(h) }
|
167
|
+
end
|
168
|
+
|
169
|
+
task do
|
170
|
+
trace << :in_the_cancel_task
|
171
|
+
handle_future.get
|
172
|
+
bre.cancel(nil)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
t.rescue(Exception) { |e| e.class.should <= CancellationException }
|
176
|
+
end
|
177
|
+
|
178
|
+
trace << :async_done
|
179
|
+
end
|
180
|
+
scope.eventLoop
|
181
|
+
trace.should == [:async_start, :async_done, :external_task, :in_the_cancel_task, :cancellation_handler]
|
182
|
+
end
|
183
|
+
|
184
|
+
it "ensures that having an external_task within an AsyncScope works" do
|
185
|
+
trace = []
|
186
|
+
this_scope = AsyncScope.new() do
|
187
|
+
external_task do |t|
|
188
|
+
t.cancellation_handler { |h| h.fail }
|
189
|
+
t.initiate_task { |h| trace << :task_started; h.complete; }
|
190
|
+
end
|
191
|
+
end
|
192
|
+
trace.should == []
|
193
|
+
this_scope.eventLoop
|
194
|
+
trace.should == [:task_started]
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|
data/test/aws/factories.rb
CHANGED
@@ -13,7 +13,36 @@
|
|
13
13
|
# permissions and limitations under the License.
|
14
14
|
##
|
15
15
|
|
16
|
-
require '
|
16
|
+
require 'aws/flow'
|
17
|
+
include AWS::Flow::Core
|
18
|
+
|
19
|
+
|
20
|
+
class FlowFactory
|
21
|
+
|
22
|
+
attr_accessor :async_scope
|
23
|
+
def generate_BRE(options = {})
|
24
|
+
scope = generate_scope
|
25
|
+
bre = BeginRescueEnsure.new(:parent => scope.root_context)
|
26
|
+
begin_statement = options[:begin] ? options[:begin] : lambda {}
|
27
|
+
rescue_statement = options[:rescue] ? options[:rescue] : lambda {|x| }
|
28
|
+
rescue_exception = options[:rescue_exceptions] ? options[:rescue_exceptions] : StandardError
|
29
|
+
ensure_statement = options[:ensure] ? options[:ensure] : lambda {}
|
30
|
+
bre.begin begin_statement
|
31
|
+
bre.rescue(rescue_exception, rescue_statement)
|
32
|
+
bre.ensure ensure_statement
|
33
|
+
scope << bre
|
34
|
+
bre
|
35
|
+
end
|
36
|
+
|
37
|
+
def generate_scope(options = {})
|
38
|
+
lambda = options[:lambda] || lambda {}
|
39
|
+
@async_scope ||= AsyncScope.new do
|
40
|
+
lambda.call
|
41
|
+
end
|
42
|
+
return @async_scope
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
17
46
|
|
18
47
|
class WorkflowGenerator
|
19
48
|
class << self
|