diff --git a/index.js b/index.js index 7789c21..fb4f1c1 100644 --- a/index.js +++ b/index.js @@ -61,7 +61,9 @@ function Resource(name, actions, app) { // default actions for (var i = 0, key; i < orderedActions.length; ++i) { key = orderedActions[i]; - if (actions[key]) this.mapDefaultAction(key, actions[key]); + var handler = actions[key]; + if (typeof handler === 'function') handler = handler.bind(actions); + if (handler) this.mapDefaultAction(key, handler); } // auto-loader @@ -187,6 +189,8 @@ Resource.prototype.add = function(resource){ + (this.name ? this.name + '/': '') + this.param + '/'; + resource.parent = this; + // re-define previous actions for (var method in resource.routes) { routes = resource.routes[method]; @@ -243,6 +247,60 @@ Resource.prototype.mapDefaultAction = function(key, fn){ } }; +/** + * Get edit path for resource. Ex '/users/42/edit'. + * + * @param {String} ids Array of ids for parents if resource is nested + * @return {String} Url of resource "root" + */ + +Resource.prototype.editPath = function() { + var ids = Array.prototype.slice.call(arguments); + var id = ids.pop(); + var parentPath = this.parent ? this.parent.recordPath.apply(this.parent, ids) : ''; + return parentPath + '/' + this.name + '/' + id + '/edit'; +} + +/** + * Get path for creating a new resource. Ex '/users/new'. + * + * @param {Array[String]} ids Array of ids for parents if resource is nested + * @return {String} Url of path to form for creating new resources + */ + +Resource.prototype.newPath = function() { + var ids = Array.prototype.slice.call(arguments); + var parentPath = this.parent ? this.parent.recordPath.apply(this.parent, ids) : ''; + return parentPath + '/' + this.name + '/new'; +} + +/** + * Get "collection" url for resource. Ex '/users'. + * + * @param {Array[String]} ids Array of ids for parents if resource is nested + * @return {String} Url of resource "root" + */ + +Resource.prototype.collectionPath = function() { + var ids = Array.prototype.slice.call(arguments); + var parentPath = this.parent ? this.parent.recordPath.apply(this.parent, ids) : ''; + return parentPath + '/' + this.name; +} + +/** + * Get "collection" url for resource. Ex '/users/42'. + * + * @param {Array[String]} ids Array of ids for parents if resource is nested + * @return {String} Url of record resource including id + */ + +Resource.prototype.recordPath = function() { + var ids = Array.prototype.slice.call(arguments); + var id = ids.pop(); + var parentPath = this.parent ? this.parent.recordPath.apply(this.parent, ids) : ''; + return parentPath + '/' + this.name + '/' + id; +} + /** * Setup http verb methods. */ diff --git a/test/resource.test.js b/test/resource.test.js index ee22990..5725a31 100644 --- a/test/resource.test.js +++ b/test/resource.test.js @@ -1,6 +1,7 @@ var assert = require('assert') , express = require('express') + , should = require('should') , Resource = require('..') , request = require('supertest') , batch = require('./support/batch'); @@ -55,6 +56,21 @@ describe('app.resource()', function(){ .expect('destroy forum 5', next()); }) + it('should bind this correctly', function(done) { + var app = express(); + var testController = { + 'users': 'userA userB', + 'index': function(req, res) { + res.send(this.users); + } + }; + + app.resource('users', testController); + request(app) + .get('/users') + .expect('userA userB', done); + }); + it('should support root resources', function(done){ var app = express(); var next = batch(done); @@ -127,6 +143,57 @@ describe('app.resource()', function(){ .expect('destroy thread 50 of forum 12', next()) }) + describe('url generation for non-nested resources', function() { + var app; + beforeEach(function() { + app = express(); + app.resource('users'); + }); + it('should return new urls', function() { + assert.equal(app.resource('users').newPath(), '/users/new'); + }); + + it('should return edit urls', function() { + assert.equal(app.resource('users').editPath(42), '/users/42/edit'); + }); + + it('should return collection urls', function() { + assert.equal(app.resource('users').collectionPath(), '/users'); + }); + + it('should return record urls with ids', function() { + assert.equal(app.resource('users').recordPath(42), '/users/42'); + }); + }); + + describe('url generation for nested resources', function() { + var app; + var dummyController = {show: function(req, res) { res.send('hello world'); }}; + beforeEach(function() { + app = express(); + var forums = app.resource('forums', dummyController); + var threads = app.resource('threads', dummyController); + var replies = app.resource('replies', dummyController); + forums.add(threads); + threads.add(replies); + }); + it('should return new urls', function() { + assert.equal(app.resource('replies').newPath(1, 2), '/forums/1/threads/2/replies/new'); + }); + + it('should return edit urls', function() { + assert.equal(app.resource('replies').editPath(1, 2, 3), '/forums/1/threads/2/replies/3/edit'); + }); + + it('should return collection urls', function() { + assert.equal(app.resource('replies').collectionPath(1, 2), '/forums/1/threads/2/replies'); + }); + + it('should return record urls with ids', function() { + assert.equal(app.resource('replies').recordPath(1, 2, 3), '/forums/1/threads/2/replies/3'); + }); + }); + describe('"id" option', function(){ it('should allow overriding the default', function(done){ var app = express();