-
-
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')