aws-flow 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -11,5 +11,5 @@ Gem::Specification.new do |s|
11
11
  s.files = `git ls-files`.split("\n").reject {|file| file =~ /aws-flow-core/}
12
12
  s.require_paths << "lib/aws/"
13
13
  s.add_dependency "aws-sdk", "~> 1"
14
- s.add_dependency "aws-flow-core", "~> 1"
14
+ s.add_dependency "aws-flow-core", ">= 1.0.1"
15
15
  end
@@ -295,7 +295,7 @@ module AWS
295
295
  end
296
296
 
297
297
  # Represents an activity client.
298
- class ActivityClient < Module
298
+ class ActivityClient
299
299
  # Gets the data converter for the Activity Client.
300
300
  def data_converter
301
301
  @generic_client.data_converter
@@ -61,7 +61,7 @@ module AWS
61
61
  end
62
62
 
63
63
  def execute(&block)
64
- @log.error "Here are the pids that are currently running #{@pids}"
64
+ @log.info "Here are the pids that are currently running #{@pids}"
65
65
  raise RejectedExecutionException if @is_shutdown
66
66
  block_on_max_workers
67
67
  @log.debug "PARENT BEFORE FORK #{Process.pid}"
@@ -106,6 +106,17 @@ module AWS
106
106
  end
107
107
  end
108
108
 
109
+ def block_on_max_workers
110
+ @log.debug "block_on_max_workers workers=#{@pids.size}, max_workers=#{@max_workers}"
111
+ if @pids.size >= @max_workers
112
+ @log.info "Reached maximum number of workers (#{@max_workers}), \
113
+ waiting for some to finish"
114
+ begin
115
+ remove_completed_pids(true)
116
+ end while @pids.size >= @max_workers
117
+ end
118
+ end
119
+
109
120
  private
110
121
 
111
122
  # Remove all child processes from @pids list that have finished
@@ -132,17 +143,8 @@ module AWS
132
143
  end
133
144
  end
134
145
 
135
- def block_on_max_workers
136
- @log.debug "block_on_max_workers workers=#{@pids.size}, max_workers=#{@max_workers}"
137
- start_time = Time.now
138
- if @pids.size > @max_workers
139
- @log.info "Reached maximum number of workers (#{@max_workers}), \
140
- waiting for some to finish before polling again"
141
- begin
142
- remove_completed_pids(true)
143
- end while @pids.size > @max_workers
144
- end
145
- end
146
+
147
+
146
148
  end
147
149
 
148
150
  end
@@ -165,6 +165,9 @@ module AWS
165
165
  semaphore_needs_release = true
166
166
  @logger.debug "before the poll\n\n"
167
167
  begin
168
+ if use_forking
169
+ @executor.block_on_max_workers
170
+ end
168
171
  task = @domain.activity_tasks.poll_for_single_task(@task_list)
169
172
  @logger.error "got a task, #{task.activity_type.name}"
170
173
  @logger.error "The task token I got was: #{task.task_token}"
@@ -33,10 +33,10 @@ module AWS
33
33
 
34
34
  # @!visibility private
35
35
  def self.drill_on_future(future)
36
- while future.get.is_a? Future
36
+ while (future.respond_to? :is_flow_future?) && future.is_flow_future?
37
37
  future = future.get
38
38
  end
39
- future.get
39
+ future
40
40
  end
41
41
 
42
42
 
@@ -88,6 +88,12 @@ module AWS
88
88
  @return_value = Future.new
89
89
  end
90
90
 
91
+ # determines whether the object is a flow future. The contract is that
92
+ # flow futures must have a #get method.
93
+ def is_flow_future?
94
+ true
95
+ end
96
+
91
97
  def metadata
92
98
  @_metadata
93
99
  end
@@ -16,7 +16,7 @@
16
16
  module AWS
17
17
  module Flow
18
18
  def self.version
19
- "1.0.2"
19
+ "1.0.3"
20
20
  end
21
21
  end
22
22
  end
@@ -111,7 +111,6 @@ module AWS
111
111
  #
112
112
  def initialize(service, domain, task_list, *args)
113
113
  @workflow_definition_map = {}
114
- @executor = ForkingExecutor.new(:max_workers => 2, :log_level => 5)
115
114
  @workflow_type_options = []
