Add Router that only matches exact strings with bootstrapped project

• Add linting with eslint
 • Add unit testing support with mocha with unit tests
 • Add Router class with support for only matching exact strings
 • Add RouteNode class for transversing all routes
master
Buddy Sandidge 9 years ago
parent b9d7eb3a7b
commit 10371b7b1e

@ -0,0 +1,19 @@
{
"rules": {
"indent": [2, 2],
"linebreak-style": [2, "unix"],
"no-console": 0,
"no-unexpected-multiline": 2,
"no-unreachable": 2,
"quotes": [2, "single"],
"semi": [2, "never"]
},
"env": {
"browser": true,
"mocha": true,
"node": true
},
"extends": "eslint:recommended",
"ecmaFeatures": {},
"plugins": []
}

@ -1,2 +1,52 @@
# routing-buddy # routing-buddy
Another routing library
Yet another routing library in JavaScript.
This project was created after looking at and trying several JavaScript routing libraries out there and not finding one that really worked that way I wanted it to work.
I wanted a library that could be used both on the client and server side without feeling like it was designed for one side with the other bolted on like it was an afterthought.
I also wanted a library that gave more flexibility for defining routes.
## Status
**NOTE**: This is currently a Work in progress.
I'm not yet sure if this will be the routing library I want.
## Design
The main class exported in this library is `Router`.
`Router` is a wrapper around `RouteNode`.
A `RouteNode` is a tree structure to define all routes.
## For development
To run unit tests:
```bash
$ # run unit tests and lint
$ npm test
$ # run unit tests only
$ mocha
$ # run linter only
$ eslint
```
See unit tests for more details.
### Usage:
```javascript
var Router = require('routing-buddy')
var router = new Router()
// Define a handler to be called when routed to the url
router.add('/some/path/here', function () {
// do something for '/some/path/here'
})
// Get the handler for '/some/path/here' and call it.
// Call the callback after finding the route
router.route('/some/path/here', function (err, maybeResults) {
// `err` will be either null or Error.
// null: everything was fine
// Error: missing route or any other error that might happen
// maybeResults: The return value from the hander from add
})
```

@ -0,0 +1,53 @@
'use strict'
var utils = require('./utils')
var noop = utils.noop
var assign = utils.assign
function RouteNode (options) {
options = options || {}
this._debug = !!options.debug
this._exact = Object.create(null)
}
assign(RouteNode.prototype, {
handler: null,
add: function add(parts, handler, context) {
parts = parts || []
handler = handler || noop
context = context || null
if (parts.length === 0) {
this.handler = handler
this.context = context
return this
}
var part = parts.shift()
var node = this._exact[part]
if (node == null) {
node = new RouteNode({debug: this._debug})
this._exact[part] = node
}
return node.add(parts, handler, context)
},
get: function get(parts, args, done) {
parts = parts || []
args = args || []
if (parts.length === 0) {
return done(null, this.handler, this.context, args)
}
var part = parts.shift()
var childNode = this._exact[part]
if (childNode) {
return childNode.get(parts, args, done)
} else {
return done(new Error('not found'))
}
}
})
module.exports = RouteNode

@ -0,0 +1,38 @@
'use strict'
var url = require('url')
var RouteNode = require('./route-node')
var utils = require('./utils')
var assign = utils.assign
var noop = utils.noop
function Router(options) {
options = options || {}
this.routes = new RouteNode()
}
assign(Router.prototype, {
add: function add(path, handler) {
return this.routes.add(this._uriToParts(path), handler)
},
route: function route(path, done) {
var parts = this._uriToParts(path)
done = done || noop
return this.routes.get(parts, [], function (err, func, context, args) {
if (err) {
return done(err)
} else {
return done(null, func.apply(context, args))
}
})
},
_uriToParts: function _uriToParts(uri) {
return url.parse(uri)
.pathname
.split('/')
.filter(function (str) { return str !== ''})
}
})
module.exports = Router

@ -0,0 +1,12 @@
'use strict'
module.exports = {
noop: function noop() {},
assign: function assign (dest, adding) {
for (var key in adding) {
if (adding.hasOwnProperty(key)) {
dest[key] = adding[key]
}
}
}
}

