You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
343 lines
12 KiB
343 lines
12 KiB
(function() {
|
|
var Controllers, fs, path;
|
|
var __slice = Array.prototype.slice;
|
|
|
|
fs = require('fs');
|
|
|
|
path = require('path');
|
|
|
|
module.exports = function(app, options) {
|
|
var _ref, _ref2, _ref3, _ref4, _ref5;
|
|
if (options == null) options = {};
|
|
if ((_ref = options.strict) == null) options.strict = true;
|
|
if ((_ref2 = options.overwriteRender) == null) options.overwriteRender = true;
|
|
if ((_ref3 = options.log) == null) options.log = false;
|
|
if ((_ref4 = options.root) == null) {
|
|
options.root = app.set('controllers') || process.cwd() + '/controllers';
|
|
}
|
|
if ((_ref5 = options.sharedFolder) == null) options.sharedFolder = 'shared';
|
|
return new Controllers(app, options);
|
|
};
|
|
|
|
Controllers = (function() {
|
|
|
|
function Controllers(app, options) {
|
|
var originalRoute, self;
|
|
this.options = options;
|
|
self = this;
|
|
this._controllers = {};
|
|
this.executeOnDirectory(this.options.root, function(file) {
|
|
var controller, ext, reduced;
|
|
ext = path.extname(file);
|
|
if (ext === '.js' || ext === '.coffee') {
|
|
reduced = file.replace(ext, '');
|
|
controller = path.basename(reduced);
|
|
self._controllers[controller] = require(reduced);
|
|
if (self.options.log) {
|
|
return console.log("Controller '" + controller + "' has been loaded");
|
|
}
|
|
}
|
|
});
|
|
originalRoute = app.routes._route;
|
|
app.routes._route = function() {
|
|
var c, callbacks, defaults, defkey, defvalue, holder, key, method, newCallbacks, newRoute, path, result, _i, _j, _len, _len2, _ref;
|
|
method = arguments[0], path = arguments[1], defaults = arguments[2], callbacks = 4 <= arguments.length ? __slice.call(arguments, 3) : [];
|
|
if ('function' === typeof defaults) {
|
|
callbacks.push(defaults);
|
|
defaults = null;
|
|
}
|
|
if (callbacks.length === 0) callbacks.push(function(req, res) {});
|
|
if (defaults == null) defaults = {};
|
|
holder = {};
|
|
for (_i = 0, _len = callbacks.length; _i < _len; _i++) {
|
|
c = callbacks[_i];
|
|
newCallbacks = self.overwriteCallback(c, holder);
|
|
}
|
|
result = originalRoute.call(app.routes, method, path, newCallbacks);
|
|
holder.route = newRoute = result.routes[method][result.routes[method].length - 1];
|
|
for (defkey in defaults) {
|
|
defvalue = defaults[defkey];
|
|
key = self.getKeyInRoute(defkey, newRoute);
|
|
if (key != null) {
|
|
key["default"] = defvalue;
|
|
} else {
|
|
if (defkey === 'controller' || defkey === 'action') {
|
|
newRoute[defkey] = defvalue;
|
|
}
|
|
}
|
|
}
|
|
_ref = newRoute.keys;
|
|
for (_j = 0, _len2 = _ref.length; _j < _len2; _j++) {
|
|
key = _ref[_j];
|
|
if (key.name === 'controller' || key.name === 'action') {
|
|
newRoute[key.name] = '*';
|
|
}
|
|
}
|
|
return result;
|
|
};
|
|
this.addHelpers(app);
|
|
}
|
|
|
|
Controllers.prototype.addReqHelpers = function(req, res) {
|
|
var self;
|
|
self = this;
|
|
return req.executeController = function(controller, action, next) {
|
|
var currentA, currentC, nextFunc;
|
|
if (!(controller != null) || !(action != null)) {
|
|
throw new Error("executeController needs the controller and action specified");
|
|
}
|
|
if (next != null) {
|
|
currentC = req.controller;
|
|
currentA = req.action;
|
|
nextFunc = next;
|
|
next = function() {
|
|
req.controller = currentC;
|
|
req.action = currentA;
|
|
return nextFunc.apply(this, arguments);
|
|
};
|
|
}
|
|
req.controller = controller;
|
|
req.action = action;
|
|
return self._controllers[controller][action](req, res, next);
|
|
};
|
|
};
|
|
|
|
Controllers.prototype.addHelpers = function(app) {
|
|
var self;
|
|
self = this;
|
|
return app.dynamicHelpers({
|
|
controller: function(req, res) {
|
|
return req.controller;
|
|
},
|
|
action: function(req, res) {
|
|
return req.action;
|
|
},
|
|
getUrl: function(req, res) {
|
|
return function(controller, action, other, query) {
|
|
var def, first, hasReplaced, i, key, regExp, replacement, result, route, value, _i, _len, _ref, _ref2, _ref3, _ref4;
|
|
if (!(action != null) || 'object' === typeof action) {
|
|
query = other;
|
|
other = action;
|
|
action = controller;
|
|
controller = null;
|
|
}
|
|
if (controller == null) controller = req.controller;
|
|
if (other == null) other = {};
|
|
other.controller = controller;
|
|
other.action = action;
|
|
if (query == null) query = {};
|
|
if (!(action != null) || !(controller != null)) {
|
|
throw new Error("getUrl needs at minimum an action defined, but also takes a controller");
|
|
}
|
|
_ref = app.routes.routes.get;
|
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
|
route = _ref[_i];
|
|
if (self.isMatchingPath(other, route)) {
|
|
hasReplaced = false;
|
|
result = route.path;
|
|
for (i = _ref2 = route.keys.length - 1; _ref2 <= 0 ? i <= 0 : i >= 0; _ref2 <= 0 ? i++ : i--) {
|
|
key = route.keys[i];
|
|
def = (_ref3 = key["default"]) != null ? _ref3 : '';
|
|
replacement = (_ref4 = other[key.name]) != null ? _ref4 : def;
|
|
if (hasReplaced && replacement === '') {
|
|
throw new Error("The optional parameter '" + key.name + "' is required for this getUrl call as an parameter further down the path has been specified");
|
|
} else {
|
|
if (!hasReplaced) {
|
|
if ((!key.optional || replacement !== def) && (hasReplaced = true)) {} else {
|
|
replacement = '';
|
|
}
|
|
}
|
|
}
|
|
regExp = new RegExp(":" + key.name + "(\\?)?");
|
|
result = result.replace(regExp, replacement);
|
|
}
|
|
result = result.replace(/\/+/g, '/');
|
|
if (result !== '/') result = result.replace(/\/+$/, '');
|
|
first = true;
|
|
for (key in query) {
|
|
value = query[key];
|
|
if (first) {
|
|
first = false;
|
|
result = result + '?' + key;
|
|
if ((value != null) && value !== '') {
|
|
result = result + '=' + value;
|
|
}
|
|
} else {
|
|
result = result + '&' + key;
|
|
if ((value != null) && value !== '') {
|
|
result = result + '=' + value;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
throw new Error("Route could not be found that matches getUrl parameters, make sure to specify a valid controller, action and required parameters");
|
|
};
|
|
}
|
|
});
|
|
};
|
|
|
|
Controllers.prototype.getKeyInRoute = function(name, route) {
|
|
var key, _i, _len, _ref;
|
|
_ref = route.keys;
|
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
|
key = _ref[_i];
|
|
if (key.name === name) return key;
|
|
}
|
|
return null;
|
|
};
|
|
|
|
Controllers.prototype.isMatchingPath = function(object, route) {
|
|
var key, value, _i, _len, _ref;
|
|
if (route.controller !== '*' && route.controller !== object.controller) {
|
|
return false;
|
|
}
|
|
if (route.action !== '*' && route.action !== object.action) return false;
|
|
for (key in object) {
|
|
value = object[key];
|
|
if (key !== 'controller' && key !== 'action') {
|
|
if (!((this.getKeyInRoute(key, route)) != null)) return false;
|
|
}
|
|
}
|
|
_ref = route.keys;
|
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
|
key = _ref[_i];
|
|
if (key.name !== 'controller' && key.name !== 'action') {
|
|
if (!key.optional && !(object[key] != null)) return false;
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
|
|
Controllers.prototype.overwriteCallback = function(callback, routeHolder) {
|
|
var options, self;
|
|
self = this;
|
|
options = this.options;
|
|
return function(req, resp, next) {
|
|
var action, controller, key, route, _i, _len, _ref, _ref2, _ref3;
|
|
callback(req, resp, next);
|
|
self.addReqHelpers(req, resp);
|
|
route = routeHolder.route;
|
|
_ref = route.keys;
|
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
|
key = _ref[_i];
|
|
if (!(req.params[key.name] != null) && (key["default"] != null)) {
|
|
req.params[key.name] = key["default"];
|
|
}
|
|
}
|
|
req.controller = (_ref2 = req.params.controller) != null ? _ref2 : route.controller;
|
|
req.action = (_ref3 = req.params.action) != null ? _ref3 : route.action;
|
|
if (options.log) {
|
|
console.log('Controller: ' + req.controller);
|
|
console.log('Action: ' + req.action);
|
|
}
|
|
if (options.strict) {
|
|
if (!(req.controller != null)) {
|
|
throw new Error("Is in strict mode and no controller specified");
|
|
}
|
|
if (!(req.action != null)) {
|
|
throw new Error("Is in strict mode and no action specified");
|
|
}
|
|
}
|
|
if ((req.controller != null) && (req.action != null)) {
|
|
if (options.overwriteRender) self.overwriteRender(req, resp);
|
|
controller = self._controllers[req.controller];
|
|
if (!(controller != null)) {
|
|
if (options.log) {
|
|
console.log("Controller '" + req.controller + "' could not be found");
|
|
}
|
|
next('route');
|
|
return;
|
|
}
|
|
action = controller[req.action];
|
|
if (!(action != null)) {
|
|
if (options.log) {
|
|
console.log("Action '" + req.action + "' could not be found on controller '" + req.controller + "' ");
|
|
}
|
|
next('route');
|
|
return;
|
|
}
|
|
return action(req, resp, next);
|
|
} else {
|
|
if (options.log) {
|
|
return console.log('Controller or action was not specified, no action was called');
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
Controllers.prototype.overwriteRender = function(req, resp) {
|
|
var original, root, self;
|
|
self = this;
|
|
original = resp.render;
|
|
root = resp.app.set('views') || process.cwd() + '/views';
|
|
return resp.render = function(view, opts, fn, parent, sub) {
|
|
var finalPass, hasHints, reset, result, secondRender, secondResult;
|
|
if ('object' === typeof view || 'function' === typeof view) {
|
|
sub = parent;
|
|
parent = fn;
|
|
fn = opts;
|
|
opts = view;
|
|
view = null;
|
|
}
|
|
if (view == null) view = req.action;
|
|
hasHints = resp.app.enabled('hints');
|
|
resp.app.disable('hints');
|
|
result = null;
|
|
secondResult = null;
|
|
reset = function() {
|
|
if (hasHints) return resp.app.enable('hints');
|
|
};
|
|
finalPass = function(err, err2, str) {
|
|
reset();
|
|
if (err != null) err = err + '\r\n\r\n' + err2;
|
|
if (fn != null) {
|
|
return fn(err, str);
|
|
} else {
|
|
if (err != null) {
|
|
return req.next(err);
|
|
} else {
|
|
return resp.send(str);
|
|
}
|
|
}
|
|
};
|
|
secondRender = function(err, str) {
|
|
if (err != null) {
|
|
return secondResult = original.call(resp, self.options.sharedFolder + '/' + view, opts, (function(err2, str2) {
|
|
return finalPass(err2, err, str2);
|
|
}), parent, sub);
|
|
} else {
|
|
reset();
|
|
if (fn != null) {
|
|
return fn(err, str);
|
|
} else {
|
|
return resp.send(str);
|
|
}
|
|
}
|
|
};
|
|
result = original.call(resp, req.controller + '/' + view, opts, secondRender, parent, sub);
|
|
if (secondResult != null) result = secondResult;
|
|
reset();
|
|
return result;
|
|
};
|
|
};
|
|
|
|
Controllers.prototype.executeOnDirectory = function(dir, action) {
|
|
return fs.readdirSync(dir).forEach(function(file) {
|
|
var localpath, stat;
|
|
localpath = dir + '/' + file;
|
|
stat = fs.statSync(localpath);
|
|
if (stat && stat.isDirectory()) {
|
|
return self.executeOnDirectory(localpath, action);
|
|
} else {
|
|
return action(localpath);
|
|
}
|
|
});
|
|
};
|
|
|
|
return Controllers;
|
|
|
|
})();
|
|
|
|
}).call(this);
|