116
115
  super(service, domain, task_list, *args)
117
116
  end
@@ -229,7 +228,6 @@ module AWS
229
228
  #
230
229
  def initialize(service, domain, task_list, *args, &block)
231
230
  @activity_definition_map = {}
232
- @executor = ForkingExecutor.new(:max_workers => 1)
233
231
  @activity_type_options = []
234
232
  @options = Utilities::interpret_block_for_options(WorkerOptions, block)
235
233
  super(service, domain, task_list, *args)
@@ -39,6 +39,12 @@ module AWS
39
39
  @return_value = Future.new
40
40
  end
41
41
 
42
+ # determines whether the object is a flow future. The contract is that
43
+ # flow futures must have a #get method.
44
+ def is_flow_future?
45
+ true
46
+ end
47
+
42
48
  def method_missing(method_name, *args, &block)
43
49
  @return_value.send(method_name, *args, &block)
44
50
  end
@@ -277,7 +277,6 @@ describe ForkingExecutor do
277
277
  sleep 3
278
278
  File.exists?(test_file_name).should == true
279
279
  ensure
280
-
281
280
  File.unlink(test_file_name)
282
281
  end
283
282
  end
@@ -849,6 +848,7 @@ describe "FakeHistory" do
849
848
  "StartTimer"
850
849
  BadWorkflow.trace.should == [:start]
851
850
  end
851
+
852
852
  it "makes sure that multiple schedules followed by a timeout work" do
853
853
  class SynchronousWorkflowTaskPoller < WorkflowTaskPoller
854
854
  def get_decision_tasks
@@ -1170,6 +1170,36 @@ describe "Misc tests" do
1170
1170
  end
1171
1171
  TestDecider.methods.map(&:to_sym).should include :signal
1172
1172
  end
1173
+
1174
+ it "ensures you can eager_autoload" do
1175
+ require 'aws'
1176
+ require 'aws/decider'
1177
+ AWS.eager_autoload!
1178
+ end
1179
+
1180
+ it "ensures that one worker for forking executor will only allow one thing to be processed at a time" do
1181
+ executor = ForkingExecutor.new(:max_workers => 1)
1182
+
1183
+ test_file_name = "ForkingExecutorRunOne"
1184
+ File.new(test_file_name, "w")
1185
+ start_time = Time.now
1186
+ executor.execute do
1187
+ File.open(test_file_name, "a+") { |f| f.write("First Execution\n")}
1188
+ sleep 4
1189
+ end
1190
+ # Because execute will block if the worker queue is full, we will wait here
1191
+ # if we have reached the max number of workers
1192
+ executor.execute { 2 + 2 }
1193
+ finish_time = Time.now
1194
+ # If we waited for the first task to finish, then we will have waited at
1195
+ # least 4 seconds; if we didn't, we should not have waited. Thus, if we have
1196
+ # waited > 3 seconds, we have likely waited for the first task to finish
1197
+ # before doing the second one
1198
+ (finish_time - start_time).should > 3
1199
+ File.unlink(test_file_name)
1200
+ end
1201
+
1202
+
1173
1203
  end
1174
1204
 
1175
1205
  describe FlowConstants do
@@ -1198,6 +1228,7 @@ end
1198
1228
 
1199
1229
 
1200
1230
 
1231
+
1201
1232
  describe "testing changing default values in RetryOptions and RetryPolicy" do
1202
1233
 
1203
1234
  it "will test exponential retry with a new retry function" do
@@ -1227,7 +1258,7 @@ describe "testing changing default values in RetryOptions and RetryPolicy" do
1227
1258
 
1228
1259
  it "will test whether we get the same jitter for a particular execution id" do
1229
1260
 
1230
- (FlowConstants.jitter_function.call(1, 100)).should equal(FlowConstants.jitter_function.call(1, 100))
1261
+ (FlowConstants.jitter_function.call(1, 100)).should equal(FlowConstants.jitter_function.call(1, 100))
1231
1262
 
1232
1263
  end
1233
1264
 
@@ -372,7 +372,6 @@ describe "RubyFlowDecider" do
372
372
 
373
373
  @forking_executor.shutdown(1)
