flask-router-plus
Version:
Flask-inspired routing system for node and connect. Nice if you just need a routing system without depending on connect, or need routing middleware without all features provided by express.
319 lines (305 loc) • 10.4 kB
JavaScript
// Generated by CoffeeScript 1.6.3
(function() {
var Compiler, Router, absoluteUrl, normalizePathname, send, url,
__slice = [].slice,
__hasProp = {}.hasOwnProperty;
Compiler = require('./compiler');
url = require('url');
send = require('./send');
absoluteUrl = function(req, pathname, search) {
var port, protocol, rv;
protocol = 'http';
if (req.headers['x-protocol'] === 'https') {
protocol = 'https';
}
rv = [protocol, '://', req.headers.host];
if (port = req.headers['x-port']) {
rv.push(":" + port);
}
rv.push(pathname);
if (search) {
rv.push(search);
}
return rv.join('');
};
normalizePathname = function(pathname) {
var match, rv;
rv = pathname.replace(/\/\.\//g, '/');
while (match = /\/[^/][^/]*\/\.\./.exec(rv)) {
rv = rv.replace(match[0], '');
}
rv = rv.replace(/\/\.\./g, '');
rv = rv.replace(/\.\//g, '');
return rv || '/';
};
Router = (function() {
function Router(compiler) {
this.compiler = compiler;
this.rules = {
GET: [],
POST: [],
PUT: [],
DELETE: [],
PATCH: [],
OPTIONS: [],
HEAD: []
};
this.compiled = false;
}
Router.prototype.route = function(req, res, next) {
var checkRule, fail, matchedRules, p, ruleArray, urlObj,
_this = this;
if (typeof next !== 'function') {
next = function(err) {
var status;
status = 404;
if (err != null ? err.status : void 0) {
status = err.status;
}
res.writeHead(status);
return res.end();
};
}
req.usePath = req.originalUrl.substr(0, req.originalUrl.length, -req.url.length);
res.answer = function(code, headers, data) {
var args;
args = send["arguments"](code, headers, data);
res.writeHead(args.code, args.headers);
if (null !== args.data) {
res.write(args.data);
}
return this.end();
};
urlObj = url.parse(req.url);
p = normalizePathname(urlObj.pathname);
req.path = p;
this.compileRules();
ruleArray = this.rules[req.method];
matchedRules = {};
checkRule = function(idx, err) {
var end, extracted, handle, handlerChain, rule, status;
if (idx === ruleArray.length) {
return fail(err);
}
rule = ruleArray[idx];
if (extracted = rule.extractor.extract(p)) {
matchedRules[rule.id] = 1;
req.params = extracted;
end = res.end;
status = {
done: false
};
res.end = function() {
var args;
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
status.done = true;
return end.apply(res, args);
};
handlerChain = rule.handlers;
handle = function(i, err) {
var current, n;
n = function(arg) {
if (status.done) {
return;
}
if (arg === 'route') {
return checkRule(idx + 1);
}
if (i === handlerChain.length - 1) {
return checkRule(idx + 1, arg);
}
return handle(i + 1, arg);
};
current = handlerChain[i];
if ((err && current.length < 4) || (!err && current.length > 3)) {
return n(err);
} else {
return current(req, res, n, err);
}
};
return handle(0, err);
} else {
return checkRule(idx + 1, err);
}
};
fail = function(err) {
var allowed, bp, extracted, method, rule, _i, _j, _len, _len1, _ref;
bp = p + '/';
for (_i = 0, _len = ruleArray.length; _i < _len; _i++) {
rule = ruleArray[_i];
if (extracted = rule.extractor.extract(bp)) {
if (rule.id in matchedRules) {
continue;
}
res.writeHead(301, {
'Location': absoluteUrl(req, bp, urlObj.search)
});
res.end();
return;
}
}
allowed = [];
_ref = _this.rules;
for (method in _ref) {
if (!__hasProp.call(_ref, method)) continue;
ruleArray = _ref[method];
if (method === req.method) {
continue;
}
for (_j = 0, _len1 = ruleArray.length; _j < _len1; _j++) {
rule = ruleArray[_j];
if (rule.extractor.test(p)) {
if (rule.id in matchedRules) {
continue;
}
allowed.push(method);
break;
}
}
}
if (allowed.length) {
res.writeHead(405, {
'Allow': allowed.join(', ')
});
res.end();
return;
}
return next(err);
};
return checkRule(0);
};
Router.prototype.register = function() {
var handler, handlerArray, handlers, id, methodName, pattern, prefix, rule, ruleArray, _i, _j, _len, _len1;
prefix = arguments[0], methodName = arguments[1], pattern = arguments[2], handlers = 4 <= arguments.length ? __slice.call(arguments, 3) : [];
ruleArray = this.rules[methodName];
if (this.compiled) {
throw new Error('Cannot register rules after compilation');
}
if (!(typeof pattern === 'string' || pattern instanceof RegExp)) {
throw new Error('Pattern must be rule string or regex');
}
id = "" + prefix + "#" + (pattern.toString());
handlerArray = null;
for (_i = 0, _len = ruleArray.length; _i < _len; _i++) {
rule = ruleArray[_i];
if (rule.id === id) {
handlerArray = rule.handlers;
break;
}
}
if (!handlerArray) {
handlerArray = [];
ruleArray.push({
id: id,
pattern: pattern,
handlers: handlerArray
});
}
for (_j = 0, _len1 = handlers.length; _j < _len1; _j++) {
handler = handlers[_j];
if (typeof handler === 'function') {
handlerArray.push(handler);
} else if (Array.isArray(handler)) {
this.register.apply(this, [prefix, methodName, pattern].concat(handler));
} else {
throw new Error('Handler must be a function or array of functions');
}
}
return handlers;
};
Router.prototype.compileRules = function() {
var compiled, method, rule, ruleArray, _i, _len, _ref;
if (this.compiled) {
return;
}
_ref = this.rules;
for (method in _ref) {
if (!__hasProp.call(_ref, method)) continue;
ruleArray = _ref[method];
for (_i = 0, _len = ruleArray.length; _i < _len; _i++) {
rule = ruleArray[_i];
rule.extractor = this.compiler.compile(rule.pattern);
}
}
return compiled = true;
};
return Router;
})();
module.exports = function(parsers) {
var compiler, r;
if (!compiler) {
compiler = new Compiler(parsers);
}
r = new Router(compiler);
return {
route: function(req, res, next) {
return r.route(req, res, next);
},
registerParser: function(name, parser) {
return compiler.parsers[name] = parser;
},
get: function() {
var handlers, pattern;
pattern = arguments[0], handlers = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
return r.register.apply(r, ['get', 'GET', pattern].concat(__slice.call(handlers)));
},
options: function() {
var handlers, pattern;
pattern = arguments[0], handlers = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
return r.register.apply(r, ['options', 'OPTIONS', pattern].concat(__slice.call(handlers)));
},
head: function() {
var handlers, pattern;
pattern = arguments[0], handlers = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
return r.register.apply(r, ['head', 'HEAD', pattern].concat(__slice.call(handlers)));
},
post: function() {
var handlers, pattern;
pattern = arguments[0], handlers = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
return r.register.apply(r, ['post', 'POST', pattern].concat(__slice.call(handlers)));
},
put: function() {
var handlers, pattern;
pattern = arguments[0], handlers = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
return r.register.apply(r, ['put', 'PUT', pattern].concat(__slice.call(handlers)));
},
del: function() {
var handlers, pattern;
pattern = arguments[0], handlers = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
return r.register.apply(r, ['del', 'DELETE', pattern].concat(__slice.call(handlers)));
},
patch: function() {
var handlers, pattern;
pattern = arguments[0], handlers = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
return r.register.apply(r, ['patch', 'PATCH', pattern].concat(__slice.call(handlers)));
},
all: function() {
var handlers, method, pattern, _i, _len, _ref;
pattern = arguments[0], handlers = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
_ref = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
method = _ref[_i];
r.register.apply(r, ['all', method, pattern].concat(__slice.call(handlers)));
}
return handlers;
},
use: function() {
var handlers, usepath;
usepath = arguments[0], handlers = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
if (!handlers.length) {
handlers = [usepath];
usepath = '/';
}
if (usepath.slice(-1) !== '/') {
usepath += '/';
}
handlers.unshift(function(req, res, next) {
req.url = '/' + req.params.__path;
return next();
});
this.all.apply(this, ["" + usepath + "<path:__path>"].concat(__slice.call(handlers)));
return handlers;
}
};
};
}).call(this);