Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,67 @@ Resources have the concept of "auto-loading" associated data. For example we can

app.resource('users', actions, { format: 'json' });

## Middleware

This version supports attaching [route middleware](http://expressjs.com/2x/guide.html#route-middleware) to actions.
Every action can have its own different middleware attached (or no middleware at all).
The middleware can be specified in one of two ways: by passing a `middleware` option to `app.resource()` or by attaching it directly to the actions.

### middleware option

With the `middleware` option, it's possible to specify the middleware in a single place:

var express = require('express')
, Resource = require('express-resource')
, app = express.createServer();

var forumsMiddleware = {
index: authMiddleware,
new: [authMiddleware, adminMiddleware],
create: [authMiddleware, adminMiddleware],
show: authMiddleware,
edit: [authMiddleware, adminMiddleware],
update: [authMiddleware, adminMiddleware],
destroy: [authMiddleware, adminMiddleware]
};
app.resource('forums', require('./forum'), {middleware: forumsMiddleware});

It's also possible to specify a special "glob" key, that matches every action, and is considered when no specific middleware is specified:

var forumsMiddleware = {
'*': authMiddleware,
new: [authMiddleware, adminMiddleware],
create: [authMiddleware, adminMiddleware],
edit: [authMiddleware, adminMiddleware],
update: [authMiddleware, adminMiddleware],
destroy: [authMiddleware, adminMiddleware]
};
app.resource('forums', require('./forum'), {middleware: forumsMiddleware});

### inline middleware

It's also possible to specify the route middleware directly with the actions. There are two styles, with a JavaScript object or with an array:

exports.index = {
middleware: authMiddleware,
fn: function(req, res){
res.send('forum index');
}
};

exports.new = [ authMiddleware, adminMiddleware, function(req, res){
res.send('new forum');
} ];

exports.create = {
middleware: [ authMiddleware, adminMiddleware ],
fn: function(req, res){
res.send('create forum');
}
};

...

## Running Tests

First make sure you have the submodules:
Expand Down
77 changes: 63 additions & 14 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,22 @@ var orderedActions = [
,'update' // PUT /:id
,'destroy' // DEL /:id
];


var defaultMiddleware = {
'index': null
,'new': null
,'create': null
,'show': null
,'edit': null
,'update': null
,'destroy': null
,'*': null
};

function isArray(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
}

/**
* Initialize a new `Resource` with the given `name` and `actions`.
*
Expand All @@ -37,9 +52,11 @@ var orderedActions = [
* @api private
*/

var Resource = module.exports = function Resource(name, actions, app) {
var Resource = module.exports = function Resource(name, actions, app, opts) {
this.name = name;
this.app = app;
this.options = opts || {};
this.middleware = this.options.middleware || defaultMiddleware;
this.routes = {};
actions = actions || {};
this.base = actions.base || '/';
Expand Down Expand Up @@ -115,11 +132,35 @@ Resource.prototype.__defineGetter__('defaultId', function(){
* @api public
*/

Resource.prototype.map = function(method, path, fn){
Resource.prototype.map = function(method, path, fnmap){
var self = this
, orig = path;

if (method instanceof Resource) return this.add(method);

var middleware, fn;
if ('function' == typeof fnmap) {
middleware = [];
fn = fnmap;
} else if (isArray(fnmap) && (fnmap.length > 1) && ('0' in Object(fnmap))) {
middleware = fnmap.slice(0, fnmap.length-1);
fn = fnmap[fnmap.length-1];
} else if (('object' == typeof fnmap) && fnmap.hasOwnProperty('fn')) {
middleware = fnmap.middleware
fn = fnmap.fn;
} else {
middleware = [];
fn = fnmap;
}

if (isArray(fn) && (fn.length > 1) && ('0' in Object(fn)) && (middleware.length == 0)) {
middleware = middleware.concat(fn.slice(0, fn.length-1));
fn = fn[fn.length-1];
} else if (('object' == typeof fn) && fn.hasOwnProperty('fn') && fn.hasOwnProperty('middleware')) {
middleware = middleware.concat(fn.middleware);
fn = fn.fn;
}

if ('function' == typeof path) fn = path, path = '';
if ('object' == typeof path) fn = path, path = '';
if ('/' == path[0]) path = path.substr(1);
Expand All @@ -132,16 +173,21 @@ Resource.prototype.map = function(method, path, fn){
route += path;
route += '.:format?';

if (middleware === undefined)
middleware = [];

// register the route so we may later remove it
(this.routes[method] = this.routes[method] || {})[route] = {
method: method
, path: route
, orig: orig
, middleware: middleware
, fn: fn
, fnmap: fnmap
};

// apply the route
this.app[method](route, function(req, res, next){
this.app[method](route, middleware, function(req, res, next){
req.format = req.params.format || req.format || self.format;
if (req.format) res.contentType(req.format);
if ('object' == typeof fn) {
Expand Down Expand Up @@ -186,7 +232,7 @@ Resource.prototype.add = function(resource){
route = routes[key];
delete routes[key];
app[method](key).remove();
resource.map(route.method, route.orig, route.fn);
resource.map(route.method, route.orig, route.fnmap);
}
}

Expand All @@ -202,27 +248,29 @@ Resource.prototype.add = function(resource){
*/

Resource.prototype.mapDefaultAction = function(key, fn){
var middleware = this.middleware[key] || this.middleware['*'] || [];
var fnmap = {'fn': fn, 'middleware': middleware};
switch (key) {
case 'index':
this.get('/', fn);
this.get('/', fnmap);
break;
case 'new':
this.get('/new', fn);
this.get('/new', fnmap);
break;
case 'create':
this.post('/', fn);
this.post('/', fnmap);
break;
case 'show':
this.get(fn);
this.get(fnmap);
break;
case 'edit':
this.get('edit', fn);
this.get('edit', fnmap);
break;
case 'update':
this.put(fn);
this.put(fnmap);
break;
case 'destroy':
this.del(fn);
this.del(fnmap);
break;
}
};
Expand All @@ -235,6 +283,7 @@ express.router.methods.concat(['del', 'all']).forEach(function(method){
Resource.prototype[method] = function(path, fn){
if ('function' == typeof path
|| 'object' == typeof path) fn = path, path = '';
var middleware = this.middleware[method] || [];
this.map(method, path, fn);
return this;
}
Expand All @@ -255,8 +304,8 @@ express.HTTPSServer.prototype.resource = function(name, actions, opts){
if ('object' == typeof name) actions = name, name = null;
if (options.id) actions.id = options.id;
this.resources = this.resources || {};
if (!actions) return this.resources[name] || new Resource(name, null, this);
if (!actions) return this.resources[name] || new Resource(name, null, this, opts);
for (var key in opts) options[key] = opts[key];
var res = this.resources[name] = new Resource(name, actions, this);
var res = this.resources[name] = new Resource(name, actions, this, opts);
return res;
};
Loading