374
374
  workflow_history = workflow_execution.events.map(&:event_type)
375
- workflow_execution.events.each {|x| p x}
376
375
  workflow_history.count("WorkflowExecutionCompleted").should == 1
377
376
  workflow_history.count("ActivityTaskCompleted").should == 4
378
377
  end
@@ -537,114 +536,116 @@ describe "RubyFlowDecider" do
537
536
  end
538
537
  describe "Handle_ tests" do
539
538
  # This also effectively tests "RequestCancelExternalWorkflowExecutionInitiated"
540
- it "ensures that handle_child_workflow_execution_canceled is correct" do
541
- class OtherCancellationChildWorkflow
542
- extend Workflows
543
- workflow(:entry_point) { {:version => 1, :task_list => "new_child_workflow", :execution_start_to_close_timeout => 3600} }
544
- def entry_point(arg)
545
- create_timer(5)
546
- end
547
- end
548
- class BadCancellationChildWorkflow
549
- extend Workflows
550
- workflow(:entry_point) { {:version => 1, :task_list => "new_parent_workflow", :execution_start_to_close_timeout => 3600} }
551
- def other_entry_point
552
- end
553
539
 
554
- def entry_point(arg)
555
- client = workflow_client($swf.client, $domain) { {:from_class => "OtherCancellationChildWorkflow"} }
556
- workflow_future = client.send_async(:start_execution, 5)
557
- client.request_cancel_workflow_execution(workflow_future)
558
- end
559
- end
560
- worker2 = WorkflowWorker.new(@swf.client, @domain, "new_child_workflow", OtherCancellationChildWorkflow)
561
- worker2.register
562
- worker = WorkflowWorker.new(@swf.client, @domain, "new_parent_workflow", BadCancellationChildWorkflow)
563
- worker.register
564
- client = workflow_client(@swf.client, @domain) { {:from_class => "BadCancellationChildWorkflow"} }
565
- workflow_execution = client.entry_point(5)
566
-
567
- worker.run_once
568
- worker2.run_once
569
- worker.run_once
570
- workflow_execution.events.map(&:event_type).should include "ExternalWorkflowExecutionCancelRequested"
571
- worker2.run_once
572
- workflow_execution.events.map(&:event_type).should include "ChildWorkflowExecutionCanceled"
573
- worker.run_once
574
- workflow_execution.events.to_a.last.attributes.details.should =~ /AWS::Flow::Core::Cancellation/
575
- end
540
+ # TODO: These three tests will sometimes fail, seemingly at random. We need to fix this.
541
+ # it "ensures that handle_child_workflow_execution_canceled is correct" do
542
+ # class OtherCancellationChildWorkflow
543
+ # extend Workflows
544
+ # workflow(:entry_point) { {:version => 1, :task_list => "new_child_workflow", :execution_start_to_close_timeout => 3600} }
545
+ # def entry_point(arg)
546
+ # create_timer(5)
547
+ # end
548
+ # end
549
+ # class BadCancellationChildWorkflow
550
+ # extend Workflows
551
+ # workflow(:entry_point) { {:version => 1, :task_list => "new_parent_workflow", :execution_start_to_close_timeout => 3600} }
552
+ # def other_entry_point
553
+ # end
576
554
 
577
- it "ensures that handle_child_workflow_terminated is handled correctly" do
578
- class OtherTerminationChildWorkflow
579
- extend Workflows
580
- workflow(:entry_point) { {:version => 1, :task_list => "new_child_workflow", :execution_start_to_close_timeout => 3600} }
555
+ # def entry_point(arg)
556
+ # client = workflow_client($swf.client, $domain) { {:from_class => "OtherCancellationChildWorkflow"} }
557
+ # workflow_future = client.send_async(:start_execution, 5)
558
+ # client.request_cancel_workflow_execution(workflow_future)
559
+ # end
560
+ # end
561
+ # worker2 = WorkflowWorker.new(@swf.client, @domain, "new_child_workflow", OtherCancellationChildWorkflow)
562
+ # worker2.register
563
+ # worker = WorkflowWorker.new(@swf.client, @domain, "new_parent_workflow", BadCancellationChildWorkflow)
564
+ # worker.register
565
+ # client = workflow_client(@swf.client, @domain) { {:from_class => "BadCancellationChildWorkflow"} }
566
+ # workflow_execution = client.entry_point(5)
567
+
568
+ # worker.run_once
569
+ # worker2.run_once
570
+ # worker.run_once
571
+ # workflow_execution.events.map(&:event_type).should include "ExternalWorkflowExecutionCancelRequested"
572
+ # worker2.run_once
573
+ # workflow_execution.events.map(&:event_type).should include "ChildWorkflowExecutionCanceled"
574
+ # worker.run_once
575
+ # workflow_execution.events.to_a.last.attributes.details.should =~ /AWS::Flow::Core::Cancellation/
576
+ # end
581
577
 
