UNPKG

mach

Version:
150 lines (123 loc) 4.25 kB
var d = require('describe-property'); var RoutingProperties = require('../utils/RoutingProperties'); var createMapper = require('./mapper'); var createRouter = require('./router'); function mapperCreator(mappings) { return function (app) { app = createMapper(app); for (var i = 0, len = mappings.length; i < len; ++i) app.map.apply(app, mappings[i]); return app; }; } function routerCreator(routes) { return function (app) { app = createRouter(app); for (var i = 0, len = routes.length; i < len; ++i) app.route.apply(app, routes[i]); return app; }; } /** * A middleware that aids in building complex apps that are fronted by other * middleware in a "middleware stack". Also provides several other useful methods * for request mapping and routing that make this middleware a good choice when * working with mach at a high level or when getting started for the first time. * * Middleware are placed into the stack by calling the `use` method which passes * along any additional arguments that it receives directly on to the middleware * when the stack is compiled. * * Other stacks can be "mounted" easily at various locations using the `map` * method. Routes can be added using `route`, `get`, `post`, etc. When a request * is received, all middleware, mappings, and routes run in the order they are * defined in the stack, top to bottom. * * var app = mach.stack(); * * app.use(mach.gzip); * app.use(mach.file, __dirname + '/public'); * * // Use an image server to serve requests that begin * // with /images out of /public/img. * app.map('/images', mach.file('/public/img')); * * // Since this call is *after* the call to map, this middleware * // will not run when requests begin with "/images". * app.use(mach.params); * * app.get('/', function (request) { * return "The params are: " + JSON.stringify(request.params); * }); * * app.post('/posts/:post_id/messages', function (request) { * // ... * }); * * mach.serve(app); * * Note: A stack is compiled the first time it is called. When a stack is * compiled, all middleware is invoked with the downstream app plus any * additional arguments that were passed to the call to stack.use. As long as * the stack doesn't change between requests, this happens only once. */ function createStack(app) { var layers = [], mappings = [], routes = []; var compiledApp; function compile(app) { if (routes.length) app = routerCreator(routes)(app); if (mappings.length) app = mapperCreator(mappings)(app); var index = layers.length; while (index) app = layers[--index].call(this, app); return app; } function stack(conn) { return conn.call(compiledApp || (compiledApp = compile(app))); } Object.defineProperties(stack, { /** * Declares that the given `middleware` should be used at the current point * in the stack. Any additional arguments to this function are passed along * to the middleware with the downstream app as the first argument when the * stack is compiled. */ use: d(function (middleware) { var args = Array.prototype.slice.call(arguments, 1); if (mappings.length) layers.push(mapperCreator(mappings.splice(0, mappings.length))); if (routes.length) layers.push(routerCreator(routes.splice(0, routes.length))); layers.push(function (app) { return middleware.apply(this, [ app ].concat(args)); }); compiledApp = null; }), /** * Uses a mapper to map a URL path to an app. */ map: d(function (location, app) { mappings.push([ location, app ]); compiledApp = null; }), /** * Uses a router to route URLs that match a pattern/method to an app. */ route: d(function (pattern, methods, app) { routes.push([ pattern, methods, app ]); compiledApp = null; }), /** * Sets the given app as the default for this stack. */ run: d(function (downstreamApp) { app = downstreamApp; compiledApp = null; }) }); Object.defineProperties(stack, RoutingProperties); return stack; } module.exports = createStack;