Chainsaw
========

Build chainable fluent interfaces the easy way in node.js.

With this meta-module you can write modules with chainable interfaces.
Chainsaw takes care of all of the boring details and makes nested flow control
super simple too.

Just call `Chainsaw` with a constructor function like in the examples below.
In your methods, just do `saw.next()` to move along to the next event and
`saw.nest()` to create a nested chain.

Examples
========

add_do.js
---------

This silly example adds values with a chainsaw.

    var Chainsaw = require('chainsaw');
    
    function AddDo (sum) {
        return Chainsaw(function (saw) {
            this.add = function (n) {
                sum += n;
                saw.next();
            };
             
            this.do = function (cb) {
                saw.nest(cb, sum);
            };
        });
    }
    
    AddDo(0)
        .add(5)
        .add(10)
        .do(function (sum) {
            if (sum > 12) this.add(-10);
        })
        .do(function (sum) {
            console.log('Sum: ' + sum);
        })
    ;

Output:
    Sum: 5

prompt.js
---------

This example provides a wrapper on top of stdin with the help of
[node-lazy](https://github.com/pkrumins/node-lazy) for line-processing.

    var Chainsaw = require('chainsaw');
    var Lazy = require('lazy');
    
    module.exports = Prompt;
    function Prompt (stream) {
        var waiting = [];
        var lines = [];
        var lazy = Lazy(stream).lines.map(String)
            .forEach(function (line) {
                if (waiting.length) {
                    var w = waiting.shift();
                    w(line);
                }
                else lines.push(line);
            })
        ;
        
        var vars = {};
        return Chainsaw(function (saw) {
            this.getline = function (f) {
                var g = function (line) {
                    saw.nest(f, line, vars);
                };
                
                if (lines.length) g(lines.shift());
                else waiting.push(g);
            };
            
            this.do = function (cb) {
                saw.nest(cb, vars);
            };
        });
    }

And now for the new Prompt() module in action:

    var util = require('util');
    var stdin = process.openStdin();
     
    Prompt(stdin)
        .do(function () {
            util.print('x = ');
        })
        .getline(function (line, vars) {
            vars.x = parseInt(line, 10);
        })
        .do(function () {
            util.print('y = ');
        })
        .getline(function (line, vars) {
            vars.y = parseInt(line, 10);
        })
        .do(function (vars) {
            if (vars.x + vars.y < 10) {
                util.print('z = ');
                this.getline(function (line) {
                    vars.z = parseInt(line, 10);
                })
            }
            else {
                vars.z = 0;
            }
        })
        .do(function (vars) {
            console.log('x + y + z = ' + (vars.x + vars.y + vars.z));
            process.exit();
        })
    ;

Installation
============

With [npm](http://github.com/isaacs/npm), just do:
    npm install chainsaw

or clone this project on github:

    git clone http://github.com/substack/node-chainsaw.git

To run the tests with [expresso](http://github.com/visionmedia/expresso),
just do:

    expresso


Light Mode vs Full Mode
=======================

`node-chainsaw` supports two different modes. In full mode, every
action is recorded, which allows you to replay actions using the
`jump()`, `trap()` and `down()` methods.

However, if your chainsaws are long-lived, recording every action can
consume a tremendous amount of memory, so we also offer a "light" mode
where actions are not recorded and the aforementioned methods are
disabled.

To enable light mode simply use `Chainsaw.light()` to construct your
saw, instead of `Chainsaw()`.