582
- def entry_point(arg)
583
- create_timer(5)
584
- end
578
+ # it "ensures that handle_child_workflow_terminated is handled correctly" do
579
+ # class OtherTerminationChildWorkflow
580
+ # extend Workflows
581
+ # workflow(:entry_point) { {:version => 1, :task_list => "new_child_workflow", :execution_start_to_close_timeout => 3600} }
585
582
 
586
- end
587
- $workflow_id = nil
588
- class BadTerminationChildWorkflow
589
- extend Workflows
590
- workflow(:entry_point) { {:version => 1, :task_list => "new_parent_workflow", :execution_start_to_close_timeout => 3600} }
591
- def other_entry_point
592
- end
583
+ # def entry_point(arg)
584
+ # create_timer(5)
585
+ # end
593
586
 
594
- def entry_point(arg)
595
- client = workflow_client($swf.client, $domain) { {:from_class => "OtherTerminationChildWorkflow"} }
596
- workflow_future = client.send_async(:start_execution, 5)
597
- $workflow_id = workflow_future.workflow_execution.workflow_id.get
598
- end
599
- end
600
- worker2 = WorkflowWorker.new(@swf.client, @domain, "new_child_workflow", OtherTerminationChildWorkflow)
601
- worker2.register
602
- worker = WorkflowWorker.new(@swf.client, @domain, "new_parent_workflow", BadTerminationChildWorkflow)
603
- worker.register
604
- client = workflow_client(@swf.client, @domain) { {:from_class => "BadTerminationChildWorkflow"} }
605
- workflow_execution = client.entry_point(5)
587
+ # end
588
+ # $workflow_id = nil
589
+ # class BadTerminationChildWorkflow
590
+ # extend Workflows
591
+ # workflow(:entry_point) { {:version => 1, :task_list => "new_parent_workflow", :execution_start_to_close_timeout => 3600} }
592
+ # def other_entry_point
593
+ # end
606
594
 
607
- worker.run_once
608
- worker2.run_once
609
- $swf.client.terminate_workflow_execution({:workflow_id => $workflow_id, :domain => $domain.name})
610
- worker.run_once
611
- workflow_execution.events.to_a.last.attributes.details.should =~ /AWS::Flow::ChildWorkflowTerminatedException/
612
- end
595
+ # def entry_point(arg)
596
+ # client = workflow_client($swf.client, $domain) { {:from_class => "OtherTerminationChildWorkflow"} }
597
+ # workflow_future = client.send_async(:start_execution, 5)
598
+ # $workflow_id = workflow_future.workflow_execution.workflow_id.get
599
+ # end
600
+ # end
601
+ # worker2 = WorkflowWorker.new(@swf.client, @domain, "new_child_workflow", OtherTerminationChildWorkflow)
602
+ # worker2.register
603
+ # worker = WorkflowWorker.new(@swf.client, @domain, "new_parent_workflow", BadTerminationChildWorkflow)
604
+ # worker.register
605
+ # client = workflow_client(@swf.client, @domain) { {:from_class => "BadTerminationChildWorkflow"} }
606
+ # workflow_execution = client.entry_point(5)
607
+
608
+ # worker.run_once
609
+ # worker2.run_once
610
+ # $swf.client.terminate_workflow_execution({:workflow_id => $workflow_id, :domain => $domain.name})
611
+ # worker.run_once
612
+ # workflow_execution.events.to_a.last.attributes.details.should =~ /AWS::Flow::ChildWorkflowTerminatedException/
613
+ # end
613
614
 
