Skip to content

Instantly share code, notes, and snippets.

@aviflax
Created August 26, 2011 01:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aviflax/1172499 to your computer and use it in GitHub Desktop.
Save aviflax/1172499 to your computer and use it in GitHub Desktop.
resource.js for Express
"use strict";
var Resource = require('../lib/resource');
var ResourceError = require('../lib/resourceerror');
var Goal = require('../models/Goal');
var GoalResource = new Resource();
GoalResource.get = function(req, res) {
var self = this;
Goal.get(req.params.id, function(err, goal) {
if (err)
return self.err(500, err, res);
res.send(JSON.stringify(goal), {'Content-Type': 'application/json'});
});
}
GoalResource.put = function(req, res) {
var self = this;
Goal.get(req.params.id, function(err, goal) {
if (err)
return self.err(500, err, res);
goal.update(req.body, function(err) {
if (err)
return self.err(409, err, res);
res.send(JSON.stringify(goal), {'Content-Type': 'application/json'});
});
});
}
module.exports = GoalResource;
"use strict";
var async = require('async');
var Resource = require('../lib/resource');
var Goal = require('../models/Goal');
var AllGoalsResource = new Resource();
AllGoalsResource.representations = {}
AllGoalsResource.representations.json = function(req, res) {
getGoals(function(err, goals) {
if (err)
return AllGoalsResource.err(500, err, res);
res.send(JSON.stringify(goals), {'Content-Type': 'application/json'});
});
}
AllGoalsResource.representations.html = function(req, res) {
getGoals(function(err, goals) {
if (err)
return AllGoalsResource.err(500, err, res);
renderHTML(goals, req.linkPrefix, function(html) {
res.send(html, {'Content-Type': 'text/html'});
});
});
}
function getGoals(cb) {
async.parallel([
function(cb) {
Goal.list('main', 'goals_by_group', {key:'now'}, function(err, goals) {
if (err)
cb(err);
else
cb(null, {'now': goals.toArray()});
});
},
function(cb) {
Goal.list('main', 'goals_by_group', {key:'next'}, function(err, goals) {
if (err)
cb(err);
else
cb(null, {'next': goals.toArray()});
});
},
function(cb) {
Goal.list('main', 'goals_by_group', {key:'later'}, function(err, goals) {
if (err)
cb(err);
else
cb(null, {'later': goals.toArray()});
});
},
], function(err, results) {
var result = {};
results.forEach(function(object) {
for (var propname in object) { result[propname] = object[propname]; }
});
cb(err, results);
});
}
function renderHTML(goals, linkPrefix, cb) {
var fs = require('fs'),
jsdom = require('jsdom'),
weld = require('weld');
jsdom.env('html/goals.html', function (err, window) {
weld.weld(window.document.getElementById('goals-now'), goals.now);
cb(doc.innerHTML);
});
}
module.exports = AllGoalsResource;
"use strict";
var ResourceError = require('./resourceerror');
function Resource() {
if ( !(this instanceof arguments.callee) ) {
throw new Error("Constructor called as a function.");
}
}
Resource.prototype.attach = function(app, path) {
// TODO: consider supporting an array of paths
// for some bizarre reason, Express doesn't support HEAD
// see: https://github.com/visionmedia/express/issues/37
var allMethods = ['del','get','options','patch','post','put'];
var supportedMethods = [];
var self = this;
allMethods.forEach(function(method) {
if (method in self && typeof(self[method]) == 'function') {
var handler = self[method];
handler = handler.bind(self);
handler = wrapErrorHandler(handler);
app[method](path, handler);
supportedMethods.push(method == 'del' ? 'DELETE' : method);
} else if (method != 'options') {
app[method](path, methodNotAllowed);
}
});
if (!('options' in self)) {
app.options(path, function(req, res) {
var methodList = supportedMethods.toString().toUpperCase();
res.send(204, {'Allow': methodList});
});
}
}
function methodNotAllowed(req, res) {
res.send('405 Method Not Allowed', { 'Content-Type': 'text/plain' }, 405);
}
function wrapErrorHandler(handler) {
/* This is really of limited value, because errors will only be caught by the try/catch
if the resource throws them during its own execution, and not in an async callback.
*/
return function(req, res) {
try {
handler(req, res);
} catch (e) {
var code = e instanceof ResourceError ? e.code : 500;
res.send(e.message, {'Content-Type': 'text/plain'}, code);
}
}
}
Resource.prototype.representations = null;
Resource.prototype.get = function(req, res) {
var reps = this.representations;
if (!reps || (typeof(reps) === 'object' && Object.keys(reps).length === 0))
return methodNotAllowed(req, res);
for (var type in reps) {
if (req.accepts(type))
return reps[type].call(this, req, res);
}
res.send('406 Not Acceptable', { 'Content-Type': 'text/plain' }, 406);
}
Resource.prototype.err = function(code, error, res) {
// this is an annoying and inelegant helper function for dealing with errors
res.send(error, { 'Content-Type': 'text/plain' }, code);
}
module.exports = Resource;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment