Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Export code from railwayjs

  • Loading branch information...
commit 919446665e946d39678a92357291ceb33d0be649 1 parent 7e1bd60
@1602 authored
Showing with 294 additions and 13 deletions.
  1. +211 −11 lib/railway_routes.js
  2. +83 −2 test/railway_routes_test.js
View
222 lib/railway_routes.js
@@ -1,3 +1,5 @@
+var singularize = require('./inflection').singularize;
+
exports.Map = Map;
function Map(app, bridge) {
@@ -6,7 +8,9 @@ function Map(app, bridge) {
this.paths = [];
this.ns = '';
// wtf???
- this.glob_path = '/';
+ this.globPath = '/';
+ this.pathTo = {};
+ this.dump = [];
}
Map.prototype.urlHelperName = function (path, action) {
@@ -54,7 +58,7 @@ Map.prototype.urlHelperName = function (path, action) {
var path;
if (typeof subpath === 'string') {
- path = this.glob_path + subpath.replace(/^\/|\/$/, '');
+ path = this.globPath + subpath.replace(/^\/|\/$/, '');
} else { // regex???
path = subpath;
}
@@ -84,17 +88,213 @@ Map.prototype.urlHelperName = function (path, action) {
}
args = args.concat(this.bridge(this.ns, controller, action));
- // this.bridge.dump.push({
- // helper: options.as || this.urlHelperName(path, action),
- // method: method,
- // path: path,
- // file: this.ns + controller,
- // name: controller,
- // action: action
- // });
+ this.dump.push({
+ helper: options.as || this.urlHelperName(path, action),
+ method: method,
+ path: path,
+ file: this.ns + controller,
+ name: controller,
+ action: action
+ });
- // this.paths.push([path, action]);
+ this.addPath(path, action);
this.app[method].apply(this.app, args);
};
});
+
+Map.prototype.addPath = function (templatePath, action) {
+ if (templatePath instanceof RegExp) {
+ // TODO: think about adding to `path_to` routes by reg ex
+ return;
+ }
+ var paramsLength = templatePath.match(/\/:/g);
+ paramsLength = paramsLength === null ? 0 : paramsLength.length;
+ var helperName = this.urlHelperName(templatePath, action);
+
+ // already defined? not need to redefine
+ if (this.pathTo[helperName]) return;
+
+ this.pathTo[helperName] = function () {
+ if (arguments.length < paramsLength) {
+ return '';
+ // throw new Error('Expected at least ' + paramsLength + ' params for build path ' + templatePath + ' but only ' + arguments.length + ' passed');
+ }
+ var value, arg, path = templatePath;
+ for (var i = 0; i < paramsLength; i += 1) {
+ value = null;
+ arg = arguments[i];
+ if (arg && typeof arg.to_param == 'function') {
+ value = arg.to_param();
+ } else if (arg && arg.id) {
+ value = arg.id;
+ } else {
+ value = arg;
+ }
+ path = path.replace(/:\w*/, value);
+ }
+ if (arguments[paramsLength]) {
+ var query = [];
+ for (var key in arguments[paramsLength]) {
+ if (key == 'format' && path.match(/\.:format\??$/)) {
+ path = path.replace(/\.:format\??$/, '.' + arguments[paramsLength][key]);
+ } else {
+ query.push(key + '=' + arguments[paramsLength][key]);
+ }
+ }
+ if (query.length) {
+ path += '?' + query.join('&');
+ }
+ }
+ path = path.replace(/\.:format\?/, '');
+ // add ability to hook url handling via app
+ if (this.app.hooks && this.app.hooks.path) {
+ this.app.hooks.path.forEach(function (hook) {
+ path = hook(path);
+ });
+ }
+ return path;
+ }.bind(this);
+ this.pathTo[helperName].toString = function () {
+ return this.pathTo[helperName]();
+ }.bind(this);
+}
+
+Map.prototype.resources = function (name, params, actions) {
+ var self = this;
+ // params are optional
+ params = params || {};
+ // if params arg omitted, second arg may be `actions`
+ if (typeof params == 'function') {
+ actions = params;
+ params = {};
+ }
+ // we have bunch of actions here, will create routes for them
+ var activeRoutes = getActiveRoutes(params);
+ // but first, create subroutes
+ if (typeof actions == 'function') {
+ this.subroutes(name + '/:' + (singularize(name) || name) + '_id', actions);
+ }
+ // now let's walk through action routes
+ for (var action in activeRoutes) {
+ var route = activeRoutes[action].split(/\s+/);
+ var method = route[0];
+ var path = route[1];
+
+ // append format
+ if (path == '/') {
+ path = '.:format?';
+ } else {
+ path += '.:format?';
+ }
+
+ // middleware logic (backward compatibility)
+ var middlewareExcept = params.middlewareExcept, skipMiddleware = false;
+ if (middlewareExcept) {
+ if (typeof middlewareExcept == 'string') {
+ middlewareExcept = [middlewareExcept];
+ }
+ middlewareExcept.forEach(function (a) {
+ if (a == action) {
+ skipMiddleware = true;
+ }
+ });
+ }
+
+ // params.path setting allows to override common path component
+ var effectivePath = (params.path || name) + path;
+
+ // and call map.{get|post|update|delete}
+ // with the path, controller, middleware and options
+ this[method.toLowerCase()].call(
+ this,
+ effectivePath,
+ name + '#' + action,
+ skipMiddleware ? [] : params.middleware,
+ getParams(action, params)
+ );
+ }
+
+ // calculate set of routes based on params.only and params.except
+ function getActiveRoutes(params) {
+ var activeRoutes = {},
+ availableRoutes =
+ { 'index': 'GET /'
+ , 'create': 'POST /'
+ , 'new': 'GET /new'
+ , 'edit': 'GET /:id/edit'
+ , 'destroy': 'DELETE /:id'
+ , 'update': 'PUT /:id'
+ , 'show': 'GET /:id'
+ };
+
+ // 1. only
+ if (params.only) {
+ if (typeof params.only == 'string') {
+ params.only = [params.only];
+ }
+ params.only.forEach(function (action) {
+ if (action in availableRoutes) {
+ activeRoutes[action] = availableRoutes[action];
+ }
+ });
+ }
+ // 2. except
+ else if (params.except) {
+ if (typeof params.except == 'string') {
+ params.except = [params.except];
+ }
+ for (var action in availableRoutes) {
+ if (params.except.indexOf(action) == -1) {
+ activeRoutes[action] = availableRoutes[action];
+ }
+ }
+ }
+ // 3. all
+ else {
+ for (var action in availableRoutes) {
+ activeRoutes[action] = availableRoutes[action];
+ }
+ }
+ return activeRoutes;
+ }
+
+ function getParams(action, params) {
+ var p = {};
+ var plural = action === 'index' || action === 'create';
+ if (params.as) {
+ p.as = plural ? params.as : singularize(params.as);
+ p.as = self.urlHelperName(self.globPath + p.as);
+ if (action === 'new' || action === 'edit') {
+ p.as = action + '_' + p.as;
+ }
+ }
+ if (params.path && !p.as) {
+ var aname = plural ? name : singularize(name);
+ aname = self.urlHelperName(self.globPath + aname);
+ p.as = action === 'new' || action === 'edit' ? action + '_' + aname : aname;
+ }
+ return p;
+ }
+};
+
+Map.prototype.namespace = function (name, subroutes) {
+ // store previous ns
+ var old_ns = this.ns, oldGlobPath = this.globPath;
+ // add new ns to old (ensure tail slash present)
+ this.ns = old_ns + name.replace(/\/$/, '') + '/';
+ this.globPath = oldGlobPath + name.replace(/\/$/, '') + '/';
+ subroutes(this);
+ this.ns = old_ns;
+ this.globPath = oldGlobPath;
+};
+
+Map.prototype.subroutes = function (name, subroutes) {
+ // store previous ns
+ var oldGlobPath = this.globPath;
+ // add new ns to old (ensure tail slash present)
+ this.globPath = oldGlobPath + name.replace(/\/$/, '') + '/';
+ subroutes(this);
+ this.globPath = oldGlobPath;
+};
+
View
85 test/railway_routes_test.js
@@ -1,5 +1,5 @@
require('./spec_helper').init(module.exports);
-var pathetic = require('../lib/railway_routes');
+var routes = require('../lib/railway_routes');
function fakeApp(container) {
var app = {};
@@ -21,7 +21,7 @@ function fakeBridge() {
it('should produce routes for all methods', function (test) {
var paths = [];
- var map = new pathetic.Map(fakeApp(paths), fakeBridge());
+ var map = new routes.Map(fakeApp(paths), fakeBridge());
map.get('/signin', 'session#new');
map.post('/signin', 'session#create');
map.del('/signout', 'session#destroy');
@@ -37,7 +37,88 @@ it('should produce routes for all methods', function (test) {
[ 'PUT', '/signup', 'users#create' ],
[ 'ALL', '/path', 'controller#action' ]
]);
+ test.equal(map.pathTo.signin(), '/signin');
+ test.equal(map.pathTo.signin, '/signin');
+ test.equal(map.pathTo.signout(), '/signout');
+ test.equal(map.pathTo.signup(), '/signup');
+ test.equal(map.pathTo.path(), '/path');
test.done();
});
+it('should create resourceful routes', function (test) {
+ var paths = [];
+ var map = new routes.Map(fakeApp(paths), fakeBridge());
+ map.resources('users');
+ test.deepEqual(paths, [
+ [ 'GET', '/users.:format?', 'users#index' ],
+ [ 'POST', '/users.:format?', 'users#create' ],
+ [ 'GET', '/users/new.:format?', 'users#new' ],
+ [ 'GET', '/users/:id/edit.:format?', 'users#edit' ],
+ [ 'DELETE', '/users/:id.:format?', 'users#destroy' ],
+ [ 'PUT', '/users/:id.:format?', 'users#update' ],
+ [ 'GET', '/users/:id.:format?', 'users#show' ]
+ ]);
+ test.equal(map.pathTo.users(), '/users');
+ test.equal(map.pathTo.users, '/users');
+ test.equal(map.pathTo.new_user(), '/users/new');
+ test.equal(map.pathTo.new_user, '/users/new');
+ test.equal(map.pathTo.edit_user(1), '/users/1/edit');
+ test.equal(map.pathTo.user(2), '/users/2');
+ test.done();
+});
+
+it('should describe namespaced route', function (test) {
+ var paths = [];
+ var map = new routes.Map(fakeApp(paths), fakeBridge());
+ map.namespace('admin', function (admin) {
+ admin.get('dashboard', 'dashboard#index');
+ });
+ test.deepEqual(paths, [
+ [ 'GET', '/admin/dashboard', 'admin/dashboard#index' ]
+ ]);
+ test.equal(map.pathTo.admin_dashboard(), '/admin/dashboard');
+ test.done();
+});
+it('should describe namespaced resource', function (test) {
+ var paths = [];
+ var map = new routes.Map(fakeApp(paths), fakeBridge());
+ map.namespace('admin', function (admin) {
+ admin.resources('pages');
+ });
+ test.deepEqual(paths, [
+ [ 'GET', '/admin/pages.:format?', 'admin/pages#index' ],
+ [ 'POST', '/admin/pages.:format?', 'admin/pages#create' ],
+ [ 'GET', '/admin/pages/new.:format?', 'admin/pages#new' ],
+ [ 'GET', '/admin/pages/:id/edit.:format?', 'admin/pages#edit' ],
+ [ 'DELETE', '/admin/pages/:id.:format?', 'admin/pages#destroy' ],
+ [ 'PUT', '/admin/pages/:id.:format?', 'admin/pages#update' ],
+ [ 'GET', '/admin/pages/:id.:format?', 'admin/pages#show' ]
+ ]);
+ test.equal(map.pathTo.admin_pages, '/admin/pages');
+ test.equal(map.pathTo.new_admin_page(), '/admin/pages/new');
+ test.equal(map.pathTo.new_admin_page, '/admin/pages/new');
+ test.equal(map.pathTo.edit_admin_page(1), '/admin/pages/1/edit');
+ test.equal(map.pathTo.admin_page(2), '/admin/pages/2');
+ test.done();
+});
+
+it('should allow overwrite path and helper', function (test) {
+ var paths = [];
+ var map = new routes.Map(fakeApp(paths), fakeBridge());
+ map.resources('avatars', {as: 'images', path: 'pictures'});
+ test.deepEqual(paths, [
+ [ 'GET', '/pictures.:format?', 'avatars#index' ],
+ [ 'POST', '/pictures.:format?', 'avatars#create' ],
+ [ 'GET', '/pictures/new.:format?', 'avatars#new' ],
+ [ 'GET', '/pictures/:id/edit.:format?', 'avatars#edit' ],
+ [ 'DELETE', '/pictures/:id.:format?', 'avatars#destroy' ],
+ [ 'PUT', '/pictures/:id.:format?', 'avatars#update' ],
+ [ 'GET', '/pictures/:id.:format?', 'avatars#show' ]
+ ]);
+ test.equal(map.pathTo.pictures, '/pictures');
+ test.equal(map.pathTo.new_picture, '/pictures/new');
+ test.equal(map.pathTo.edit_picture(1), '/pictures/1/edit');
+ test.equal(map.pathTo.picture(1602), '/pictures/1602');
+ test.done();
+});
Please sign in to comment.
Something went wrong with that request. Please try again.