diff --git a/README.md b/README.md index bd69d62..c618d70 100644 --- a/README.md +++ b/README.md @@ -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' }); ``` diff --git a/docs/routr.md b/docs/routr.md index 80cebc3..3394be1 100644 --- a/docs/routr.md +++ b/docs/routr.md @@ -1,6 +1,6 @@ # Routr API -## Constructor(routes) +## Constructor(routes, options) Creates a new routr plugin instance with the following parameters: @@ -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 @@ -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 diff --git a/lib/router.js b/lib/router.js index f849f9a..b66e111 100644 --- a/lib/router.js +++ b/lib/router.js @@ -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; } /** @@ -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 { @@ -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 */ @@ -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) { @@ -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'), @@ -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) { @@ -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); }); } @@ -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) { diff --git a/package.json b/package.json index 14dac68..6e9137d 100644 --- a/package.json +++ b/package.json @@ -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 diff --git a/tests/unit/lib/router.js b/tests/unit/lib/router.js index e149403..2a1f887 100644 --- a/tests/unit/lib/router.js +++ b/tests/unit/lib/router.js @@ -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', @@ -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 () {