capistrano/capistrano — pull request #243 by carsomyr (2012-07-23 04:45:55+00:00)
Title: Task continuations: A new feature parameterizing task executions

Firstly, apologies for the huge pull request. I am proposing a fairly extensive feature, so I needed to make sure that it was fully tested and documented, hence the numerous commits, files modified, and documentation.

#### Introduction

The motivation behind task continuations is to provide a way to alter the behavior of tasks without rewriting them. A few projects (the [multistage extension](https://github.com/capistrano/capistrano/wiki/2.x-Multistage-Extension), [capistrano-multiconfig](https://github.com/railsware/capistrano-multiconfig), [capistrano-multiproject](https://github.com/olek/capistrano-multiproject), [CapHub](https://github.com/railsware/caphub)) already do various aspects of this, albeit without modification of Capistrano itself. The implementation strategy in each case involves executing multiple tasks and having later tasks reflect on the results of earlier tasks. It invariably presumes some sort of implicit dependency among tasks.

After thinking about how the current plugins for multi{config, stage, project} worked, I decided to take a different tack: Tasks would remain independent, but they could be chained in a recursive manner. That's the essence of a task continuation (taking after the functional programming notion of [continuation](http://en.wikipedia.org/wiki/Continuation)): It's like a normal task in all respects save for the block parameter in the task body, which represents a deferred computation.

# This is a normal task.
task :t1 do
puts "Executing the main task."
end

# This is a task continuation.
task :tc1 do |&block|
puts "Executing pre-task code."
block.call
puts "Executing post-task code."
end

By applying potentially multiple, nested task continuations to the main task (achieved with a natural extension of the Capistrano command line and DSL syntax), it's not hard to imagine how the main task's environment could be modified to one's liking. In the example above, executing any of the snippets below would result in the desired output.

$ cap tc1:t1

or

$ cap tc1::t1

or

task :t2 do
tc1.t1
end

$ cap t2

outputs

* executing the task continuation `tc1'
Executing pre-task code.
* executing the task `t1'
Executing the main task.
Executing post-task code.

The above example is just a taster. The next section details the documentation and examples dedicated to helping you better understand the proposed feature.

#### Documentation and Examples

I've added a [wiki page](https://github.com/carsomyr/capistrano/wiki/2.x-Task-Continuations) at my fork containing the full task continuations specification. It contains more in-depth discussion with regards to usage. Perhaps more importantly, the [Capistrano-MultiEnv](https://github.com/carsomyr/capistrano-multienv) project serves as a demonstration of the expressive power of task continuations and helps me with my own deployment needs. **By following the instructions in the README, you'll be able to see how task continuations can be used to parameterize multi{cloud, stage, project} deployments.**

#### Logistical Details

1. I am looking for the broader community's feedback on what they like and what can be improved.
2. I believe that the feature is nearly backwards compatible. An area of trouble could be with DSL usage, since DSL methods now return `NamespaceContext` objects rather than `Namespace` objects. Practically speaking, few users should be affected, as most invoke the DSL to execute tasks within tasks.
3. @leehambley, what additional steps should I take to guide a pull request of this size smoothly through the review process?