README.md in seam-0.0.9 vs README.md in seam-0.0.10
- old
+ new
@@ -1,9 +1,139 @@
# Seam
-TODO: Write a gem description
+Simple workflows in Ruby.
+## Usage
+
+Seam is meant for situations where you want to take some entity (user, order, ec.) through a long-running process that is comprised of multiple steps.
+
+For example, if you want every new user a "hello" email after signup, then wait a few days, and then send a "gone so soon?" email if they haven't signed in again.
+
+This gem provides some simple tools for building and executing this process.
+It provides a way to define the process, break it up into separate components, and then send entities through the process.
+
+####Define a workflow####
+
+To start, define a workflow.
+
+````
+flow = Seam::Flow.new
+flow.send_order_to_warehouse
+flow.wait_for_order_to_be_shipped wait_up_to: 7.days
+flow.send_shipping_email email_template: 'shipping_7'
+flow.mark_order_as_fulfilled
+````
+
+A flow will convert any method call you make into a step that has to be completed.
+
+You can also pass a hash to the method, which will be saved for later.
+
+````
+flow.wait_for_order_to_be_shipped wait_up_to: 7.days
+````
+
+####Starting an instance of the flow####
+
+Starting an instance of the flow is done with "start":
+
+````
+flow.start order_id: '1234'
+````
+
+An instance of this effort was created and saved in whatever persistence is being used (in-memory by default).
+
+This effort will start at the first step (send_order_to_warehouse) and then progress through the steps as they are completed.
+
+"start" also returns the effort that was created, and it will look like this:
+
+````
+<Seam::Effort
+ @completed_steps=[],
+ @history=[],
+ @complete=false,
+ @id="1ecc4cbe-16af-45f6-8532-7f37493ec11c",
+ @created_at=2013-08-20 22:58:07 -0500,
+ @next_execute_at=2013-08-20 22:58:07 -0500,
+ @next_step="send_order_to_warehouse",
+ @flow={"steps"=>[{"name"=>"send_order_to_warehouse", "type"=>"do", "arguments"=>{}}, {"name"=>"wait_for_order_to_be_shipped", "type"=>"do", "arguments"=>{}}, {"name"=>"send_shipping_email", "type"=>"do", "arguments"=>{}}, {"name"=>"mark_order_as_fulfilled", "type"=>"do", "arguments"=>{}}]},
+ @data={"order_id"=>"1234"}>
+````
+
+So we have a unique instance of this flow and the instance has been saved in the database. The first step to be executed for this instance is "send_order_to_warehouse", so let's create a worker for this step.
+
+####Defining workers for each step####
+
+A workflow is comprised of steps, and each step needs a worker. Each worker will execute whatever it was meant to do, and then either:
+
+1. Pass the workflow instance to the next step on the process, or
+2. Delay the step execution for a later date, or
+3. End the entire workflow process for the instance.
+
+Since send_order_to_warehouse is the first step in this workflow, let's build the worker for it first:
+
+````
+class SendOrderToWarehouseWorker < Seam::Worker
+ def process
+ # the current workflow instance is available as "effort"
+ order = Order.find effort.data['order_id']
+ warehouse_service.send order
+
+ # by default, if this worker completes with no error the workflow instance will be sent to the next step
+ end
+end
+````
+
+If you name your class as a camel-case version of the step, Seam will automatically bind up the worker to the step.
+
+To execute the worker, use:
+
+````
+SendOrderToWarehouse.execute_all
+````
+
+This method will look for all workflow instances that are currently ready for the step in question.
+
+####Progressing through the workflow####
+
+By default, steps are considered as being completed when the worker completes successfully. There might be times where you don't want to go quickly, like the next step in this example:
+
+````
+class WaitForOrderToBeShippedWorker < Seam::Worker
+ def process
+ effort.data["shipping_status"] = # some method that returns the shipping status
+ unless effort.data["shipping_status"] == "shipped"
+ try_again_in 4.hours
+ end
+ end
+end
+````
+
+"try_again_in" can be used to signal that the step has not been completed and should be retried later.
+
+"eject" can also be used to signify that the entire workflow should be stopped, like so:
+
+````
+class WaitForOrderToBeShippedWorker < Seam::Worker
+ def process
+ effort.data["shipping_status"] = # some method that returns the shipping status
+ if effort.data["shipping_status"] == "cancelled"
+ eject # no need to continue!
+ end
+ end
+end
+````
+
+####History####
+
+As workflow instances progress through each step, the history of every operation will be stored. A history of the "data" block before and after each step run is also stored.
+
+The history is available through:
+
+````
+effort.history
+````
+
## Installation
Add this line to your application's Gemfile:
gem 'seam'
@@ -13,13 +143,9 @@
$ bundle
Or install it yourself as:
$ gem install seam
-
-## Usage
-
-TODO: Write usage instructions here
## Contributing
1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)