aws/aws-flow-ruby — pull request #56 by jacqueshimself (2014-07-02 09:56:16+00:00)
Title: Adding a "runner" utility 

This utility should help with the deployment of workflow and activity workers, as well as the registration of domains. It is packaged as a command line utility, aws-flow-ruby, that gets installed when the gem is installed.

Here is some documentation on how the utility can be used:

SWF has released the code for sample workflows, together with instructions for running them at: https://github.com/awslabs/aws-flow-ruby-samples

Here, we focus on the Hello World example at https://github.com/awslabs/aws-flow-ruby-samples/tree/master/Samples/hello_world

Running this sample involves running three separate command line utilities (with embed hard-coded defaults to simplify getting started). What these utilities do behind the scenes is:
* register the domain in which the workflow type, activity type, and workflow execution will live
* start a workflow worker which will run the decider code
* start an activity worker which will run the activities's code

Here is how the runner can be used to run the Hello World sample in a simple and configurable manner.

Clone the samples:
```bash
git clone https://github.com/awslabs/aws-flow-ruby-samples
```

At this point, there are two main ways to specify how the application should be run:
* a fully explicit way
* a conventions-based "container light" way.

### Fully explicit specification

In the fully-explicit specification, the runner is told explicitly where the code should be loaded from and which workflows and activities classes should be passed to the workers.

To that effect, store the following JSON spec in a file, say helloWorld.json, and pass it to the runner as follows:
```bash
aws-flow -f helloWorld.json
```

```javascript
{
"domains": [
{
"name": "HelloWorld",
"retention_in_days": 10
}
],
"workflow_paths": [ "aws-flow-ruby-samples/Samples/hello_world/lib/helloworld_workflow.rb" ],
"workflow_workers": [
{
"domain": "HelloWorld",
"task_list": "workflow_tasklist",
"workflow_classes": [ "HelloWorldWorkflow" ],
"number_of_workers": 1
}
],
"activity_paths": [ "aws-flow-ruby-samples/Samples/hello_world/lib/helloworld_activity.rb" ],
"activity_workers": [
{
"domain": "HelloWorld",
"task_list": "activity_tasklist",
"activity_classes": [ "HelloWorldActivity" ],
"number_of_forks_per_worker": 1,
"number_of_workers": 1
}
]
}
```

Here is what the components of this spec are:
* domains, a list of domains to register. Each domain has 2 attributes:
* name, the name of the domain
* retention_in_days, the period for which workflow executions in the domain are retained after they close
* workflow_paths, a list of files that should be "require"d to load code
* workflow_workers, a list of groups of workflow workers to spawn. Each has the following attributes:
* domain, the name of the domain in which the worker will operate
* task_list, the name of the task list to poll for tasks
* workflow_classes, the list of workflow implementations (ruby class names) that the worker will provide
* number_of_workers, the number of processes to spawn
* The part of the spec for activity workers is the same as the spec for workflow workers except this additional attribute of an activity worker spec:
* number_of_forks_per_worker, the maximum number of forked processes to which an activity worker (which handles the poll and delegates the work to a forked process) can spawn.

### "Container-light" specification

The runner does also support a lighter way of specifying what should be run. This mode relies on a few simple conventions:
* a directory named "flow", in the same directory as the JSON specification. In that directory, two files:
* activities.rb : it can contain "require" statements relative to where the json spec is, or all the workflows code for your application
* workflows.rb: it can contain "require" statements relative to where the json spec is, or all the activities code for your application
To start the activities (resp. workflows) workers, the runner scans the object space to locate classes in which the "AWS::Flow::Activities" (resp. "AWS::Flow::Workflows") module has been mixed in and adds them as implementations that the workers will serve.

The auto-loading can be overridden by specifying load paths. The autodiscovery can be overridden by specifying the classes.

The hello world example can then be run with:
* flow/activities.rb containing:
```ruby
require 'aws-flow-ruby-samples/Samples/hello_world/lib/helloworld_activity.rb'
```
* flow/workflows.rb containing:
```ruby
require 'aws-flow-ruby-samples/Samples/hello_world/lib/helloworld_workflow.rb'
```
* helloWorld.json.simpler containing:
```javascript
{
"domains": [
{
"name": "HelloWorld-2014-06-30-11-49",
"retention_in_days": 10
}
],
"workflow_workers": [
{
"domain": "HelloWorld-2014-06-30-11-49",
"task_list": "workflow_tasklist",
"number_of_workers": 1
}
],
"activity_workers": [
{
"domain": "HelloWorld-2014-06-30-11-49",
"task_list": "activity_tasklist",
"number_of_forks_per_worker": 1,
"number_of_workers": 1
}
]
}
```
* using the following command line:
```bash
aws-flow -f helloWorld.json.simpler
```