diff --git a/lib/router.js b/lib/router.js index a60b408..961e220 100644 --- a/lib/router.js +++ b/lib/router.js @@ -266,7 +266,7 @@ Router.prototype.resource = function(name, options, fn) { } options = options || {}; - var actions = [ 'new', 'create', 'show', 'edit', 'update', 'destroy' ] + var actions = [ 'show', 'new', 'create', 'edit', 'update', 'destroy' ] , ns = this._ns[this._ns.length - 1] , path = ns.qpath(name) , controller = ns.qcontroller(name) @@ -281,20 +281,32 @@ Router.prototype.resource = function(name, options, fn) { }); } - var self = this; + var self = this + , showActionIndex = actions.indexOf('show') + , getResourceHelper = utils.once(function () { + return helper; + }, null); + + // Moving `show` action to the beginning of the actions list, in front of `create`, `update` and `destroy`. + // It is important to generate it's helper first, because it's route has optional "format" parameter. + if (showActionIndex > 0) { + actions.splice(showActionIndex, 1); + actions.unshift('show'); + } + actions.forEach(function(action) { switch (action) { case 'new': self._route('get' , path + '/new.:format?' , controller, 'new' , utils.functionize('new', helper)); break; - case 'create': self._route('post', path , controller, 'create' ); + case 'create': self._route('post', path , controller, 'create' , getResourceHelper()); break; - case 'show': self._route('get' , path + '.:format?' , controller, 'show' , helper); + case 'show': self._route('get' , path + '.:format?' , controller, 'show' , getResourceHelper()); break; case 'edit': self._route('get' , path + '/edit.:format?', controller, 'edit' , utils.functionize('edit', helper)); break; - case 'update': self._route('put' , path , controller, 'update' ); + case 'update': self._route('put' , path , controller, 'update' , getResourceHelper()); break; - case 'destroy': self._route('del' , path , controller, 'destroy'); + case 'destroy': self._route('del' , path , controller, 'destroy' , getResourceHelper()); break; } }); @@ -355,7 +367,7 @@ Router.prototype.resources = function(name, options, fn) { } options = options || {}; - var actions = [ 'index', 'new', 'create', 'show', 'edit', 'update', 'destroy' ] + var actions = [ 'index', 'show', 'new', 'create', 'edit', 'update', 'destroy' ] , ns = this._ns[this._ns.length - 1] , singular = inflect.singularize(name) , path = ns.qpath(name) @@ -374,22 +386,41 @@ Router.prototype.resources = function(name, options, fn) { }); } - var self = this; + var self = this + , getResourceHelper = utils.once(function () { + return helper; + }, null) + , getCollectionHelper = utils.once(function () { + return collectionHelper; + }, null); + + // Moving 'index' and 'show' actions to the beginning of the actions list, in front of other actions. + // It is important to generate helpers for them first, because they have optional "format" parameters. + actions = actions.sort(function (a1, a2) { + if (a1 === 'index' || a1 === 'show') { + return -1; + } else if (a2 === 'index' || a2 === 'show') { + return 1; + } else { + return 0; + } + }); + actions.forEach(function(action) { switch (action) { - case 'index': self._route('get' , path + '.:format?' , controller, 'index' , collectionHelper); + case 'index': self._route('get' , path + '.:format?' , controller, 'index' , getCollectionHelper()); break; case 'new': self._route('get' , path + '/new.:format?' , controller, 'new' , utils.functionize('new', helper)); break; - case 'create': self._route('post', path , controller, 'create' ); + case 'create': self._route('post', path , controller, 'create' , getCollectionHelper()); break; - case 'show': self._route('get' , path + '/' + param + '.:format?' , controller, 'show' , helper); + case 'show': self._route('get' , path + '/' + param + '.:format?' , controller, 'show' , getResourceHelper()); break; case 'edit': self._route('get' , path + '/' + param + '/edit.:format?', controller, 'edit' , utils.functionize('edit', helper)); break; - case 'update': self._route('put' , path + '/' + param , controller, 'update' ); + case 'update': self._route('put' , path + '/' + param , controller, 'update' , getResourceHelper()); break; - case 'destroy': self._route('del' , path + '/' + param , controller, 'destroy'); + case 'destroy': self._route('del' , path + '/' + param , controller, 'destroy' , getResourceHelper()); break; } }); diff --git a/lib/utils.js b/lib/utils.js index 65f38f9..83e502d 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -268,3 +268,31 @@ exports.pathRegexp = function(path, keys, sensitive, strict) { .replace(/\*/g, '(.*)'); return new RegExp('^' + path + '$', sensitive ? '' : 'i'); }; + +/** + * Generates function, which will call provided function only once and return it's result. + * All subsequent calls it will return this cached result, or the value of `futherResult`, if provided. + * + * @param {Function} fn + * @param [futherResult] + * @returns {Function} + */ +exports.once = function (fn, futherResult) { + var executed = false + , futherResultPassed = (arguments.length > 1); + + return function () { + var fnResult; + + if (executed) { + return futherResult; + } else { + executed = true; + fnResult = fn.apply(this, arguments); + if (!futherResultPassed) { + futherResult = fnResult; + } + return fnResult; + } + } +};