From 7c34964dff6360f1e2e01f4c4b55e2f84d0cc77c Mon Sep 17 00:00:00 2001 From: Buddy Sandidge Date: Sun, 7 Feb 2016 21:11:51 -0800 Subject: [PATCH] Add support for functions in router Can add a function as to match a route. The function takes in a string url segment and returns some value if the route matches or returns null or undefined for no match. Example usage: function toInt(part) { var results = /^(\d+)$/.exec(part) return results ? parseInt(results[0], 10) : null } var router = new Router() router.add(['/by/order', toInt], function (val) { // val === 123 }) router.route('/by/order/123') --- lib/route-node.js | 57 ++++++++++++++++++++++++++++++---------------- lib/utils.js | 4 ++-- test/route-node.js | 20 ++++++++++++++++ test/router.js | 21 +++++++++++++++++ test/utils.js | 19 ++++++++++++++++ 5 files changed, 99 insertions(+), 22 deletions(-) diff --git a/lib/route-node.js b/lib/route-node.js index 077191f..6c67816 100644 --- a/lib/route-node.js +++ b/lib/route-node.js @@ -1,14 +1,17 @@ 'use strict' var utils = require('./utils') -var noop = utils.noop var assign = utils.assign +var isFunction = utils.isFunction +var isRegExp = utils.isRegExp var isString = utils.isString +var noop = utils.noop function RouteNode (options) { options = options || {} this._children = Object.create(null) this._regExs = Object.create(null) + this._funcs = Object.create(null) } assign(RouteNode.prototype, { @@ -29,8 +32,10 @@ assign(RouteNode.prototype, { if (isString(part)) { node = this._children[part] - } else { + } else if (isRegExp(part)) { this._regExs[part] = part + } else if (isFunction(part)) { + this._funcs[part] = part } if (node == null) { @@ -56,42 +61,54 @@ assign(RouteNode.prototype, { if (results && results.node) { return results.node.get(parts, args.concat(results.args), done) } else { - return done(new Error('not found')) + results = this._getFunctionChild(part) + if (results && results.node) { + return results.node.get(parts, args.concat(results.args), done) + } else { + return done(new Error('not found')) + } } } }, _getRegexChild: function _getRegexChild(part) { - var childKey = null - var args = [] var self = this - Object.keys(this._regExs).forEach(function checkRegex(key) { - if (childKey) { - return + return Object.keys(this._regExs).reduce(function checkRegex(memo, key) { + if (memo) { + return memo } var regex = self._regExs[key] var results = regex.exec(part) - var lastIndex = null - if (results) { - childKey = key + if (!results) { + return null } + + var lastIndex = null + var args = [] while (results && results.index !== lastIndex) { args = args.concat(results[0]) lastIndex = results.index results = regex.exec(part) } - }) + return {args: args, key: key, node: self._children[key]} + }, null) + }, - if (childKey) { - return { - args: args, - key: childKey, - node: this._children[childKey] + _getFunctionChild: function _getFunctionChild(part) { + var self = this + return Object.keys(self._funcs).reduce(function checkFunc(memo, key) { + if (memo) { + return memo } - } else { - return null - } + var func = self._funcs[key] + var result = func(part) + if (result) { + return {args: [result], key: key, node: self._children[key]} + } else { + return null + } + }, null) } }) diff --git a/lib/utils.js b/lib/utils.js index 412b55a..ea919aa 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -31,8 +31,8 @@ function checkType(type) { } } -// add isRegExp, isArray -['RegExp', 'Array'].forEach(function addIsChecks(type) { +// add isRegExp, isArray, isFunction +['RegExp', 'Array', 'Function'].forEach(function addIsChecks(type) { utils['is' + type] = checkType(type) }) diff --git a/test/route-node.js b/test/route-node.js index f7abcd6..64fca80 100644 --- a/test/route-node.js +++ b/test/route-node.js @@ -58,5 +58,25 @@ module.exports = describe('RouteNode', function () { }) }) + it('→ get route with function', function (done) { + function toInt(part) { + var results = /^(\d+)$/.exec(part) + if (results) { + return parseInt(results[0], 10) + } else { + return null + } + } + + node.add(['by', 'id', toInt], callback, context) + node.get(['by', 'id', '123'], [], function (err, func, cbContext, args) { + expect(err).toBe(null, err && err.message) + expect(func).toBe(callback) + expect(cbContext).toBe(context) + expect(args).toEqual([123]) + done() + }) + }) + }) }) diff --git a/test/router.js b/test/router.js index 795f8e0..7600c2c 100644 --- a/test/router.js +++ b/test/router.js @@ -75,4 +75,25 @@ describe('Router', function () { done() }) }) + + it('→ get based on function', function (done) { + function toInt(part) { + var results = /^(\d+)$/.exec(part) + if (results) { + return parseInt(results[0], 10) + } else { + return null + } + } + + var router = new Router() + router.add(['by', 'order', toInt], function (val) { + expect(val).toBe(123) + }) + + router.route('/by/order/123', function (err) { + expect(err).toBe(null, err && err.message) + done() + }) + }) }) diff --git a/test/utils.js b/test/utils.js index 9377b07..44f2e74 100644 --- a/test/utils.js +++ b/test/utils.js @@ -4,6 +4,7 @@ var utils = require('../lib/utils') var assign = utils.assign var forEach = utils.forEach var isArray = utils.isArray +var isFunction = utils.isFunction var isRegExp = utils.isRegExp var isString = utils.isString var noop = utils.noop @@ -58,6 +59,24 @@ describe('utils', function () { }) }) + describe('→ isFunction', function () { + it('→ true for function', function () { + expect(isFunction(function () {})).toBe(true) + }) + + it('→ false for array', function () { + expect(isFunction([])).toBe(false) + }) + + it('→ false for regex', function () { + expect(isFunction(/some regex/)).toBe(false) + }) + + it('→ false for strings', function () { + expect(isFunction('some string')).toBe(false) + }) + }) + describe('→ forEach', function () { it('→ does not call properties on prototype', function () { var count = 0