Last active
May 9, 2016 16:59
-
-
Save trevordixon/3061477 to your computer and use it in GitHub Desktop.
Very simple Express-like routing for PhantomJS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
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.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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?