Fluid.js
Fluid.js is a simple fluent interface API for javascript and node.js. It is used to create fluent interfaces around existing vanilla objects, without all the boiler plate code.
This is a useful extension to the popular Async module.
NOTE: Fluid.js currently only supports methods that take a callback as the last argument:-
function(options, callback) -> callback(err, result)
function(arg1, arg2, ..., callback) -> callback(err, result)
etc...
The Problem
Flow control and callbacks can become very messy and unmanagable when using lots of asynchronous functions. Many libraries exist for node.js that make things much less painful. This module is meant to add yet another tool to your belt.
For example a typical bit of file processing might look something like:-
fs.readFile("./input1.txt", function(err, input1Buffer) {
if (err) {console.log(err)} else {
fs.readFile("./input2.txt", function(err, input2Buffer) {
if (err) {console.log(err)} else {
var content = input1Buffer.toString() + input2Buffer.toString();
fs.writeFile("./output.txt", content, function(err) {
if (err) {console.log(err)} else {
//do something
}
});
}
});
}
});
With Fluid.js we could write it like this:-
$f(fs).readFile("./input1.txt").readFile("./input2.txt")
.writeFile("./output1.txt", $f("this.readFile[0].toString() + this.readFile[1].toString()"))
.go(function(err, values) {
if (err) {console.log(err)} else {
//do something
}
});
Any error handling is managed in the "go" callback and all callback return values are available for usage.
Quick Examples
Setup
var $f = require("fluid");
Example Objects
var myFirstObj = {
doSomething : function(options, callback) {
//do something
callback(err, result);
},
doSomethingElse : function(options, callback) {
//do something else
callback(err, result);
}
};
var mySecondObj = {
doSomethingMore : function(options, callback) {
//do something more
callback(err, result);
}
};
Streamlined
Note: series is the default flow control mode, so it can be ommitted
$f(myFirstObj).doSomething(/* args */).doSomethingElse(/* args */)
.with(mySecondObj).doSomethingMore(/* args */)
.go(function(err, results) {
// results is now an array of return values from each method callback
});
Expressive
$f().series()
.with(myFirstObj)
.doSomething(/* args */)
.doSomethingElse(/* args */)
.with(mySecondObj)
.doSomethingMore(/* args */)
.go(function(err, results) {
// results is now an array of return values from each method callback
});
Multiple flow control types
$f()
.series()
.with(myFirstObj)
.doSomething(/* args */)
.doSomethingElse(/* args */)
.parallel()
.with(mySecondObj)
.doSomethingMore(/* args */)
.doSomethingMorer(/* args */)
.doSomethingMorerer(/* args */)
.go(function(err, results) {
// results is now an array of return values from each method callback
});
Install
To install with node package manager:
npm install fluid
Test
To test with node package manager:
npm test
Download
Releases are available for download from GitHub.
Documentation
Fluid Context Methods
### fluid([context])
Creates a new fluent interface (fluid context), optionally wrapping an initial user specifed application context.
Note, if desired, the initial context can be left blank and applied later using the with command.
Arguments
- context - An initial application context to wrap with fluent methods.
- Returns a new fluid context.
Example
$f(fs).readFile("./test.txt")
/* .etc.etc... */
.go(function(err, res) { /* finished */ }
Fluid Context Methods
### with(context)
Switches the current application context to the one specified.
Note, The context can be switch as many times as required. Useful when working with multiple libraries.
Arguments
- context - An application context to wrap.
- Returns the current fluid context.
Example
$f(myObj).doSomething(/* args */)
.with(myObj2).doSomethingElse(/* args */)
/* .etc.etc... */
.go(function(err, res) { /* finished */ });
### series()
Creates a new group of method calls that will be executed in series. Multiple groups with different flow control types can be used together, and will themselves be executed in series when the go command is called.
Note, This is the default execution mode when a new fluid context has been created.
Arguments
- Returns the current fluid context.
Example
$f(myObj).series()
.doSomething(/* args */)
.doSomethingElse(/* args */)
/* .etc.etc... */
.go(function(err, res) { /* finished */ });
### parallel()
Creates a new group of method calls that will be executed in parallel. Multiple groups with different flow control types can be used together, and will themselves be executed in series when the go command is called.
Note, see the Async module for more information on the difference between series and parallel flow controls.
Arguments
- Returns the current fluid context.
Example
$f(myObj).parallel()
.doSomething(/* args */)
.doSomethingElse(/* args */)
/* .etc.etc... */
.go(function(err, res) { /* finished */ });
### info(options)
Provides a way to explicitly name callback values on the result. Also allows decorating errors with extra detail.
Arguments
- options - Options for providing explicit detail for result
- name - String. Specifies a name for the callback return value on the result.
- error - String. Specifies extra detail to decorate any error raised.
Example
$f(fs)
.info({name : "test1"}).readFile("./test1.txt")
.info({name : "test2"}).readFile("./test2.txt")
.go(function(err, result) {
/*
result:-
{
test1 : [<Buffer>],
test2 : [<Buffer>]
}
*/
});
You can also use the short hand:-
$f(fs)
({name : "test1"}).readFile("./test1.txt")
({name : "test2"}).readFile("./test2.txt")
.go(function(err, result) {});
Note, there is no '.' before the '('. The fluid context itself wraps the info function.
### custom(customFunction(callback))
Creates a new custom task that is added to the existing group. The custom task must invoke the callback provided when finished processing.
Arguments
- customFunction(callback) - Some custom code that will receive a callback to be invoked upon completion.
- callback - Function. A callback to invoke when custom code has completed.
Example
$f(fs)
.readFile("./test1.txt")
.readFile("./test2.txt")
.custom(function(callback) {
callback(null, this.readFile[0].toString() + this.readFile[1].toString());
})
.writeFile("./output.txt", $f("this.custom[0]"))
.go(function(err, result) {});
### self()
If the context being wrapped is a function itself, it can be invoked using the self method.
Example
var myFunc = function(arg, callback) {
callback(null, arg + 1);
}
$f(myFunc)
.self(1)
.self(2)
.go(function(err, result) {});
### go([options,] callback)
Executes all queued function calls against their registered application contexts, then invokes the specified callback when completed, or any of the methods error.
If multiple flow control groups have been created, each group will be executed in series.
Arguments
- Optional. options - Options for execution
- debug - Boolean. Logs out to the console information about execution for debugging.
- callback(err, result) - A callback that is invoked after all of the methods have been executed, or an error occurrs. If no error occurrs, result will be an object containing arrays of values from each function callback.
Example
// assuming an object exists called myObj
$f(myObj).series()
.doSomething(/* args */)
.doSomething(/* args */)
.doSomethingElse(/* args */)
.parallel()
.doSomethingMore(/* args */)
.go(function(err, result) {
if (err) { /* error */ } else {
/*
result :-
{
"doSomething" : [
{ /* return value from first doSomething */ }
{ /* return value from second doSomething */ }
],
"doSomethingElse" : [{ /* return value from doSomethingElse */ }],
"doSomethingMore" : [{ /* return value from doSomethingMore */ }]
}
*/
}
});
### Context Methods
The fluid context will wrap all function properties of an object.
NOTE: Fluid.js currently only supports methods that take a callback as the last argument:-
function(options, callback) -> callback(err, result)
function(arg1, arg2, ..., callback) -> callback(err, result)
etc...
Example
var myCalculator = {
addTen : function(val, callback) {
callback(null, val + 10 );
},
multiplyByTen : function(val, callback) {
callback(null, val * 10);
}
};
$f(myCalculator)
.addTen(1)
.addTen(2)
.multiplyByTen(2)
.go(function(err, result) {
/*
result :-
{
"addTen" : [11, 12],
"multiplyByTen" : [20]
}
*/
});
You can even use the result of previous callbacks in the arguments to other functions. See late bound arguments.
$f(myCalculator)
.addTen(1)
.multiplyByTen($f("this.addTen[0]"))
.go(function(err, result) {
/*
result :-
{
"addTen" : [11],
"multiplyByTen" : [110]
}
*/
});
### Late Bound Arguments - fluid(expression)
Late bound arguments are useful when you need the result of a previous function as an argument to another.
Arguments
- expression - String. A javascript expression to be evaluated when the function is called. 'this' is the current result so far, and will contain return values of all previously executed callbacks.
Example
$f(myCalculator)
.addTen(1)
.multiplyByTen($f("this.addTen[0]"))
.go(function(err, result) {
/*
result :-
{
"addTen" : [11],
"multiplyByTen" : [110]
}
*/
});
Note, Fluid treats all string passed to it as late bound argument expressions.
License
Copyright(c) 2012 Pete Clark
MIT license
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in ALL copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.