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')
master
Buddy Sandidge 9 years ago
parent a317aa86a8
commit 7c34964dff

@ -1,14 +1,17 @@
'use strict' 'use strict'
var utils = require('./utils') var utils = require('./utils')
var noop = utils.noop
var assign = utils.assign var assign = utils.assign
var isFunction = utils.isFunction
var isRegExp = utils.isRegExp
var isString = utils.isString var isString = utils.isString
var noop = utils.noop
function RouteNode (options) { function RouteNode (options) {
options = options || {} options = options || {}
this._children = Object.create(null) this._children = Object.create(null)
this._regExs = Object.create(null) this._regExs = Object.create(null)
this._funcs = Object.create(null)
} }
assign(RouteNode.prototype, { assign(RouteNode.prototype, {
@ -29,8 +32,10 @@ assign(RouteNode.prototype, {
if (isString(part)) { if (isString(part)) {
node = this._children[part] node = this._children[part]
} else { } else if (isRegExp(part)) {
this._regExs[part] = part this._regExs[part] = part
} else if (isFunction(part)) {
this._funcs[part] = part
} }
if (node == null) { if (node == null) {
@ -53,45 +58,57 @@ assign(RouteNode.prototype, {
return childNode.get(parts, args, done) return childNode.get(parts, args, done)
} else { } else {
var results = this._getRegexChild(part) var results = this._getRegexChild(part)
if (results && results.node) {
return results.node.get(parts, args.concat(results.args), done)
} else {
results = this._getFunctionChild(part)
if (results && results.node) { if (results && results.node) {
return results.node.get(parts, args.concat(results.args), done) return results.node.get(parts, args.concat(results.args), done)
} else { } else {
return done(new Error('not found')) return done(new Error('not found'))
} }
} }
}
}, },
_getRegexChild: function _getRegexChild(part) { _getRegexChild: function _getRegexChild(part) {
var childKey = null
var args = []
var self = this var self = this
Object.keys(this._regExs).forEach(function checkRegex(key) { return Object.keys(this._regExs).reduce(function checkRegex(memo, key) {
if (childKey) { if (memo) {
return return memo
} }
var regex = self._regExs[key] var regex = self._regExs[key]
var results = regex.exec(part) var results = regex.exec(part)
var lastIndex = null if (!results) {
if (results) { return null
childKey = key
} }
var lastIndex = null
var args = []
while (results && results.index !== lastIndex) { while (results && results.index !== lastIndex) {
args = args.concat(results[0]) args = args.concat(results[0])
lastIndex = results.index lastIndex = results.index
results = regex.exec(part) results = regex.exec(part)
} }
}) return {args: args, key: key, node: self._children[key]}
}, null)
},
if (childKey) { _getFunctionChild: function _getFunctionChild(part) {
return { var self = this
args: args, return Object.keys(self._funcs).reduce(function checkFunc(memo, key) {
key: childKey, if (memo) {
node: this._children[childKey] return memo
} }
var func = self._funcs[key]
var result = func(part)
if (result) {
return {args: [result], key: key, node: self._children[key]}
} else { } else {
return null return null
} }
}, null)
} }
}) })

@ -31,8 +31,8 @@ function checkType(type) {
} }
} }
// add isRegExp, isArray // add isRegExp, isArray, isFunction
['RegExp', 'Array'].forEach(function addIsChecks(type) { ['RegExp', 'Array', 'Function'].forEach(function addIsChecks(type) {
utils['is' + type] = checkType(type) utils['is' + type] = checkType(type)
}) })

@ -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()
})
})
}) })
}) })

@ -75,4 +75,25 @@ describe('Router', function () {
done() 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()
})
})
}) })

@ -4,6 +4,7 @@ var utils = require('../lib/utils')
var assign = utils.assign var assign = utils.assign
var forEach = utils.forEach var forEach = utils.forEach
var isArray = utils.isArray var isArray = utils.isArray
var isFunction = utils.isFunction
var isRegExp = utils.isRegExp var isRegExp = utils.isRegExp
var isString = utils.isString var isString = utils.isString
var noop = utils.noop 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 () { describe('→ forEach', function () {
it('→ does not call properties on prototype', function () { it('→ does not call properties on prototype', function () {
var count = 0 var count = 0

Loading…
Cancel
Save