Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Resourceful path construction, per issue #20 #29

Open
wants to merge 8 commits into from

4 participants

This page is out of date. Refresh to see the latest.
Showing with 204 additions and 2 deletions.
  1. +71 −2 index.js
  2. +133 −0 test/resource.path.test.js
View
73 index.js
@@ -118,6 +118,9 @@ Resource.prototype.map = function(method, path, fn){
route += path;
route += '.:format?';
+ // add convenience functions
+ this.createRouteHelper(path, route);
+
// register the route so we may later remove it
(this.routes[method] = this.routes[method] || {})[route] = {
method: method
@@ -125,7 +128,7 @@ Resource.prototype.map = function(method, path, fn){
, orig: orig
, fn: fn
};
-
+
// apply the route
this.app[method](route, function(req, res, next){
req.format = req.params.format || self.format;
@@ -142,7 +145,7 @@ Resource.prototype.map = function(method, path, fn){
fn(req, res, next);
}
});
-
+
return this;
};
@@ -173,6 +176,11 @@ Resource.prototype.add = function(resource){
resource.map(route.method, route.orig, route.fn);
}
}
+
+ // delete previous route helpers
+ for(var methodName in resource.paths) {
+ delete resourceAccess.path[methodName];
+ }
return this;
};
@@ -212,6 +220,66 @@ Resource.prototype.mapDefaultAction = function(key, fn){
};
/**
+ * Create URL path generators.
+ *
+ * @param {String} orig
+ * @api private
+ */
+
+Resource.prototype.createRouteHelper = function(mapPath, route) {
+ resourceAccess.path = resourceAccess.path || {};
+ resourceAccess.path.idField = resourceAccess.path.idField || 'id';
+
+ var methodName = this.name || "roots";
+ var argCount = 0;
+
+ route = route.replace(/\.:format\?$/, '');
+ route = route.replace(/\/null/, ''); // Happens with a nameless top level resource
+
+ // Check to see how many levels deep we are and clean up the route
+ if(this.base.length > 1) {
+ // build up in reverse
+ var params = this.base.match(/:(\w+)/g);
+ params.reverse().forEach(function(param) {
+ if(param === ':id') { param = ':root'; }
+ methodName = param.slice(1) + '_' + methodName;
+ argCount += 1;
+ });
+ }
+
+ if(mapPath === 'new') {
+ methodName = 'new_' + en.singularize(methodName);
+ } else if(mapPath.indexOf(this.param) !== (-1)) {
+ var actionName = mapPath.slice(this.param.length);
+ if (actionName[0] == '/') { actionName = actionName.slice(1); }
+
+ methodName = en.singularize(methodName);
+ if(actionName.length > 0) {
+ methodName = actionName + '_' + methodName;
+ }
+ argCount++;
+ } else if (mapPath.length > 0) {
+ // custom collection action
+ methodName = mapPath + '_' + methodName;
+ }
+
+ // Add paths to this resource so we can remove them later if we become nested (see #add)
+ this.paths = this.paths || [];
+ this.paths.push(methodName);
+
+ // Add to the app.resource object
+ resourceAccess.path[methodName + '_path'] = resourceAccess.path[methodName] || function() {
+ var localRoute = route;
+ Array.prototype.forEach.call(arguments, function(arg) {
+ var id = arg[resourceAccess.path.idField] || arg;
+ localRoute = localRoute.replace(/:\w+/, id);
+ });
+ return localRoute;
+ };
+}
+
+
+/**
* Setup http verb methods.
*/
@@ -233,6 +301,7 @@ express.router.methods.concat(['del', 'all']).forEach(function(method){
* @api public
*/
+var resourceAccess =
express.HTTPServer.prototype.resource =
express.HTTPSServer.prototype.resource = function(name, actions, opts){
var options = actions || {};
View
133 test/resource.path.test.js
@@ -0,0 +1,133 @@
+
+/**
+ * Module dependencies.
+ */
+var assert = require('assert')
+ , express = require('express')
+ , should = require('should')
+ , Resource = require('../');
+
+module.exports = {
+ 'test resource': function(){
+ var app = express.createServer();
+ var ret = app.resource('forums', require('./fixtures/forum'));
+
+ assert.strictEqual(app.resource.path.forums_path(), '/forums');
+ assert.strictEqual(app.resource.path.new_forum_path(), '/forums/new');
+ assert.strictEqual(app.resource.path.forum_path({id: 5}), '/forums/5');
+
+ assert.strictEqual(app.resource.path.forum_path({id: 10}), '/forums/10');
+
+ assert.strictEqual(app.resource.path.edit_forum_path({id: 5}), '/forums/5/edit');
+ },
+ 'test shallow nesting': function(){
+ var app = express.createServer();
+
+ var forumObj = {id: 5};
+ var threadObj = {id: 50};
+
+ var forum = app.resource('forums', require('./fixtures/forum'));
+ var thread = app.resource('threads', require('./fixtures/thread'));
+ forum.map(thread);
+
+ assert.strictEqual(app.resource.path.forums_path(), '/forums');
+ assert.strictEqual(app.resource.path.new_forum_path(), '/forums/new');
+ assert.strictEqual(app.resource.path.forum_path(forumObj), '/forums/5');
+ assert.strictEqual(app.resource.path.edit_forum_path(forumObj), '/forums/5/edit');
+
+ assert.strictEqual(app.resource.path.forum_threads_path(forumObj), '/forums/5/threads');
+ assert.strictEqual(app.resource.path.new_forum_thread_path(forumObj), '/forums/5/threads/new');
+ assert.strictEqual(app.resource.path.forum_thread_path(forumObj, threadObj), '/forums/5/threads/50');
+ assert.strictEqual(app.resource.path.edit_forum_thread_path(forumObj, threadObj), '/forums/5/threads/50/edit');
+ },
+ 'test top level resource nesting': function(){
+ var app = express.createServer();
+
+ var forumObj = {id: 5};
+ var threadObj = {id: 50};
+
+ var forum = app.resource(require('./fixtures/forum'));
+ var thread = app.resource('threads', require('./fixtures/thread'));
+ forum.map(thread);
+
+ assert.strictEqual(app.resource.path.roots_path(), '/');
+ assert.strictEqual(app.resource.path.new_root_path(), '/new');
+ assert.strictEqual(app.resource.path.root_path(forumObj), '/5');
+ assert.strictEqual(app.resource.path.edit_root_path(forumObj), '/5/edit');
+
+ assert.strictEqual(app.resource.path.root_threads_path(forumObj), '/5/threads');
+ assert.strictEqual(app.resource.path.new_root_thread_path(forumObj), '/5/threads/new');
+ assert.strictEqual(app.resource.path.root_thread_path(forumObj, threadObj), '/5/threads/50');
+ assert.strictEqual(app.resource.path.edit_root_thread_path(forumObj, threadObj), '/5/threads/50/edit');
+ },
+ 'test deep resource nesting': function(){
+ var app = express.createServer();
+
+ var userObj = {id: 1};
+ var forumObj = {id: 5};
+ var threadObj = {id: 50};
+
+ var user = app.resource('users', { index: function(req, res){ res.end('users'); } });
+ var forum = app.resource('forums', require('./fixtures/forum'));
+ var thread = app.resource('threads', require('./fixtures/thread'));
+
+ var ret = user.add(forum);
+ ret.should.equal(user);
+
+ var ret = forum.add(thread);
+ ret.should.equal(forum);
+
+ assert.strictEqual(app.resource.path.users_path(), '/users');
+
+ assert.strictEqual(app.resource.path.user_forums_path(userObj), '/users/1/forums');
+ assert.strictEqual(app.resource.path.new_user_forum_path(userObj), '/users/1/forums/new');
+ assert.strictEqual(app.resource.path.user_forum_path(userObj, forumObj), '/users/1/forums/5');
+ assert.strictEqual(app.resource.path.edit_user_forum_path(userObj, forumObj), '/users/1/forums/5/edit');
+
+ assert.strictEqual(app.resource.path.user_forum_threads_path(userObj, forumObj), '/users/1/forums/5/threads');
+ assert.strictEqual(app.resource.path.new_user_forum_thread_path(userObj, forumObj), '/users/1/forums/5/threads/new');
+ assert.strictEqual(app.resource.path.user_forum_thread_path(userObj, forumObj, threadObj), '/users/1/forums/5/threads/50');
+ assert.strictEqual(app.resource.path.edit_user_forum_thread_path(userObj, forumObj, threadObj), '/users/1/forums/5/threads/50/edit');
+ },
+ 'test resource with custom actions': function(){
+ var app = express.createServer();
+ var ret = app.resource('forums', require('./fixtures/forum'));
+
+ var actions = {
+ lock: function(req, res){
+ res.end('login');
+ },
+ design: function(req, res){
+ res.end('logout');
+ }
+ };
+
+ ret.map('get', 'lock', actions.lock);
+ ret.map('get', '/design', actions.design);
+
+ assert.strictEqual(app.resource.path.design_forums_path(), '/forums/design');
+ assert.strictEqual(app.resource.path.lock_forum_path({id: 5}), '/forums/5/lock');
+ },
+ 'test resource with direct id value': function(){
+ var app = express.createServer();
+ var ret = app.resource('forums', require('./fixtures/forum'));
+
+ assert.strictEqual(app.resource.path.forum_path(5), '/forums/5');
+ assert.strictEqual(app.resource.path.edit_forum_path(5), '/forums/5/edit');
+
+ assert.strictEqual(app.resource.path.forum_path("5"), '/forums/5');
+ assert.strictEqual(app.resource.path.edit_forum_path("5"), '/forums/5/edit');
+ },
+ 'test resource with custom id field': function(){
+ var app = express.createServer();
+ var ret = app.resource('forums', require('./fixtures/forum'));
+
+ // NOTE: because this is set across all resources, this test should be run last or it will need
+ // to be reset for each test. If this is too confusing, we could change to set in on a per-resource basis.
+ app.resource.path.idField = '_id';
+
+ assert.strictEqual(app.resource.path.forum_path({_id: 5}), '/forums/5');
+ assert.strictEqual(app.resource.path.edit_forum_path({_id: 5}), '/forums/5/edit');
+ },
+
+};
Something went wrong with that request. Please try again.