@ -0,0 +1,34 @@
{
"name": "routing-buddy",
"version": "0.0.0",
"description": "Another router library for both client and server",
"main": "lib/router.js",
"scripts": {
"test": "mocha --reporter dot && eslint lib/*.js test/*.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/xbudex/routing-buddy.git"
},
"keywords": [
"routing",
"router",
"url",
"routing",
"universal",
"browser",
"server",
"client"
],
"author": "Buddy Sandidge <buddy.sandidge@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/xbudex/routing-buddy/issues"
},
"homepage": "https://github.com/xbudex/routing-buddy#readme",
"devDependencies": {
"eslint": "^1.10.3",
"expect": "^1.14.0",
"mocha": "^2.4.5"
}
}

@ -0,0 +1,50 @@
var expect = require('expect')
var RouteNode = require('../lib/route-node')
module.exports = describe('RouteNode', function () {
it('→ exists', function () {
expect(new RouteNode()).toExist()
})
describe('→ add nodes', function () {
var node = null
var callback = function () {}
var context = {}
beforeEach(function () {
node = new RouteNode()
})
it('→ add root node', function (done) {
node.add([], callback, context)
node.get([], [], function (err, func, cbContext, args) {
expect(err).toBe(null)
expect(func).toBe(callback)
expect(cbContext).toBe(context)
expect(args).toEqual([])
done()
})
})
it('→ get nested callback', function (done) {
node.add(['nested'], callback, context)
node.get(['nested'], [], function (err, func, cbContext, args) {
expect(err).toBe(null)
expect(func).toBe(callback)
expect(cbContext).toBe(context)
expect(args).toEqual([])
done()
})
})
it('→ get error callback for not found', function (done) {
node.get(['fake', 'route'], [], function (err, func, cbContext, args) {
expect(err).toBeA(Error)
expect(err.message).toMatch(/not found/)
expect(func).toNotExist()
expect(cbContext).toNotExist()
expect(args).toNotExist()
done()
})
})
})
})

@ -0,0 +1,40 @@
var expect = require('expect')
var Router = require('../lib/router')
describe('Router', function () {
it('→ exists', function () {
expect(Router).toExist()
})
describe('→ add routes', function () {
it('→ add root node', function (done) {
var router = new Router()
router.add('/', function () { return 'some value' })
router.route('/', function (err, handlerResult) {
expect(err).toBe(null)
expect(handlerResult).toBe('some value')
done()
})
})
it('→ add/get nested route', function (done) {
var router = new Router()
router.add('/some/path/here', function () { return 'some value' })
router.route('/some/path/here', function (err, handlerResult) {
expect(err).toBe(null)
expect(handlerResult).toBe('some value')
done()
})
})
it('→ get error in callback for missing handler', function (done) {
var router = new Router()
router.route('/some/fake/path', function (err, handlerResult) {
expect(err).toBeA(Error)
expect(err.message).toMatch(/not found/)
expect(handlerResult).toNotExist()
done()
})
})
})
})

@ -0,0 +1,45 @@
var expect = require('expect')
var utils = require('../lib/utils')
var noop = utils.noop
var assign = utils.assign
describe('utils', function () {
it('→ exists', function () {
expect(utils).toExist()
})
describe('→ noop', function () {
it('→ is a function', function () {
expect(noop).toBeA(Function)
})
})
describe('→ assign', function () {
it('→ adds properties to dest', function () {
var dest = {}
var src = {foo: 123, bar: 'asdf'}
assign(dest, src)
expect(dest).toEqual({foo: 123, bar: 'asdf'})
})
it('→ overrides properties to dest', function () {
var dest = {foo: 456, bar: 'qwerty', blah: /foo/}
var src = {foo: 123, bar: 'asdf'}
assign(dest, src)
expect(dest).toEqual({foo: 123, bar: 'asdf', blah: /foo/})
})
it('→ does not overrides properties on prototype', function () {
function SourceClass (foo) {
this.foo = foo
}
SourceClass.prototype.bar = 'asdf'
var dest = {foo: 456, bar: 'qwerty', blah: /foo/}
assign(dest, new SourceClass(123))
expect(dest).toEqual({foo: 123, bar: 'qwerty', blah: /foo/})
})
})
})
Loading…
Cancel
Save