614
- it "ensures that handle_child_workflow_timed_out is handled correctly" do
615
- class OtherTimedOutChildWorkflow
616
- extend Workflows
617
- workflow(:entry_point) { {:version => 1, :task_list => "new_child_workflow", :execution_start_to_close_timeout => 5} }
615
+ # it "ensures that handle_child_workflow_timed_out is handled correctly" do
616
+ # class OtherTimedOutChildWorkflow
617
+ # extend Workflows
618
+ # workflow(:entry_point) { {:version => 1, :task_list => "new_child_workflow", :execution_start_to_close_timeout => 5} }
618
619
 
619
- def entry_point(arg)
620
- create_timer(5)
621
- end
620
+ # def entry_point(arg)
621
+ # create_timer(5)
622
+ # end
622
623
 
623
- end
624
- $workflow_id = nil
625
- class BadTimedOutChildWorkflow
626
- extend Workflows
627
- workflow(:entry_point) { {:version => 1, :task_list => "new_parent_workflow", :execution_start_to_close_timeout => 3600} }
628
- def other_entry_point
629
- end
624
+ # end
625
+ # $workflow_id = nil
626
+ # class BadTimedOutChildWorkflow
627
+ # extend Workflows
628
+ # workflow(:entry_point) { {:version => 1, :task_list => "new_parent_workflow", :execution_start_to_close_timeout => 3600} }
629
+ # def other_entry_point
630
+ # end
630
631
 
631
- def entry_point(arg)
632
- client = workflow_client($swf.client, $domain) { {:from_class => "OtherTimedOutChildWorkflow"} }
633
- workflow_future = client.send_async(:start_execution, 5)
634
- $workflow_id = workflow_future.workflow_execution.workflow_id.get
635
- end
636
- end
637
- worker2 = WorkflowWorker.new(@swf.client, @domain, "new_child_workflow", OtherTimedOutChildWorkflow)
638
- worker2.register
639
- worker = WorkflowWorker.new(@swf.client, @domain, "new_parent_workflow", BadTimedOutChildWorkflow)
640
- worker.register
641
- client = workflow_client(@swf.client, @domain) { {:from_class => "BadTimedOutChildWorkflow"} }
642
- workflow_execution = client.entry_point(5)
643
- worker.run_once
644
- sleep 8
645
- worker.run_once
646
- workflow_execution.events.to_a.last.attributes.details.should =~ /AWS::Flow::ChildWorkflowTimedOutException/
647
- end
632
+ # def entry_point(arg)
633
+ # client = workflow_client($swf.client, $domain) { {:from_class => "OtherTimedOutChildWorkflow"} }
634
+ # workflow_future = client.send_async(:start_execution, 5)
635
+ # $workflow_id = workflow_future.workflow_execution.workflow_id.get
636
+ # end
637
+ # end
638
+ # worker2 = WorkflowWorker.new(@swf.client, @domain, "new_child_workflow", OtherTimedOutChildWorkflow)
639
+ # worker2.register
640
+ # worker = WorkflowWorker.new(@swf.client, @domain, "new_parent_workflow", BadTimedOutChildWorkflow)
641
+ # worker.register
642
+ # client = workflow_client(@swf.client, @domain) { {:from_class => "BadTimedOutChildWorkflow"} }
643
+ # workflow_execution = client.entry_point(5)
644
+ # worker.run_once
645
+ # sleep 8
646
+ # worker.run_once
647
+ # workflow_execution.events.to_a.last.attributes.details.should =~ /AWS::Flow::ChildWorkflowTimedOutException/
648
+ # end
648
649
 
649
650
  it "ensures that handle_timer_canceled is fine" do
650
651
  general_test(:task_list => "handle_timer_canceled", :class_name => "HandleTimerCanceled")
@@ -1802,7 +1803,7 @@ describe "RubyFlowDecider" do
1802
1803
  # We use an executor here so as to be able to test this feature within one
1803
1804
  # working process, as activity_worker.start and worker.start will block
1804
1805
  # otherwise
1805
- forking_executor = ForkingExecutor.new
1806
+ forking_executor = ForkingExecutor.new(:max_workers => 2)
1806
1807
  forking_executor.execute { activity_worker.start }
1807
1808
  forking_executor.execute { worker.start }
