Skip to content

Commit

Permalink
Add ability to substitue the query string parsing and stingifying lib…
Browse files Browse the repository at this point in the history
…rary
  • Loading branch information
Michael Ridgway committed Mar 16, 2016
1 parent 6bea56e commit d08b311
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 15 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,20 @@ var router = new Router([
]);

// match route
var route = router.getRoute('/user/garfield');
var route = router.getRoute('/user/garfield?foo=bar');
if (route) {
// this will output:
// - "view_user" for route.name
// - "/user/garfield" for route.url
// - {id: "garfield"} for route.params
// - {path: "/user/:id", method: "get", foo: { bar: "baz"}} for route.config
// - { foo: 'bar' } for route.query
console.log('[Route found]:', route);
}

// generate path name (does not include query string) from route
// "path" will be "/user/garfield/post/favoriteFood"
var path = router.makePath('view_user_post', {id: 'garfield', post: 'favoriteFood'});
// "path" will be "/user/garfield/post/favoriteFood?meal=breakfast"
var path = router.makePath('view_user_post', {id: 'garfield', post: 'favoriteFood'}, { meal: 'breakfast' });

```

Expand Down
9 changes: 6 additions & 3 deletions docs/routr.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Routr API

## Constructor(routes)
## Constructor(routes, options)

Creates a new routr plugin instance with the following parameters:

Expand All @@ -9,6 +9,8 @@ Creates a new routr plugin instance with the following parameters:
** `route.path`: The matching pattern of the route. Follows rules of [path-to-regexp](https://github
.com/pillarjs/path-to-regexp)
** `route.method=GET`: The method that the path should match to.
* `options` (optional): Options for parsing and generating the urls
** `options.queryLib=require('query-string')`: Library to use to `parse` and `stringify` query strings

## Instance Methods

Expand All @@ -20,9 +22,10 @@ Returns the matched route info if path/method matches to a route; null otherwise
* `options` options object
* `options.method` (optional) The case-insensitive HTTP method string. DEFAULT: 'get'

### makePath(name, params)
### makePath(name, params, query)

Generates a path string with the route with the given name, using the specified params.

* `name` (required) The route name
* `options` (required) The route parameters to be used to create the path string
* `params` (required) The route parameters to be used to create the path string
* `query` (required) The query parameters to be used to create the path string
23 changes: 15 additions & 8 deletions lib/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@ var cachedCompilers = {};
* @param {String} name The name of the route
* @param {Object} config The configuration for this route.
* @param {String} config.path The path of the route.
* @param {Object} [options] Options for parsing and generating the urls
* @param {String} [options.queryLib] Library to use for `parse` and `stringify` methods
* @constructor
*/
function Route(name, config) {
function Route(name, config, options) {
options = options || {};
this.name = name;
this.config = config || {};
this.keys = [];
this.regexp = pathToRegexp(this.config.path, this.keys);
this._queryLib = options.queryLib || queryString;
}

/**
Expand Down Expand Up @@ -105,7 +109,7 @@ Route.prototype.match = function (url, options) {
// 4. query params
var queryParams = {};
if (-1 !== pos) {
queryParams = queryString.parse(url.substring(pos));
queryParams = self._queryLib.parse(url.substring(pos+1));
}

return {
Expand All @@ -118,7 +122,7 @@ Route.prototype.match = function (url, options) {
* Generates a path string with this route, using the specified params.
* @method makePath
* @param {Object} params The route parameters to be used to create the path string
* @param {Object} query The query parameters to be used to create the path string
* @param {Object} [query] The query parameters to be used to create the path string
* @return {String} The generated path string.
* @for Route
*/
Expand All @@ -139,7 +143,7 @@ Route.prototype.makePath = function (params, query) {
try {
url = compiler(params);
if (query) {
url += '?' + queryString.stringify(query);
url += '?' + this._queryLib.stringify(query);
}
return url;
} catch (e) {
Expand All @@ -157,6 +161,8 @@ Route.prototype.makePath = function (params, query) {
* A Router class that provides route matching and route generation functionalities.
* @class Router
* @param {Object} routes Route table, which is a name to router config map.
* @param {Object} [options] Options for parsing and generating the urls
* @param {String} [options.queryLib] Library to use for `parse` and `stringify` methods
* @constructor
* @example
var Router = require('routr'),
Expand All @@ -183,17 +189,18 @@ Route.prototype.makePath = function (params, query) {
console.log('[Route found]: name=', route.name, 'url=', route.url, 'params=', route.params, 'config=', route.config);
}
*/
function Router(routes) {
function Router(routes, options) {
var self = this;
self._routes = {};
self._routeOrder = [];
self._options = options || {};
debug('new Router, routes = ', routes);

if (!Array.isArray(routes)) {
// Support handling route config object as an ordered map (legacy)
self._routeOrder = Object.keys(routes);
self._routeOrder.forEach(function createRoute(name) {
self._routes[name] = new Route(name, routes[name]);
self._routes[name] = new Route(name, routes[name], self._options);
});
} else if (routes) {
routes.forEach(function createRouteFromArrayValue(route) {
Expand All @@ -205,7 +212,7 @@ function Router(routes) {
throw new Error('Duplicate route with name ' + route.name);
}
self._routeOrder.push(route.name);
self._routes[route.name] = new Route(route.name, route);
self._routes[route.name] = new Route(route.name, route, self._options);
});
}

Expand Down Expand Up @@ -257,7 +264,7 @@ Router.prototype.getRoute = function (url, options) {
* @method makePath
* @param {String} name The route name
* @param {Object} params The route parameters to be used to create the path string
* @param {Object} query The query parameters to be used to create the path string
* @param {Object} [query] The query parameters to be used to create the path string
* @return {String} The generated path string, null if there is no route with the given name.
*/
Router.prototype.makePath = function (name, params, query) {
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
"istanbul": "^0.3.2",
"jshint": "^2.5.1",
"mocha": "^2.0.1",
"precommit-hook": "^2.0.1"
"precommit-hook": "^2.0.1",
"sinon": "^1.17.3"
},
"jshintConfig": {
"node": true
Expand Down
39 changes: 39 additions & 0 deletions tests/unit/lib/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

var expect = require('chai').expect;
var Router = require('../../../lib/router');
var sinon = require('sinon');
var routesObject = {
article: {
path: '/:site/:category?/:subcategory?/:alias',
Expand Down Expand Up @@ -359,6 +360,44 @@ describe('Router', function () {
]);
}).to.throw(Error);
});

it('should allow custom query string library', function () {
var queryLib = {
parse: sinon.spy(function (queryString) {
return queryString.split('&').reduce(function (a, v) {
var split = v.split('=');
a[split[0]] = split[1] || null;
return a;
}, {});
}),
stringify: sinon.spy(function (queryObject) {
return Object.keys(queryObject).map(function (key) {
return key + '=' + queryObject[key];
}).join('&');
})
};
var router = new Router([
{
name: 'home',
path: '/',
method: 'get'
}
], {
queryLib: queryLib
});
var matched = router.getRoute('/?foo=bar&bar=baz');
expect(queryLib.parse.called).to.equal(true);
expect(matched.query).to.deep.equal({
foo: 'bar',
bar: 'baz'
});
var stringified = router.makePath('home', {}, {
foo: 'bar',
bar: 'baz'
});
expect(queryLib.stringify.called).to.equal(true);
expect(stringified).to.equal('/?foo=bar&bar=baz');
});
});

describe('Route', function () {
Expand Down

0 comments on commit d08b311

Please sign in to comment.