-
-
Save trevordixon/3061477 to your computer and use it in GitHub Desktop.
| // Do things like: | |
| var app = new Routes(); | |
| app.use(Routes.static('../htdocs')); | |
| app.use(function(req, res, next) { | |
| req.startTime = new Date().getTime(); | |
| next(); | |
| }); | |
| app.all(/.+/, function(req, res, next) { | |
| console.log('Here comes a request.'); | |
| next(); | |
| }); | |
| app.get('/', function(req, res, next) { | |
| res.send('Hello there.'); | |
| }) | |
| app.post('/', function(req, res, next) { | |
| res.send(req.post.name); | |
| }); | |
| app.get('/test', function(req, res, next) { | |
| res.write('What a test.'); | |
| next(); | |
| }); | |
| app.get('/user/([a-zA-Z0-9]+)', function(req, res, next) { | |
| res.write('You are user: ' + req.params[0]); | |
| next(); | |
| }); | |
| app.get('/nonexistent', function(req, res) { | |
| res.send(404); | |
| }); | |
| app.get('/go_away', function(req, res) { | |
| res.redirect('/'); | |
| }); | |
| app.use(function(req, res) { | |
| console.log(new Date().getTime() - req.startTime); | |
| res.close(); | |
| }); | |
| app.listen(8080); |
| var Routes = (function() { | |
| var _ = {}, ctor = function(){}; | |
| _.bind = function bind(func, context) { | |
| var bound, args, slice = Array.prototype.slice; | |
| args = slice.call(arguments, 2); | |
| return bound = function() { | |
| if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); | |
| ctor.prototype = func.prototype; | |
| var self = new ctor; | |
| var result = func.apply(self, args.concat(slice.call(arguments))); | |
| if (Object(result) === result) return result; | |
| return self; | |
| }; | |
| }; | |
| var Res = function Res(res) { | |
| this.headers = res.headers || {}; | |
| this.res = res; | |
| this.statusCode = res.statusCode; | |
| }; | |
| Res.prototype.header = function(h, v) { | |
| if (v) return this.headers[h] = v; | |
| else return this.headers[h]; | |
| }; | |
| Res.prototype.send = function(body) { | |
| if (arguments.length == 2) { | |
| if (typeof body != 'number' && typeof arguments[1] == 'number') { | |
| this.statusCode = arguments[1]; | |
| } else { | |
| this.statusCode = body; | |
| body = arguments[1]; | |
| } | |
| } | |
| if (typeof body == 'number') { this.statusCode = body; body = ''; } | |
| else if (typeof body == 'object') body = JSON.stringify(body); | |
| this.close(body); | |
| return this; | |
| }; | |
| Res.prototype.write = function(data) { | |
| this.res.write(data); | |
| return this; | |
| }; | |
| Res.prototype.redirect = function(url) { | |
| this.header('Location', url); | |
| this.send(301); | |
| }; | |
| Res.prototype.close = function(data) { | |
| this.res.statusCode = this.statusCode; | |
| this.res.headers = this.headers || {}; | |
| this.write(data); | |
| this.res.close(); | |
| return this; | |
| }; | |
| var R = function Routes() { | |
| this.server = require('webserver').create(); | |
| this.routes = []; | |
| }; | |
| R.prototype.preRoute = function(req, res) { | |
| this.router.call(this, req, new Res(res)); | |
| }; | |
| R.prototype.router = function(req, res, i) { | |
| var i = i || 0; | |
| for (i; i < this.routes.length; i++) { | |
| var route = this.routes[i]; | |
| if (route.method == 'ALL' || route.method == req.method) { | |
| var match = req.url.match(route.route); | |
| if (match) { | |
| req.params = match.slice(1); | |
| try { | |
| return route.handler.call(this, req, res, _.bind(this.router, this, req, res, ++i)); | |
| } catch (err) { | |
| console.log(err.stack); | |
| return res.send(err.stack, 500); | |
| } | |
| } | |
| } | |
| } | |
| res.send('Not found', 404); | |
| }; | |
| R.prototype.addRoute = function(method, route, handler) { | |
| if (!(route instanceof RegExp)) route = new RegExp("^" + route + "$"); | |
| this.routes.push({method: method, route: route, handler: handler}); | |
| }; | |
| R.prototype.all = function(route, handler) { | |
| this.addRoute('ALL', route, handler); | |
| }; | |
| R.prototype.get = function(route, handler) { | |
| this.addRoute('GET', route, handler); | |
| }; | |
| R.prototype.post = function(route, handler) { | |
| this.addRoute('POST', route, handler); | |
| }; | |
| R.prototype.head = function(route, handler) { | |
| this.addRoute('HEAD', route, handler); | |
| }; | |
| R.prototype.put = function(route, handler) { | |
| this.addRoute('PUT', route, handler); | |
| }; | |
| R.prototype.delete = function(route, handler) { | |
| this.addRoute('DELETE', route, handler); | |
| }; | |
| R.prototype.use = function(handler) { | |
| this.addRoute('ALL', /.+/, handler); | |
| }; | |
| R.prototype.listen = function(port) { | |
| this.server.listen(port, _.bind(this.preRoute, this)); | |
| }; | |
| R.static = function(root) { | |
| var fs = require('fs'), | |
| root = fs.absolute(root); | |
| return function(req, res, next) { | |
| if (req.method != 'GET') return next(); | |
| var resource = req.url.slice(1), | |
| path = root + '/' + resource; | |
| if (resource && fs.isFile(path) && fs.isReadable(path)) { | |
| var file = fs.read(path); | |
| res.send(file); | |
| } else { | |
| next(); | |
| } | |
| } | |
| }; | |
| return R; | |
| })(); | |
| if (module && module.exports) module.exports = Routes; |
I have a hard time trying to figure out lines 7 to 12 in Routes.js, are they there to make sure the bound function works regardless if the function was called with new or called directly?
It's not actually an npm module, but a built-in PhantomJS module. http://code.google.com/p/phantomjs/wiki/Interface#WebServer_Module
I pulled the bind function out of underscore.js, and yes, it makes it possible to use the new keyword with a bound function. http://stackoverflow.com/questions/8552908/understanding-the-code-of-bind
Let's get some documentation up in haaaaaaaar!
Bug report:
if we add a route /test, and then request /test?foo=bar. The url(/test?foo=bar) will not match the route: new RegExp("^" + "/test" + "$"), and the req.params will not be set correctly.
//curl -I http://127.0.0.1:3000/test?foo=bar
app.get('/test', function(req, res) {
//...
}
but in expressjs, the route will work as expected.
Looks really nice! A lot of meta stuff there with bind etc. Trying to figure out the magic but would like to know what npm module you are using when calling require('webserver')