diff --git a/index.js b/index.js index e5fd172..a79cea6 100644 --- a/index.js +++ b/index.js @@ -149,8 +149,7 @@ Resource.prototype.map = function(method, path, fn){ , fn: fn }; - // apply the route - this.app[method](route, function(req, res, next){ + this._applyRoute(method, route, function(req, res, next){ req.format = req.params.format || req.format || self.format; if (req.format) res.type(req.format); if ('object' == typeof fn) { @@ -167,6 +166,10 @@ Resource.prototype.map = function(method, path, fn){ return this; }; +Resource.prototype._applyRoute = function(method, route, fn){ + this.app[method](route, fn); +}; + /** * Nest the given `resource`. * @@ -177,18 +180,24 @@ Resource.prototype.map = function(method, path, fn){ */ Resource.prototype.add = function(resource){ + var relativeBase = this.base + + (this.name ? this.name + '/': '') + + this.param + '/'; + + resource._updatePathBase(relativeBase) + + return this; +}; + +Resource.prototype._updatePathBase = function(newBase){ var app = this.app , routes , route; - // relative base - resource.base = this.base - + (this.name ? this.name + '/': '') - + this.param + '/'; - + this.base = newBase; // re-define previous actions - for (var method in resource.routes) { - routes = resource.routes[method]; + for (var method in this.routes) { + routes = this.routes[method]; for (var key in routes) { route = routes[key]; delete routes[key]; @@ -198,11 +207,9 @@ Resource.prototype.add = function(resource){ app.routes[method].splice(i, 1); } }) - resource.map(route.method, route.orig, route.fn); + this.map(route.method, route.orig, route.fn); } } - - return this; }; /** @@ -263,11 +270,15 @@ methods.concat(['del', 'all']).forEach(function(method){ app.resource = function(name, actions, opts){ var options = actions || {}; + var Resource = (opts || {}).resourceConstructor || app.resource.constructor 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); for (var key in opts) options[key] = opts[key]; + delete options.resourceConstructor var res = this.resources[name] = new Resource(name, actions, this); return res; }; + +app.resource.constructor = Resource; diff --git a/test/resource.test.js b/test/resource.test.js index 4693f62..dacd49f 100644 --- a/test/resource.test.js +++ b/test/resource.test.js @@ -33,164 +33,164 @@ describe('app.resource()', function(){ request(app) .post('/forums') .expect('create forum', next()); - + request(app) .get('/forums/5') .expect('show forum 5', next()); - + request(app) .get('/forums/5/edit') .expect('edit forum 5', next()); - + request(app) .del('/forums/5') .expect('destroy forum 5', next()); }) it('should support root resources', function(done){ - var app = express(); - var next = batch(done); - var forum = app.resource(require('./fixtures/forum')); - var thread = app.resource('threads', require('./fixtures/thread')); - forum.map(thread); - - request(app) - .get('/') - .expect('forum index', next()); - - // request(app) - // .get('/12') - // .expect('show forum 12', next()); - - // request(app) - // .get('/12/threads') - // .expect('thread index of forum 12', next()); - - // request(app) - // .get('/1/threads/50') - // .expect('show thread 50 of forum 1', next()); - }) - - describe('"id" option', function(){ - it('should allow overriding the default', function(done){ - var app = express(); - var next = batch(done); - - app.resource('users', { - id: 'uid', - show: function(req, res){ - res.send(req.params.uid); - } - }); - - request(app) - .get('/users') - .expect(404, next()); - - request(app) - .get('/users/10') - .expect('10', next()); - }) - }) - - describe('with several segments', function(){ - it('should work', function(done){ - var app = express(); - var next = batch(done); - var cat = app.resource('api/cat', require('./fixtures/cat')); - - request(app) - .get('/api/cat') - .expect('list of cats', next()); - - request(app) - .get('/api/cat/new') - .expect('new cat', next()); - }) - }) - - it('should allow configuring routes', function(done){ - var app = express(); - var next = batch(done); - var Forum = require('./fixtures/forum').Forum; - - function load(id, fn) { fn(null, "User"); } - - var actions = { - login: function(req, res){ - res.end('login'); - }, - logout: function(req, res){ - res.end('logout'); - } - }; - - var users = app.resource('users', actions, { load: load }); - users.map('get', 'login', actions.login); - users.map('get', '/logout', actions.logout); - - request(app) - .get('/users/1/login') - .expect('login', next()); - - request(app) - .get('/users/logout') - .expect('logout', next()); - }) - - describe('autoloading', function(){ - describe('when no resource is found', function(){ - it('should not invoke the callback', function(done){ - var app = express(); - - function load(id, fn) { fn(); } - var actions = { show: function(){ - assert.fail('called show when loader failed'); - }}; - - app.resource('pets', actions, { load: load }); - - request(app) - .get('/pets/0') - .expect(404, done); - }) - }) - - describe('when a resource is found', function(){ - it('should invoke the callback', function(done){ - var app = express(); - var Forum = require('./fixtures/forum').Forum; - - var actions = { show: function(req, res){ - res.end(req.forum.title); - }}; - - var forum = app.resource('forum', actions); - forum.load(Forum.get); - - request(app) - .get('/forum/12') - .expect('Ferrets', done); - }) - - it('should work recursively', function(done){ + var app = express(); + var next = batch(done); + var forum = app.resource(require('./fixtures/forum')); + var thread = app.resource('threads', require('./fixtures/thread')); + forum.map(thread); + + request(app) + .get('/') + .expect('forum index', next()); + + // request(app) + // .get('/12') + // .expect('show forum 12', next()); + + // request(app) + // .get('/12/threads') + // .expect('thread index of forum 12', next()); + + // request(app) + // .get('/1/threads/50') + // .expect('show thread 50 of forum 1', next()); + }) + + describe('"id" option', function(){ + it('should allow overriding the default', function(done){ + var app = express(); + var next = batch(done); + + app.resource('users', { + id: 'uid', + show: function(req, res){ + res.send(req.params.uid); + } + }); + + request(app) + .get('/users') + .expect(404, next()); + + request(app) + .get('/users/10') + .expect('10', next()); + }) + }) + + describe('with several segments', function(){ + it('should work', function(done){ + var app = express(); + var next = batch(done); + var cat = app.resource('api/cat', require('./fixtures/cat')); + + request(app) + .get('/api/cat') + .expect('list of cats', next()); + + request(app) + .get('/api/cat/new') + .expect('new cat', next()); + }) + }) + + it('should allow configuring routes', function(done){ + var app = express(); + var next = batch(done); + var Forum = require('./fixtures/forum').Forum; + + function load(id, fn) { fn(null, "User"); } + + var actions = { + login: function(req, res){ + res.end('login'); + }, + logout: function(req, res){ + res.end('logout'); + } + }; + + var users = app.resource('users', actions, { load: load }); + users.map('get', 'login', actions.login); + users.map('get', '/logout', actions.logout); + + request(app) + .get('/users/1/login') + .expect('login', next()); + + request(app) + .get('/users/logout') + .expect('logout', next()); + }) + + describe('autoloading', function(){ + describe('when no resource is found', function(){ + it('should not invoke the callback', function(done){ var app = express(); - var Forum = require('./fixtures/forum').Forum; - var Thread = require('./fixtures/thread').Thread; - - var actions = { show: function(req, res){ - res.end(req.forum.title + ': ' + req.thread.title); + + function load(id, fn) { fn(); } + var actions = { show: function(){ + assert.fail('called show when loader failed'); }}; - - var forum = app.resource('forum', { load: Forum.get }); - var threads = app.resource('thread', actions, { load: Thread.get }); - - forum.add(threads); - + + app.resource('pets', actions, { load: load }); + request(app) - .get('/forum/12/thread/1') - .expect('Ferrets: Tobi rules', done); - }) - }) - }) + .get('/pets/0') + .expect(404, done); + }) + }) + + describe('when a resource is found', function(){ + it('should invoke the callback', function(done){ + var app = express(); + var Forum = require('./fixtures/forum').Forum; + + var actions = { show: function(req, res){ + res.end(req.forum.title); + }}; + + var forum = app.resource('forum', actions); + forum.load(Forum.get); + + request(app) + .get('/forum/12') + .expect('Ferrets', done); + }) + + it('should work recursively', function(done){ + var app = express(); + var Forum = require('./fixtures/forum').Forum; + var Thread = require('./fixtures/thread').Thread; + + var actions = { show: function(req, res){ + res.end(req.forum.title + ': ' + req.thread.title); + }}; + + var forum = app.resource('forum', { load: Forum.get }); + var threads = app.resource('thread', actions, { load: Thread.get }); + + forum.add(threads); + + request(app) + .get('/forum/12/thread/1') + .expect('Ferrets: Tobi rules', done); + }) + }) + }) }) \ No newline at end of file