X-Git-Url: http://git.squeep.com/?p=squeep-api-dingus;a=blobdiff_plain;f=lib%2Frouter%2Findex.js;fp=lib%2Frouter.js;h=a13ac47d62f083e588d047f7d556f7502f4b91e4;hp=2bad3e2b509373d22279d6a866f1dbd1e57a11c8;hb=9fe1e9b22b8e753c44dec77d1dee3d0061b2e991;hpb=91197ffbd90707333da06a714aaec0b8b8143577 diff --git a/lib/router.js b/lib/router/index.js similarity index 67% rename from lib/router.js rename to lib/router/index.js index 2bad3e2..a13ac47 100644 --- a/lib/router.js +++ b/lib/router/index.js @@ -6,12 +6,12 @@ */ const { METHODS: httpMethods } = require('http'); -const common = require('./common'); -const { DingusError } = require('./errors'); +const common = require('../common'); +const { DingusError } = require('../errors'); +const PathParameter = require('./path-parameter'); // Internal identifiers for route entries. -const METHODS = Symbol('METHODS'); -const PARAM = Symbol('PARAM'); +const kPathMethods = Symbol('kSqueepDingusRouterPathMethods'); const defaultOptions = { ignoreTrailingSlash: false, @@ -30,8 +30,6 @@ const defaultOptions = { * mapping of methods to handler functions. * * @property {Object} pathsByLength index to registered paths by number of parts - * @property {Symbol} METHODS key to method:handler map on search paths - * @property {Symbol} PARAM key to parameter name in search path parts */ class Router { /** @@ -46,57 +44,50 @@ class Router { this.pathsByLength = { 1: [], }; - - this.METHODS = METHODS; - this.PARAM = PARAM; } + /** - * @typedef {Object} Router~ParamPart - * @property {String} PARAM (symbol key) - */ - /** - * @typedef {Array} Router~SearchPath - * @property {Object} METHODS (symbol key) + * @typedef {Array} Router~RoutePath + * @property {Object} kPathMethods (symbol key) */ /** * Prepare a path for insertion into search list. - * A searchable path is a list of path parts, with a property of an object of method handlers. - * @param {String} pathDefinition - * @returns {Router~SearchPath} + * A route path is an Array of path parts, with a symbolic property of an object mapping method handlers. + * @param {String} rawPath + * @returns {Router~RoutePath} * @private */ - _pathDefinitionToPathMatch(pathDefinition) { - const pathMatch = pathDefinition + _pathToRoutePath(rawPath) { + const routePath = rawPath .split('/') - .map((p) => p.startsWith(this.paramPrefix) ? { [PARAM]: p.slice(this.paramPrefix.length) } : p); + .map((p) => p.startsWith(this.paramPrefix) ? new PathParameter(p.slice(this.paramPrefix.length)) : p); if (this.ignoreTrailingSlash - && pathMatch[pathMatch.length - 1] === '') { - pathMatch.pop(); + && routePath[routePath.length - 1] === '') { + routePath.pop(); } - pathMatch[METHODS] = {}; - pathMatch.forEach((p) => Object.freeze(p)); - Object.freeze(pathMatch); - return pathMatch; + routePath[kPathMethods] = {}; + Object.freeze(routePath); + return routePath; } /** * Compare checkPath to fixedPath, no param substitution, params must match. - * @param {Router~SearchPath} fixedPath - * @param {Router~SearchPath} checkPath + * @param {Router~RoutePath} routePath + * @param {Router~RoutePath} checkPath * @returns {Boolean} * @private */ - static _pathCompareExact(fixedPath, checkPath) { - if (fixedPath.length !== checkPath.length) { + static _pathCompareExact(routePath, checkPath) { + if (routePath.length !== checkPath.length) { return false; } - for (let i = 0; i < fixedPath.length; i++) { - const fixedPart = fixedPath[i]; + for (let i = 0; i < routePath.length; i++) { + const fixedPart = routePath[i]; const checkPart = checkPath[i]; - if (typeof fixedPart === 'object' && typeof checkPart === 'object') { - if (fixedPart[PARAM] !== checkPart[PARAM]) { + if (fixedPart instanceof PathParameter && checkPart instanceof PathParameter) { + if (fixedPart.parameter !== checkPart.parameter) { return false; } } else if (fixedPart !== checkPart) { @@ -108,24 +99,24 @@ class Router { /** - * Compare checkPath to fixedPath, populating params. - * @param {Router~SearchPath} fixedPath + * Compare routePath to fixedPath, populating params. + * @param {Router~RoutePath} routePath * @param {Array} checkPath * @param {Object} returnParams * @returns {Boolean} * @private */ - static _pathCompareParam(fixedPath, checkPath, returnParams = {}) { + static _pathCompareParam(routePath, checkPath, returnParams = {}) { const params = {}; - if (fixedPath.length !== checkPath.length) { + if (routePath.length !== checkPath.length) { return false; } - for (let i = 0; i < fixedPath.length; i++) { - const fixedPart = fixedPath[i]; + for (let i = 0; i < routePath.length; i++) { + const fixedPart = routePath[i]; const checkPart = checkPath[i]; - if (typeof fixedPart === 'object') { - params[fixedPart[PARAM]] = checkPart; + if (fixedPart instanceof PathParameter) { + params[fixedPart.parameter] = checkPart; } else if (fixedPart !== checkPart) { return false; } @@ -138,7 +129,7 @@ class Router { /** * @typedef Router~MatchedPath * @property {Object} pathParams populated param fields - * @property {Router~SearchPath} matchedPath + * @property {Router~RoutePath} matchedPath */ /** * Search for an existing path, return matched path and path parameters. @@ -166,15 +157,15 @@ class Router { /** * Return a matching path, no param substitution, params must match - * @param {Router~SearchPath} matchParts - * @returns {Router~SearchPath=} + * @param {Router~RoutePath} routePath + * @returns {Router~RoutePath=} * @private */ - _pathFindExact(matchParts) { - const pathsByLength = this.pathsByLength[matchParts.length]; + _pathFindExact(routePath) { + const pathsByLength = this.pathsByLength[routePath.length]; if (pathsByLength) { for (const p of pathsByLength) { - if (Router._pathCompareExact(p, matchParts)) { + if (Router._pathCompareExact(p, routePath)) { return p; } } @@ -199,7 +190,7 @@ class Router { * @param {any[]} handlerArgs */ on(methods, urlPath, handler, handlerArgs = []) { - const matchParts = this._pathDefinitionToPathMatch(urlPath); + const matchParts = this._pathToRoutePath(urlPath); let existingPath = this._pathFindExact(matchParts); if (!existingPath) { existingPath = matchParts; @@ -218,7 +209,7 @@ class Router { if (!httpMethods.includes(method) && method !== '*') { throw new DingusError(`invalid method '${method}'`); } - existingPath[METHODS][method] = { handler, handlerArgs }; + existingPath[kPathMethods][method] = { handler, handlerArgs }; }); } @@ -243,11 +234,11 @@ class Router { ctx.params = pathParams; if (matchedPath) { ctx.matchedPath = matchedPath; - if (method in matchedPath[METHODS]) { - return matchedPath[METHODS][method]; + if (method in matchedPath[kPathMethods]) { + return matchedPath[kPathMethods][method]; } - if ('*' in matchedPath[METHODS]) { - return matchedPath[METHODS]['*']; + if ('*' in matchedPath[kPathMethods]) { + return matchedPath[kPathMethods]['*']; } throw new DingusError('NoMethod'); } @@ -257,5 +248,6 @@ class Router { } +Router.kPathMethods = kPathMethods; module.exports = Router;