![build status](https://secure.travis-ci.org/crcn/beanpoll.js.png)
Beanpole - Routing framework
Motivation
- Abstract communication between parts of an application
- keeps code modular
- works in-app, or with other protocols: amqp, http, etc.
This:
router.on({
'pull auth/user': function(req, res) {
},
'pull auth/user -> add/photos': function(req, res) {
}
})
Versus somethine like this:
var addPhotos = function(req, res) {
authUser(req, res, function() {
});
}
Projects using Beanpole
- celeri - CLI library
- bonsai - application server
- leche - Framework to build frontend / backend applications with the same code.
- daisy - Expose beanpole to: http, websockets, amqp (rabbitmq), etc.
- beandocs - Generate documentation from your beanpole route comments.
- beanprep - Scans beans in a given directory, and installs their dependencies.
- cupboard - Reverse package manager.
Beanpole ports
Overview
![Alt ebnf diagram](http://i.imgur.com/v1wdO.png)
The basic route consists of a few parts: the type
of route, and the channel
. Here are some examples:
router.on('pull hello/:name', ...);
and
router.on('push hello/:name', ...);
Push Routes:
- Used to broadcast a message, or change (1 to many).
- Doesn't expect a response.
- Multiple listeners per route.
Pull Routes:
- Used to request data from a particular route (1 to 1).
- Expects a response.
- One listener per route.
- examples:
- request to http-exposed route
Collect Routes:
- Used to request data from many listeners (1 to many, similar to pull).
- Expects a response.
Error Handling
function auth(credits, callback) {
if(credits.user != 'user' || credits.pass != 'pass') return callback(new Error('invalid credits'));
callback(false, { user: 'user', pass: 'pass' });
}
router.on({
'pull authenticate': function(req, res) {
auth(req.query, res.success(function(user) {
res.end(user);
}));
}
})
var req = router.request('authenticate').
error(function(err) {
console.log(err.stack);
}).
success(function(response) {
console.log(response);
}).
query({ user: 'user', pass: 'bad pass' }).
pull();
Custom Routes
You can easily create custom route handlers. Take celeri for example:
var beanpoll = require('beanpoll'),
structr = require('structr');
var CmdMessenger = structr({
_next: function(middleware) {
var self = this;
try {
middleware.listener(Structr.copy(middleware.params, data), function() {
return self.next();
});
} catch(e) {
self.response.error(e)
}
}
}, beanpoll.Messenger);
var CmdDirector = structr({
_newMessenger: function(message, middleware) {
return new CmdMessenger(message, middleware, this);
}
}, beanpoll.Director);
var router = beanpoll.router();
router.use(function() {
return {
name: 'console',
director: new CmdDirector('celeri', router)
}
});
router.on('console say/hello', function(data, next) {
});
Middleware can also be specified without using the token: ->
.An example:
router.on({
'pull my/*': function()
{
},
'pull my/profile': function()
{
}
});
Providing a wildcard *
tells the router that anything after the route must go through it.
Managing very long routes
You may run into a route which looks like this:
router.on({
'pull -public -method=POST remove/cache/subscribers -> profile/validate/SAVE_ARTICLE -> groups/:group/subscribers OR groups/:group/subscribers/add': function() {
});
To fix the ugliness, breakup the route and escape any linebreaks:
router.on({
'pull \
-public -method=POST \
remove/cache/subscribers -> \
profile/validate/SAVE_ARTICLE -> \
groups/:group/subscribers OR \
groups/:group/subscribers/add': function() {
}
})
You can also split it up:
router.on({
'pull \
remove/cache/subscribers -> \
profile/validate/SAVE_ARTICLE -> \
validate/group/subscribers': function() {
}
})
router.on({
'pull \
-public -method=POST \
validate/group/subscribers ->
groups/:group/subscribers OR \
groups/:group/subscribers/add': function() {
}
})
Methods
router.on(type[,listener])
Listens to the given routes
type
- string or object. String would contain the route. Object would contain multiple routes / listenerslistener
- function listening to the route given.
router.request(router)
returns the request builder
router.request('signup/user').
query({ username: 'blarg' }).
headers({ 'Content-Type': 'application/json' }).
success(function(response) {
}).
error(function(err) {
}).
response(err, response) {
}).
push();
type
- the channel broadcast a message to.data
- the data to push to the given routeoptions
- options for the given route
meta
- tags to use to filter out listeners
same as push, but expects a response
router.channels()
returns route expression
request.write(chunk)
Initializes a streamed response. Great for sending files
request.end([chunk])
Ends a response
request.hasNext()
Returns TRUE if there's a listener after the current one.
request.next()
Moves onto the next route.