beanpoll

0.2.19 • Public • Published

build status

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) {
        //auth here
    },
 
    /**
     */
 
    'pull auth/user -> add/photos': function(req, res) {
        //add photos here
    }
})

Versus somethine like this:

 
 
var addPhotos = function(req, res) {
    authUser(req, res, function() {
        //do stuff here
    });
}
 

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

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) {
        
        //don't bother handling errors - done by response
        auth(req.query, res.success(function(user) {
            
            res.end(user);
 
        }));
    }
})
 
 
 
//error
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');
 
//handles the message, response, and middleware
var CmdMessenger = structr({
    
    _next: function(middleware) {
        
        var self = this;
 
        try {
 
            //call the command handler, and wrap the LAST parameter as a next function
            middleware.listener(Structr.copy(middleware.params, data), function() {
                return self.next();
            });
 
        } catch(e) {
            self.response.error(e)
        }
 
    }
 
}, beanpoll.Messenger);
 
 
//the "Event Emitter"
var CmdDirector = structr({
 
    _newMessenger: function(message, middleware) {
        return new CmdMessenger(message, middleware, this);
    }
 
}, beanpoll.Director);
 
 
 
var router = beanpoll.router();
 
 
//use the new plugin
router.use(function() {
    return {
        name: 'console',
        director: new CmdDirector('celeri', router)
    }
});
 
//use it:
router.on('console say/hello', function(data, next) {
    //do stuff here
});
 

Middleware can also be specified without using the token: ->.An example:

    
router.on({               
    
    /**
     */
    
    'pull my/*': function()
    {
        //authorize user
    },  
    
    /**
     */
    
    'pull my/profile': function()
    {                 
        //goes through authorization first 
    }
});
 

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 / listeners
  • listener - 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' }).
 
//called when the second param is present. 
success(function(response) {
    
}).
 
//separated error from the response
error(function(err) {
    
}).
 
//called when there's a result, or error
response(err, response) { 
    
}).
 
//type of request: push, pull, collect, your own
push();

router.push(route[, query][, headers])

  • type - the channel broadcast a message to.
  • data - the data to push to the given route
  • options - options for the given route
    • meta - tags to use to filter out listeners

router.pull(route[, query][, headers][, callback])

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.

Readme

Keywords

none

Package Sidebar

Install

npm i beanpoll

Weekly Downloads

113

Version

0.2.19

License

none

Last publish

Collaborators

  • architectd
  • crcn