Created
July 5, 2011 12:52
-
-
Save tj/1064789 to your computer and use it in GitHub Desktop.
express auto-generated rest client
This file contains hidden or 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 express = require('express') | |
, client = require('../') | |
, app = express.createServer(); | |
var users = [ | |
{ name: 'tobi' } | |
, { name: 'loki' } | |
, { name: 'jane' } | |
, { name: 'manny' } | |
]; | |
var repos = { | |
manny: ['express', 'jade', 'stylus'] | |
, tobi: ['kue', 'cluster'] | |
}; | |
app.set('views', __dirname + '/views'); | |
app.set('view engine', 'ejs'); | |
app.register('.html', require('ejs')); | |
app.use(express.bodyParser()); | |
app.use(express.static(__dirname + '/public')); | |
app.param('id', function(req, res, next, id){ | |
var user = users[id]; | |
if (!user) return next('route'); | |
req.user = user; | |
user.id = id; | |
next(); | |
}); | |
app.get('/', function(req, res, next){ | |
res.render('index'); | |
}); | |
app.get('/api/users', function(req, res, next){ | |
res.json(users); | |
}); | |
app.del('/api/users', function(req, res){ | |
users.length = 0; | |
res.json({ message: 'users deleted' }); | |
}); | |
app.get('/api/user/:id', function(req, res, next){ | |
res.json(req.user); | |
}); | |
app.get('/api/user/:id/repos', function(req, res, next){ | |
res.json(repos[req.user.name]); | |
}); | |
app.put('/api/user/:id', function(req, res, next){ | |
users[req.user.id] = req.body; | |
res.json({ message: 'user updated' }); | |
}); | |
app.del('/api/user/:id', function(req, res, next){ | |
users.splice(req.user.id, 1); | |
res.json({ message: 'user deleted' }); | |
}); | |
app.use('/api', client('/api', app)); | |
app.use(function(req, res){ | |
res.send(404); | |
}); | |
console.log('// generated js:'); | |
console.log(client.compile('/api', app)); | |
app.listen(3000); |
This file contains hidden or 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 client = function(exports, request){ | |
// setup exports | |
exports.users = exports.users || {}; | |
exports.user.repos = exports.user.repos || {}; | |
/* | |
* GET users. | |
*/ | |
exports.users = function(){ | |
return request("GET", "/api/users"); | |
}; | |
/* | |
* GET user. | |
*/ | |
exports.user = function(id){ | |
if (null == id) throw new Error("id is required"); | |
return request("GET", "/api/user/" + id + ""); | |
}; | |
/* | |
* GET user repos. | |
*/ | |
exports.user.repos = function(id){ | |
if (null == id) throw new Error("id is required"); | |
return request("GET", "/api/user/" + id + "/repos"); | |
}; | |
/* | |
* DELETE users. | |
*/ | |
exports.users.del = function(){ | |
return request("DELETE", "/api/users"); | |
}; | |
/* | |
* DELETE user. | |
*/ | |
exports.user.del = function(id){ | |
if (null == id) throw new Error("id is required"); | |
return request("DELETE", "/api/user/" + id + ""); | |
}; | |
/* | |
* PUT user. | |
*/ | |
exports.user.put = function(id){ | |
if (null == id) throw new Error("id is required"); | |
return request("PUT", "/api/user/" + id + ""); | |
}; | |
}; |
This file contains hidden or 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
/*! | |
* express-rest | |
* Copyright(c) 2011 TJ Holowaychuk <[email protected]> | |
* MIT Licensed | |
*/ | |
/** | |
* Expose `middleware`. | |
*/ | |
exports = module.exports = middleware; | |
/** | |
* Library version. | |
*/ | |
exports.version = '0.0.1'; | |
/** | |
* Generate the rest client. | |
* | |
* @param {String} path | |
* @param {HTTPServer} app | |
* @return {String} | |
* @api public | |
*/ | |
function middleware(path, app) { | |
var js = exports.compile(path, app); | |
return function(req, res, next){ | |
if (0 != '/client.js'.indexOf(req.url)) return next(); | |
res.contentType('.js'); | |
res.send(js); | |
} | |
} | |
/** | |
* Compile routes for `app` into a rest client. | |
* | |
* @param {String} path | |
* @param {HTTPServer} app | |
* @return {String} | |
* @api private | |
*/ | |
exports.compile = function compile(path, app) { | |
var map = app.routes.routes | |
, keys = Object.keys(map) | |
, ret = {}; | |
// filter irrelevant paths | |
keys.forEach(function(method){ | |
var routes = map[method]; | |
ret[method] = routes.filter(function(route){ | |
return 0 == route.path.indexOf(path); | |
}); | |
}); | |
// generate route objects | |
keys.forEach(function(method){ | |
var routes = map[method]; | |
ret[method] = routes.map(function(route){ | |
var ret = {}; | |
ret.path = parsePath(route.path.replace(path, '')); | |
ret.prop = createProp(ret.path); | |
ret.args = createArgs(ret.path); | |
ret.method = method.toUpperCase(); | |
ret.toSource = function(){ return compileRoute(path, ret); }; | |
return ret; | |
}); | |
}); | |
// setup boilerplate | |
var buf = '\n// setup exports\n\n'; | |
for (var method in ret) { | |
ret[method].forEach(function(route){ | |
if (!route.prop) return; | |
var parts = route.prop.split('.') | |
, curr = ['exports']; | |
parts.forEach(function(part){ | |
var prop = curr.concat(part).join('.'); | |
if (!~buf.indexOf(prop)) { | |
buf += prop + ' = ' + prop + ' || {};\n'; | |
} | |
curr.push(part); | |
}); | |
}); | |
} | |
// methods | |
for (var method in ret) { | |
ret[method].forEach(function(route){ | |
if (!route.prop) return; | |
buf += '\n' + route.toSource(); | |
}); | |
} | |
// module boilerplate | |
return 'var client = function(exports, request){\n' | |
+ buf.replace(/^/gm, ' ') | |
+ '\n};'; | |
} | |
/** | |
* Parse `path` string, returning objects with a `type` | |
* and `string` representing the path segments. | |
* | |
* @param {String} path | |
* @return {Array} | |
* @api private | |
*/ | |
function parsePath(path) { | |
return path.split('/').slice(1).map(function(part){ | |
if (':' == part[0] && '?' == part[part.length - 1]) { | |
return { type: 'optional', string: part.slice(1, -1) }; | |
} else if (':' == part[0]) { | |
return { type: 'required', string: part.slice(1) }; | |
} else { | |
return { type: 'segment', string: part }; | |
} | |
}); | |
} | |
/** | |
* Create property name for `path`. | |
* | |
* @param {String} path | |
* @return {String} | |
* @api private | |
*/ | |
function createProp(path) { | |
return path.filter(function(part){ | |
return 'segment' == part.type; | |
}).map(function(part){ | |
return part.string; | |
}).join('.'); | |
} | |
/** | |
* Filter arguments from `path`. | |
* | |
* @param {Array} path | |
* @return {Array} | |
* @api private | |
*/ | |
function createArgs(path) { | |
return path.filter(function(part){ | |
return 'segment' != part.type; | |
}); | |
} | |
/** | |
* Compile `path` into a javascript string. | |
* | |
* @param {String} base | |
* @param {Array} path | |
* @return {String} | |
* @api private | |
*/ | |
function compilePath(base, path) { | |
return '"' + base + '/' + path.map(function(part){ | |
switch (part.type) { | |
case 'segment': | |
return part.string; | |
case 'required': | |
return '" + ' + part.string + ' + "'; | |
case 'optional': | |
return '" + (' + part.string + ' || "") + "'; | |
} | |
}).join('/') + '"'; | |
} | |
/** | |
* Compile validation for `args`. | |
* | |
* @param {Array} args | |
* @return {String} | |
* @api private | |
*/ | |
function compileArgValidation(args) { | |
return args.filter(function(arg){ | |
return 'required' == arg.type; | |
}).map(function(arg){ | |
return ' if (null == ' + arg.string + ') throw new Error("' + arg.string + ' is required");'; | |
}).join('\n'); | |
} | |
/** | |
* Compile `args`. | |
* | |
* @param {Array} args | |
* @return {String} | |
* @api private | |
*/ | |
function compileArgs(args) { | |
return args.map(function(arg){ | |
return arg.string; | |
}).join(', '); | |
} | |
/** | |
* Compile the given `route` into javascript. | |
* | |
* @param {String} path | |
* @param {Object} route | |
* @return {String} | |
* @api private | |
*/ | |
function compileRoute(path, route) { | |
var parts = route.prop.split('.') | |
, method = route.method.toLowerCase(); | |
if ('delete' == method) method = 'del'; | |
if ('get' != method) route.prop += '.' + method; | |
var js = ''; | |
js += '/*\n'; | |
js += ' * ' + route.method + ' ' + parts.join(' ') + '.\n'; | |
js += ' */\n'; | |
js += '\n'; | |
js += 'exports.'; | |
js += route.prop + ' = function(' + compileArgs(route.args) + '){\n'; | |
var args = compileArgValidation(route.args); | |
if (args.length) js += args + '\n'; | |
js += ' return request("' + route.method + '", ' + compilePath(path, route.path) + ');'; | |
js += '\n};\n'; | |
return js; | |
} |
This file contains hidden or 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
$(function(){ | |
client(window, function(method, url){ | |
return $.ajax({ url: url, type: method }); | |
}); | |
// or you could do: | |
// var api = {}; | |
// client(api, ...) | |
// api.users() etc | |
$('#user').click(function(){ | |
user(3).success(function(res){ | |
alert('got ' + JSON.stringify(res)); | |
}); | |
}); | |
$('#user-repos').click(function(){ | |
user.repos(3).success(function(res){ | |
alert('got ' + JSON.stringify(res)); | |
}); | |
}); | |
$('#users').click(function(){ | |
users().success(function(res){ | |
alert(res.map(function(user){ | |
return user.name; | |
}).join(', ')); | |
}); | |
}); | |
$('#delete-user').click(function(){ | |
user.del(3).success(function(res){ | |
alert(res.message); | |
}); | |
}); | |
$('#delete-users').click(function(){ | |
users.del().success(function(res){ | |
alert(res.message); | |
}); | |
}); | |
}); |
haha. was hacking together a little automated REST client prototype
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
what is this!