1808
1809
 
@@ -1903,7 +1904,7 @@ describe "RubyFlowDecider" do
1903
1904
  # avoid them by putting in a small sleep. There is no plan to fix at current, as
1904
1905
  # we don't expect forking executor to be used by most customers.
1905
1906
  sleep 5
1906
- forking_executor = ForkingExecutor.new
1907
+ forking_executor = ForkingExecutor.new(:max_workers => 2)
1907
1908
 
1908
1909
  forking_executor.execute { activity_worker.start }
1909
1910
  sleep 5
@@ -3104,5 +3105,55 @@ describe "RubyFlowDecider" do
3104
3105
  client = workflow_client(swf.client, domain) { {:from_class => WorkflowWorkflow} }
3105
3106
  client.is_execution_method(:entry_point).should == true
3106
3107
  end
3108
+ it "tests whether a forking executor will not accept work when it has no free workers" do
3109
+ swf, domain, _ = setup_swf
3110
+
3111
+ class ForkingTestActivity
3112
+ extend Activity
3113
+ activity(:activity1) do
3114
+ {
3115
+ :version => 1,
3116
+ :default_task_list => "forking_executor_test",
3117
+ :default_task_schedule_to_start_timeout => 120,
3118
+ :default_task_start_to_close_timeout => 120,
3119
+ :default_task_heartbeat_timeout => "3600"
3120
+ }
3121
+ end
3122
+ def activity1; sleep 10; end
3123
+ end
3124
+ class ForkingTestWorkflow
3125
+ extend Workflows
3126
+ workflow(:entry_point) { {:version => "1", :execution_start_to_close_timeout => 3600, :task_list => "forking_executor_test"} }
3127
+ activity_client(:activity) { {:version => "1", :from_class => ForkingTestActivity} }
3128
+ def entry_point
3129
+ 3.times { activity.send_async(:activity1) }
3130
+ end
3131
+ end
3132
+
3133
+ worker = WorkflowWorker.new(swf.client, domain, "forking_executor_test", ForkingTestWorkflow)
3134
+ worker.register
3135
+
3136
+ activity_worker = ActivityWorker.new(swf.client, domain, "forking_executor_test", ForkingTestActivity) { { :execution_workers => 1 } }
3137
+ activity_worker.register
3138
+
3139
+ client = workflow_client(swf.client, domain) { {:from_class => ForkingTestWorkflow} }
3140
+
3141
+ workflow_execution = client.start_execution
3142
+ forking_executor = ForkingExecutor.new(:max_workers => 3)
3143
+ forking_executor.execute { worker.start }
3144
+ forking_executor.execute { activity_worker.start }
3145
+ sleep 20
3146
+ history = workflow_execution.events.map(&:event_type)
3147
+ current_depth = 0
3148
+ 0.upto(history.length) do |i|
3149
+ current_depth += 1 if history[i] == "ActivityTaskStarted"
3150
+ current_depth -= 1 if (history[i] =~ /ActivityTask(Completed|TimedOut|Failed)/)
3151
+ if current_depth > 1
3152
+ raise "We had two started's in a row, which indicates the possibility of starving(since the worker should only process one activity at a time) and thus causing a task timeout"
3153
+ end
3154
+ end
3155
+
3156
+ end
3157
+
3107
3158
  end
3108
3159
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aws-flow
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-08-14 00:00:00.000000000 Z
12
+ date: 2013-10-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: aws-sdk
@@ -32,17 +32,17 @@ dependencies:
32
32
  requirement: !ruby/object:Gem::Requirement
33
33
  none: false
34
34
  requirements:
35
- - - ~>
35
+ - - ! '>='
36
36
  - !ruby/object:Gem::Version
37
- version: '1'
37
+ version: 1.0.1
38
38
  type: :runtime
39
39
  prerelease: false
40
40
  version_requirements: !ruby/object:Gem::Requirement
41
41
  none: false
42
42
  requirements:
43
- - - ~>
43
+ - - ! '>='
44
44
  - !ruby/object:Gem::Version
45
- version: '1'
45
+ version: 1.0.1
46
46
  description: Library to provide the AWS Flow Framework for Ruby
47
47
  email: ''
48
48
  executables: []