sebastian

A flow-control library aimed at encouraging organized, testable code.

Sebastian

##Intro

Sebastian is a flow control library aimed at encouraging developers to write organized, testable code. It works in the browser or with Node.js.

There are many flow-control libraries out there, but none that I am quite satisfied with at the moment. Async is great, but I don't agree with Node.js-style callback conventions and I don't like mixing success and error condition logic. Also, I have found Async-wrapped code to be difficult to test. Sebastian is built to encourage construction of discrete chunks of manageable code that can be easily tested. I'm a big fan of Deferreds. Sebastian uses jQuery Deferred $.when() wrapper to treat asynchronous and synchronous steps/code the same.

  • For Node.js, jquery-deferred, but Q support may be added at a later date.
  • For browser environments, jQuery.
var flow = require("sebastian").flow;
 
flow("helloFlow")
    .step("one", function() {
        console.log("hello..");
    }).step("two", function() {
        console.log("hello 2..");
    }).begin();

This creates a flow called "helloFlow", adds to steps two the flow, and starts the flow.

    require(["jquery", "path/to/sebastian"], function($, sebastian) {
 
        //call the local definition
        sebastian("blah")
                .step("one", function() {
                    console.log("step one..");
                })
                .step("two", function() {
                    console.log("step two..");
                })
                .begin();
 
    });
<script type="text/javascript" src="vendor/jquery/jquery.min.js"></script>
<script type="text/javascript" src="/sebastian.js"></script>
 
<script type="text/javascript">
 
    $.Flow("firstFlow")
            .step("one", function() {
                console.log("executing step one in firstFlow...");
            })
            .step("two", function() {
                console.log("executing step two in firstFlow...");
            }).begin();
 
 
</script>
 

To see all examples (Node.js) go here

When flow is executed sequentially, result from each step is passed on to the next step.

    flow("mockAsyncFlow")
            .step("one", function() {
                var deferred = $.Deferred();
                setTimeout(function() {
                    console.log("step 1...");
                    deferred.resolve();
                }, 301);
 
                return deferred;
 
            })
            .step("two", function() {
                var deferred = $.Deferred();
                setTimeout(function() {
                    console.log("step 2...");
                    deferred.resolve();
                }, 200);
 
                return deferred;
            })
            .step("three", function() {
                var deferred = $.Deferred();
                setTimeout(function() {
                    console.log("step 3...");
                    deferred.resolve();
                }, 100);
 
                return deferred;
            })
            .begin();
 
    flow("mockAsyncFlow")
            .step("one", function() {
                var deferred = $.Deferred();
                setTimeout(function() {
                    console.log("step 1...");
                    deferred.resolve();
                }, 301);
 
                return deferred;
 
            })
            .step("two", function() {
                var deferred = $.Deferred();
                setTimeout(function() {
                    console.log("step 2...");
                    deferred.resolve();
                }, 200);
 
                return deferred;
            })
            .step("three", function() {
                var deferred = $.Deferred();
                setTimeout(function() {
                    console.log("step 3...");
                    deferred.resolve();
                }, 100);
 
                return deferred;
            })
            .parallel()
            .begin();
 
    flow("someFlow")
        .step("one", function() {
            console.log("one..");
        })
        .step("two", function() {
            console.log("two..");
        })
        .step("three", function() {
            console.log("three..");
        });
 
    flow("someFlow")
        .skip("one")
        .begin();
 
    flow("someFlow")
            .step("one", function() {
                console.log("one..");
            })
            .step("two", function() {
                console.log("two..");
            })
            .step("three", function() {
                console.log("three..");
            });
 
    flow("someFlow")
        .skip("one")
        .skip("two")
        .begin();
 
    flow("someFlow")
            .step("one", function() {
                console.log("one..);
            })
            .step("two", function() {
                console.log("two..);
            })
            .step("three", function() {
                console.log("three..);
            });
 
    flow("someFlow")
        .startOn("two")
        .begin();
 
    flow("firstFlow")
            .step("one", function() {
                console.log("executing step one in firstFlow...");
            })
            .step("two", function() {
                console.log("executing step two in firstFlow...");
            });
 
    flow("secondFlow")
            .step("one", function() {
                console.log("executing step one in second flow...");
                return $.Deferred().reject("blah");
            })
            .step("two", function() {
                console.log("executing step two in second flow...");
            });
 
 
    console.log("beginning flow two, chained to one on failure");
    flow("secondFlow").onFailure().jumpTo("firstFlow").begin();
 
    flow("firstFlow")
            .step("one", function() {
                console.log("executing step one in firstFlow...");
            })
            .step("two", function() {
                console.log("executing step two in firstFlow...");
            });
 
    flow("secondFlow")
            .step("one", function() {
                console.log("executing step one in second flow...");
                return $.Deferred().reject("blah");
            })
            .step("two", function() {
                console.log("executing step two in second flow...");
            });
 
 
    console.log("beginning flow two, chained to one on failure 'blah'");
    flow("secondFlow").onFailure("blah").jumpTo("firstFlow").begin();
 
    flow("firstFlow")
            .step("one", function() {
                console.log("executing step one in firstFlow...");
            })
            .step("two", function() {
                console.log("executing step two in firstFlow...");
            });
 
    flow("secondFlow")
            .step("one", function() {
                console.log("executing step one in second flow...");
            })
            .step("two", function() {
                console.log("executing step two in second flow...");
            });
 
 
    console.log("beginning flow two, chained to one on success");
    flow("secondFlow").onSuccess().jumpTo("firstFlow").begin();
 
    flow("secondFlow")
            .step("one", function() {
                console.log("executing step one in second flow...");
                return $.Deferred().reject("blah");
            })
            .step("two", function() {
                console.log("executing step two in second flow...");
            });
 
 
    console.log("beginning flow two, chained to one on failure 'blah'");
    flow("secondFlow")
        .onFailure("blah")
        .handleWith(function() {
                console.log("it failed!");
            }).begin();
 
 
    flow("someFlow").step("one", function() {});
 
    var someFlow = flow("someFlow");
 
    someFlow.begin();
 
    flow("someFlow").step("one", function() {});
 
    var someFlow = flow("someFlow");
 
    var step = someFlow.step("one");
 
    var name = step.name; //the step name
    var callback = step.callback; //the actual step function/logic