simpler_workflow 0.1.0

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/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in simpler_workflow.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Snugg Home LLC
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,180 @@
1
+ # SimplerWorkflow
2
+
3
+ A wrapper around Amazon's Simple Workflow Service meant to simplify declaring and using activities and workflow. Provides some sane defaults
4
+ and work around some idiosyncracies of the platform.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'simpler_workflow'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install simpler_workflow
19
+
20
+ ## Usage
21
+
22
+ ### Configuring AWS Gem
23
+
24
+ We are using the aws-sdk gem to communicate with Amazon's service. You can configure the service by putting an ```aws.yml``` file
25
+ in your config directory. The file should contain the following information:
26
+
27
+ ```yaml
28
+ development:
29
+ access_key_id: <Amazon Acess Key ID>
30
+ secret_access_key: <Amazon's secret access key>
31
+ ```
32
+
33
+ This will authenticate your application or script against AWS and give you access to your SWF domain, workflows and activity.
34
+
35
+ ### Access a domain
36
+
37
+ You will need to get a handle on a SWF domain before you can do anything else. Domains are accessed using the ```SimplerWorkflow::Domain```
38
+ class. This declares a domain that does not retain workflow execution data:
39
+
40
+ ```ruby
41
+ domain = SimplerWorkflow::Domain["my-domain"]
42
+ ```
43
+ An other option is to use the ```domains``` method to get a handle on a domain and pass a block. This allows you to write the following code:
44
+
45
+ ```ruby
46
+ domain = SimplerWorkflow::Domain.domains("my_domain") do
47
+ # Register activities
48
+ register_activity(:an_activity, '1.0.0') do
49
+ # See details below...
50
+ end
51
+ # Register workflow(s)
52
+ register_workflow(:a_workflow, '1.1.0') do
53
+ # See details below...
54
+ end
55
+ end
56
+ ```
57
+
58
+ You can also get a handle on a domain that retains information about workflow execution for 10 days with the following code:
59
+
60
+ ```ruby
61
+ domain = SimplerWorkflow::Domain.new("my-domain", 10)
62
+ ```
63
+
64
+ Domains are scoped by AWS accounts. The name of the domain must be unique within the account. You do not need to create the domain on AWS
65
+ since it is created the first time it is accessed.
66
+
67
+ ### Creating an activity
68
+
69
+ Activities perform the work attached to the workflow and report back to SWF when the activity completes or it fails.
70
+
71
+ ```SimplerWorkflow``` makes it easier to register an activity with your domain.
72
+
73
+ Activities must provide the following:
74
+ * A name
75
+ * A version
76
+ * Some code to run when it is invoked
77
+
78
+ You can also optionaly declare when what to do when the activity fails or succeeds.
79
+
80
+ ```ruby
81
+ my_activity = domain.register_activity :my_activity, "1.0.0" do
82
+ perform_activity do |task|
83
+ input = task.input
84
+ puts task
85
+ end
86
+ end
87
+
88
+ my_activity.start_activity_loop
89
+ ```
90
+
91
+ The activity manages a loop that waits for messages from SWF.
92
+
93
+ Activities are passed a task parameter. This parameter is provided to the activity by SWF and provides a lot of information about the task
94
+ at end. One item passed is the input attribute.
95
+
96
+ The block attached to the perform_activity method is called when the activity is invoked. This block contains the actions that an activity
97
+ will perform. The ```SimplerWorkflow::Activity``` class will automatically report that the activity completed successfully when the
98
+ block returns unless a response has been provided in the block. It will automatically report that an activity failed when an unhandled
99
+ exception is thrown within the block.
100
+
101
+ The activity can influence what happens when the activity succeeds or fail. You can specify the activity's failure response through the
102
+ ```SimplerWorkflow::Activity#on_fail``` method. By default, the activity will ask the workflow to abort itself on failure. You can also
103
+ ask the workflow to repeat the activity by passing ```:retry``` to the method:
104
+
105
+ ```ruby
106
+ my_activity = domain.register_activity :my_activity, "1.0.0" do
107
+ on_fail :retry
108
+
109
+ perform_activity do |task|
110
+ # ...
111
+ end
112
+ end
113
+ ```
114
+
115
+ The activity can also tell a workflow what activity to trigger next on the workflow. This only works when using the default decision
116
+ loop (described later). This is done by declaring what is the next activity should be:
117
+
118
+ ```ruby
119
+ my_activity = domain.register_activity :my_activity, "1.0.0" do
120
+ on_success :my_next_activity, "1.0.0"
121
+
122
+ perform_activity do |task|
123
+ # ...
124
+ end
125
+ end
126
+ ```
127
+
128
+ ### Workflow and Decision Loops
129
+
130
+ The next key concept in ```SimplerWorkflow``` is the workflow. The workflow decides what activities to invoke, what to do when
131
+ they complete and what to do when they fail. The ```SimplerWorkflow::Workflow``` object manages the decision loop.
132
+
133
+ By default, the workflow is setup to allow for a linear set of activities until the list runs out. This is convenient for simple
134
+ workflows. There are also hooks to override what happens with each decision point to accomodate more complex workflows.
135
+
136
+ Workflows are declared and registered through the ```SimplerWorkflow::Domain#register_workflow``` method. This will register a
137
+ workflow and configure it to start a linear workflow with version 1.0.0 of the :my_activity activity:
138
+
139
+ ```ruby
140
+ my_workflow = domain.register_workflow :my_workflow, '1.0.0' do
141
+ initial_activity :my_activity, '1.0.0'
142
+ end
143
+ ```
144
+
145
+ The next step is to start the decision loop:
146
+
147
+ ```ruby
148
+ my_workflow.decision_loop
149
+ ```
150
+
151
+ #### Customizing the workflow
152
+
153
+ There are hooks for different section of the decision loop. You can specify what happens when the workflow is started with the ```on_start_execution```
154
+ method:
155
+
156
+ ```ruby
157
+ my_workflow = domain.register_workflow :my_workflow, '1.0.0' do
158
+ on_start_execution do |task, event|
159
+ puts "Mary had a little lamb"
160
+ task.schedule_activity_task my_activity.to_activity_type, :input => { :my_param => 'value'}
161
+ end
162
+ end
163
+ ```
164
+
165
+ The task and event parameters are received from SWF. Unfortunately, you must still work within the constraints of the AWS SWF SDK. The ```SimplerWorkflow::Activity#to_activity_type``` generates the proper incantation used by SWF to identify and locate an activity.
166
+
167
+ You can also define similar hooks for events using the following methods:
168
+
169
+ * ```on_activity_completed``` is called when an activity completes and SWF reports back to the decision loop.
170
+ * ```on_activity_failed``` is called when an activity reports a failure to SWF.
171
+
172
+ ## Contributing
173
+
174
+ We welcome all kinds of contributions. This include code, fixes, issues, documentation, tests... Here's how you can contribute:
175
+
176
+ 1. Fork it
177
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
178
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
179
+ 4. Push to the branch (`git push origin my-new-feature`)
180
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,23 @@
1
+ require 'aws-sdk'
2
+
3
+ module SimplerWorkflow
4
+ def SimplerWorkflow.domain(domain_name)
5
+ @domains ||= {}
6
+ @domains[domain_name.to_sym] ||= Domain.new(domain_name)
7
+ end
8
+
9
+ def SimplerWorkflow.swf
10
+ @swf ||= ::AWS::SimpleWorkflow.new
11
+ end
12
+
13
+ autoload :Version, 'simpler_workflow/version'
14
+ autoload :Domain, 'simpler_workflow/domain'
15
+ autoload :Workflow, 'simpler_workflow/workflow'
16
+ autoload :Activity, 'simpler_workflow/activity'
17
+ end
18
+
19
+ class Map
20
+ def Map.from_json(json)
21
+ from_hash(JSON.parse(json))
22
+ end
23
+ end
@@ -0,0 +1,125 @@
1
+ module SimplerWorkflow
2
+ class Activity
3
+ attr_reader :domain, :name, :version, :options, :next_activity
4
+
5
+ def initialize(domain, name, version, options = {})
6
+ Activity.activities[[name, version]] ||= begin
7
+ default_options = {
8
+ :default_task_list => name,
9
+ :default_task_start_to_close_timeout => 1 * 60 * 60,
10
+ :default_task_schedule_to_start_timeout => 20,
11
+ :default_task_schedule_to_close_timeout => 1 * 60 * 60,
12
+ :default_task_heartbeat_timeout => :none
13
+ }
14
+ @options = default_options.merge(options)
15
+ @domain = domain
16
+ @name = name
17
+ @version = version
18
+ @failure_policy = :abort
19
+ self
20
+ end
21
+ end
22
+
23
+ def method_missing(meth_name, *args)
24
+ if @options.has_key?(meth_name.to_sym)
25
+ @options[meth_name.to_sym] = args[0]
26
+ else
27
+ super
28
+ end
29
+ end
30
+
31
+ def on_success(activity, version = nil)
32
+ case activity
33
+ when Hash
34
+ activity.symbolize_keys!
35
+ name = activity[:name].to_sym
36
+ version = activity[:version]
37
+ when String
38
+ name = activity.to_sym
39
+ when Symbol
40
+ name = activity
41
+ end
42
+ @next_activity = { :name => name, :version => version }
43
+ end
44
+
45
+ def on_fail(failure_policy)
46
+ @failure_policy = failure_policy.to_sym
47
+ end
48
+
49
+ def failure_policy
50
+ @failure_policy || :abort
51
+ end
52
+
53
+ def perform_activity(&block)
54
+ @perform_task = block
55
+ end
56
+
57
+ def perform_task(task)
58
+ logger.info("Performing task #{name}")
59
+ @perform_task.call(task)
60
+ rescue => e
61
+ logger.error e.message
62
+ logger.error e.backtrace.join("\n")
63
+ task.fail! :reason => e.message, :details => {:failure_policy => failure_policy}.to_json
64
+ end
65
+
66
+ def to_activity_type
67
+ domain.activity_types[name.to_s, version]
68
+ end
69
+
70
+ def start_activity_loop
71
+ Thread.new do
72
+ loop do
73
+ begin
74
+ logger.info("Starting activity_loop for #{name}")
75
+ domain.activity_tasks.poll(name.to_s) do |task|
76
+ logger.info("Received task...")
77
+ perform_task(task)
78
+ unless task.responded?
79
+ if next_activity
80
+ result = {:next_activity => next_activity}.to_json
81
+ task.complete!(:result => result)
82
+ else
83
+ task.complete!
84
+ end
85
+ end
86
+ end
87
+ rescue Timeout::Error => e
88
+ rescue => e
89
+ logger.error(e.message)
90
+ logger.error(e.backtrace.join("\n"))
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ def self.[](name, version = nil)
97
+ case name
98
+ when String
99
+ name = name.to_sym
100
+ when Hash
101
+ name.symbolize_keys!
102
+ version = name[:version]
103
+ name = name[:name]
104
+ end
105
+ activities[[name, version]]
106
+ end
107
+
108
+ def self.register(name, version, activity)
109
+ activities[[name, version]] = activity
110
+ end
111
+
112
+ protected
113
+ def self.activities
114
+ @activities ||= {}
115
+ end
116
+
117
+ def self.swf
118
+ SimplerWorkflow.swf
119
+ end
120
+
121
+ def logger
122
+ $logger || Rails.logger
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,97 @@
1
+ module SimplerWorkflow
2
+ class Domain
3
+ def initialize(domain_name, retention = 2, &block)
4
+ domain_name = domain_name.to_s
5
+ @domain = swf.domains[domain_name]
6
+ unless swf.domains.include?(@domain)
7
+ @domain = swf.domains.create(domain_name, retention)
8
+ end
9
+
10
+ self.instance_eval(&block) if block
11
+
12
+ self
13
+ end
14
+
15
+ def Domain.domains(domain_name, &block)
16
+ @domains ||= {}
17
+ @domains[domain_name] ||= Domain.new(domain_name)
18
+ @domains[domain_name].instance_eval(&block) if block
19
+ @domains[domain_name]
20
+ end
21
+
22
+ def Domain.[](domain_name)
23
+ Domain.domains(domain_name)
24
+ end
25
+
26
+ def register_workflow(name, version, &block)
27
+ unless workflow = Workflow[name, version]
28
+ workflow = Workflow.new(self, name, version)
29
+ end
30
+
31
+ workflow.instance_eval(&block) if block
32
+
33
+ begin
34
+ self.domain.workflow_types.register(name.to_s, version, workflow.options)
35
+ rescue ::AWS::SimpleWorkflow::Errors::TypeAlreadyExistsFault => e
36
+ # Instance already registered...
37
+ end
38
+ workflow
39
+ end
40
+
41
+ def workflows
42
+ Workflow
43
+ end
44
+
45
+ def start_workflow(name, version, input)
46
+ logger.info("Starting workflow[#{name},#{version}]")
47
+ workflow = Workflow[name, version] || Workflow.new(self, name, version)
48
+ workflow.start_workflow(input)
49
+ end
50
+
51
+ def activities
52
+ Activity
53
+ end
54
+
55
+ def activity_types
56
+ domain.activity_types
57
+ end
58
+
59
+ def register_activity(name, version, &block)
60
+ unless activity = Activity[name, version]
61
+ logger.info("Registering Activity[#{name},#{version}]")
62
+ activity = Activity.new(self, name, version)
63
+ end
64
+
65
+ activity.instance_eval(&block) if block
66
+
67
+ begin
68
+ self.domain.activity_types.register(name.to_s, version, activity.options)
69
+ rescue ::AWS::SimpleWorkflow::Errors::TypeAlreadyExistsFault
70
+ # Nothing to do, should probably log something here...
71
+ end
72
+
73
+ activity
74
+ end
75
+
76
+ def method_missing(meth_name, *args)
77
+ if domain.respond_to?(meth_name.to_sym)
78
+ domain.send(meth_name.to_sym, *args)
79
+ else
80
+ super
81
+ end
82
+ end
83
+
84
+ protected
85
+ def swf
86
+ SimplerWorkflow.swf
87
+ end
88
+
89
+ def domain
90
+ @domain
91
+ end
92
+
93
+ def logger
94
+ $logger || Rails.logger
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,3 @@
1
+ module SimplerWorkflow
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,150 @@
1
+ module SimplerWorkflow
2
+ class Workflow
3
+ attr_reader :task_list, :domain, :name, :version, :options, :initial_activity_type
4
+
5
+ def initialize(domain, name, version, options = {})
6
+ Workflow.workflows[[name, version]] ||= begin
7
+ default_options = {
8
+ :default_task_list => name,
9
+ :default_task_start_to_close_timeout => 1 * 24 * 60 * 60,
10
+ :default_execution_start_to_close_timeout => 1 * 24 * 60 * 60,
11
+ :default_child_policy => :terminate
12
+ }
13
+ @options = default_options.merge(options)
14
+ @domain = domain
15
+ @name = name
16
+ @version = version
17
+ self
18
+ end
19
+ end
20
+
21
+ def initial_activity(name, version = nil)
22
+ if activity = Activity[name.to_sym, version]
23
+ @initial_activity_type = activity.to_activity_type
24
+ elsif activity = domain.activity_types[name.to_s, version]
25
+ @initial_activity_type = activity
26
+ end
27
+ end
28
+
29
+ def decision_loop
30
+ logger.info("Starting decision loop for #{name.to_s}, #{version} listening to #{task_list}")
31
+ domain.decision_tasks.poll(task_list) do |decision_task|
32
+ logger.info("Received decision task")
33
+ decision_task.new_events.each do |event|
34
+ logger.info("Processing #{event.event_type}")
35
+ case event.event_type
36
+ when 'WorkflowExecutionStarted'
37
+ start_execution(decision_task, event)
38
+ when 'ActivityTaskCompleted'
39
+ activity_completed(decision_task, event)
40
+ when 'ActivityTaskFailed'
41
+ activity_failed(decision_task, event)
42
+ end
43
+ end
44
+ end
45
+ rescue Timeout::Error => e
46
+ retry
47
+ end
48
+
49
+ def task_list
50
+ @options[:default_task_list][:name].to_s
51
+ end
52
+
53
+ def start_execution(decision_task, event)
54
+ logger.info "Starting the execution of the job."
55
+ if @on_start_execution && @on_start_execution.respond_to?(:call)
56
+ @on_start_execution.call(decision_task, event)
57
+ else
58
+ decision_task.schedule_activity_task initial_activity_type, :input => event.attributes.input
59
+ end
60
+ end
61
+
62
+ def activity_completed(decision_task, event)
63
+ if @on_activity_completed && @on_activity_completed.respond_to?(:call)
64
+ @on_activity_completed.call(decision_task, event)
65
+ else
66
+ if event.attributes.keys.include?(:result)
67
+ result = Map.from_json(event.attributes.result)
68
+ next_activity = result[:next_activity]
69
+ activity_type = domain.activity_types[next_activity[:name], next_activity[:version]]
70
+ decision_task.schedule_activity_task activity_type, :input => scheduled_event(decision_task, event).attributes.input
71
+ else
72
+ logger.info("Workflow #{name}, #{version} completed")
73
+ decision_task.complete_workflow_execution :result => 'success'
74
+ end
75
+ end
76
+ end
77
+
78
+ def activity_failed(decision_task, event)
79
+ logger.info("Activity failed.")
80
+ if @on_activity_failed && @on_activity_failed.respond_to?(:call)
81
+ @on_activity_failed.call(decision_task, event)
82
+ else
83
+ if event.attributes.keys.include?(:details)
84
+ details = Map.from_json(event.attributes.details)
85
+ case details.failure_policy.to_sym
86
+ when :abort
87
+ decision_task.cancel_workflow_execution
88
+ when :retry
89
+ logger.info("Retrying activity #{last_activity(decision_task, event).name} #{last_activity(decision_task, event).version}")
90
+ decision_task.schedule_activity_task last_activity(decision_task, event), :input => last_input(decision_task, event)
91
+ end
92
+ else
93
+ decision_task.cancel_workflow_execution
94
+ end
95
+ end
96
+ end
97
+
98
+ def start_workflow(input)
99
+ domain.workflow_types[name.to_s, version].start_execution(:input => input)
100
+ end
101
+
102
+ def on_start_execution(&block)
103
+ @on_start_execution = block
104
+ end
105
+
106
+ def on_activity_completed(&block)
107
+ @on_activity_completed = block
108
+ end
109
+
110
+ def on_activity_failed(&block)
111
+ @on_activity_failed = block
112
+ end
113
+
114
+ def self.[](name, version)
115
+ workflows[[name, version]]
116
+ end
117
+
118
+ def self.register(name, version, workflow)
119
+ workflows[[name, version]] = workflow
120
+ end
121
+
122
+ def method_missing(meth_name, *args)
123
+ if @options.has_key?(meth_name.to_sym)
124
+ @options[meth_name.to_sym] = args[0]
125
+ else
126
+ super
127
+ end
128
+ end
129
+
130
+ protected
131
+ def self.workflows
132
+ @workflows ||= {}
133
+ end
134
+
135
+ def self.swf
136
+ SimplerWorkflow.swf
137
+ end
138
+
139
+ def scheduled_event(decision_task, event)
140
+ decision_task.events.to_a[event.attributes.scheduled_event_id - 1]
141
+ end
142
+ def last_activity(decision_task, event)
143
+ scheduled_event(decision_task, event).attributes.activity_type
144
+ end
145
+
146
+ def last_input(decision_task, event)
147
+ scheduled_event(decision_task, event).attributes.input
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,16 @@
1
+ module SimplerWorkflow
2
+ class WorkflowCollection
3
+ def [](name, version)
4
+ registry[[name,version]]
5
+ end
6
+
7
+ def []=(name, version, value)
8
+ registry[[name, version]] = value
9
+ end
10
+
11
+ protected
12
+ def registry
13
+ @registry ||= {}
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/simpler_workflow/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Frederic Jean"]
6
+ gem.email = ["fred@snugghome.com"]
7
+ gem.description = %q{A wrapper around Amazon's Simple Workflow Service}
8
+ gem.summary = %q{A wrapper and DSL around Amazon's Simple Workflow Service with the goal of making it almost pleasant to define workflows.}
9
+ gem.homepage = "https://github.com/SnuggHome/simpler_workflow"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "simpler_workflow"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = SimplerWorkflow::VERSION
17
+
18
+ gem.add_dependency 'aws-sdk', '~> 1.4.0'
19
+ gem.add_dependency 'map'
20
+ gem.add_development_dependency 'rspec'
21
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simpler_workflow
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Frederic Jean
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-04-24 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: aws-sdk
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 7
29
+ segments:
30
+ - 1
31
+ - 4
32
+ - 0
33
+ version: 1.4.0
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: map
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 0
47
+ version: "0"
48
+ type: :runtime
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: rspec
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ hash: 3
59
+ segments:
60
+ - 0
61
+ version: "0"
62
+ type: :development
63
+ version_requirements: *id003
64
+ description: A wrapper around Amazon's Simple Workflow Service
65
+ email:
66
+ - fred@snugghome.com
67
+ executables: []
68
+
69
+ extensions: []
70
+
71
+ extra_rdoc_files: []
72
+
73
+ files:
74
+ - .gitignore
75
+ - Gemfile
76
+ - LICENSE
77
+ - README.md
78
+ - Rakefile
79
+ - lib/simpler_workflow.rb
80
+ - lib/simpler_workflow/activity.rb
81
+ - lib/simpler_workflow/domain.rb
82
+ - lib/simpler_workflow/version.rb
83
+ - lib/simpler_workflow/workflow.rb
84
+ - lib/simpler_workflow/workflow_collection.rb
85
+ - simpler_workflow.gemspec
86
+ homepage: https://github.com/SnuggHome/simpler_workflow
87
+ licenses: []
88
+
89
+ post_install_message:
90
+ rdoc_options: []
91
+
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ hash: 3
100
+ segments:
101
+ - 0
102
+ version: "0"
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ hash: 3
109
+ segments:
110
+ - 0
111
+ version: "0"
112
+ requirements: []
113
+
114
+ rubyforge_project:
115
+ rubygems_version: 1.8.20
116
+ signing_key:
117
+ specification_version: 3
118
+ summary: A wrapper and DSL around Amazon's Simple Workflow Service with the goal of making it almost pleasant to define workflows.
119
+ test_files: []
120
+