diff --git a/Routes/routeHandler/routeFinder.js b/Routes/routeHandler/routeFinder.js index 3ce7ada..721c2f3 100644 --- a/Routes/routeHandler/routeFinder.js +++ b/Routes/routeHandler/routeFinder.js @@ -1,5 +1,5 @@ module.exports = (function() { - var findBestMatchingRoute = function(patterns, params) { + var findBestMatchingRoute = function(routes, params) { var paramKeys = []; for (var key in params) { if (params[key]) { @@ -13,8 +13,8 @@ module.exports = (function() { var bestMatch = ''; var currentBest = 0; - for (var i = patterns.length; i--;) { - var pattern = patterns[i]; + for (var i = routes.length; i--;) { + var pattern = routes[i]; var patternItems = pattern._parameters; if (patternItems.length > paramKeys.length) { @@ -35,12 +35,37 @@ module.exports = (function() { } return bestMatch; - }; + }; + + var getUrl = function (bestMatch, params) { + var qs = []; + + for (var key in params) { + var toMatch = '{' + key + '}'; + if (!bestMatch.match(toMatch)) { + qs.push(key + '=' + encodeURIComponent(params[key])); + } else { + bestMatch = bestMatch.replace(toMatch, params[key]); + } + } + + var qsString = ''; + + if (qs.length) { + qsString = '?' + qs.join('&'); + } + + return bestMatch + qsString; + }; - var RouteFinder = function RouteFinder() { + var RouteFinder = function RouteFinder(routes, params) { + var bestMatch = findBestMatchingRoute(routes, params); + return getUrl(bestMatch, params); }; + // Expose a couple of methods for unit testing. RouteFinder.findBestMatchingRoute = findBestMatchingRoute; + RouteFinder.getUrl = getUrl; return RouteFinder; })(); \ No newline at end of file diff --git a/Routes/routeHandler/routeParser.js b/Routes/routeHandler/routeParser.js index 0e30bb3..7f20692 100644 --- a/Routes/routeHandler/routeParser.js +++ b/Routes/routeHandler/routeParser.js @@ -64,6 +64,8 @@ module.exports = (function() { } } + pattern.data = pattern.data || {}; + pattern.func = generateParseFunction(pattern.url); pattern._parameters = getParameters(pattern.url); return pattern; diff --git a/test/Routes/routeHandler/routeFinder.js b/test/Routes/routeHandler/routeFinder.js index 0b00f78..a179b47 100644 --- a/test/Routes/routeHandler/routeFinder.js +++ b/test/Routes/routeHandler/routeFinder.js @@ -85,11 +85,10 @@ describe('RouteFinder', function() { it('should fail to find a route if all routes have an additional parameter.', function() { var match = routeFinder.findBestMatchingRoute([{ url: '/{something}/{test}/', - _parameters: ['something','test'] - }, - { + _parameters: ['something', 'test'] + }, { url: '/{something}/{controller}/{action}/{test}/', - _parameters: ['something','controller','action','test'] + _parameters: ['something', 'controller', 'action', 'test'] } ], { controller: 'test', @@ -100,18 +99,16 @@ describe('RouteFinder', function() { match.should.equal(''); }); - it('should a route if all routes except the match have an additional parameter.', function() { + it('should find a route if all routes except the match have an additional parameter.', function() { var match = routeFinder.findBestMatchingRoute([{ url: '/{something}/{test}/', - _parameters: ['something','test'] - }, - { + _parameters: ['something', 'test'] + }, { url: '/{something}/{controller}/{action}/{test}/', - _parameters: ['something','controller','action','test'] - }, - { + _parameters: ['something', 'controller', 'action', 'test'] + }, { url: '/{controller}/{action}/{test}/', - _parameters: ['controller','action','test'] + _parameters: ['controller', 'action', 'test'] } ], { controller: 'test', @@ -122,4 +119,84 @@ describe('RouteFinder', function() { match.should.equal('/{controller}/{action}/{test}/'); }); }); + + describe('#getUrl()', function() { + it('should use the query string if the best matching route has no parameters.', function() { + var parameters = { + test: 'testValue', + test2: 'testValue2' + }; + + var result = routeFinder.getUrl('/', parameters); + + result.should.equal('/?test=testValue&test2=testValue2'); + }); + + it('should use the query string and the route if a partial match is found.', function() { + var parameters = { + test: 'testValue', + test2: 'testValue2' + }; + + var result = routeFinder.getUrl('/{test}/', parameters); + + result.should.equal('/testValue/?test2=testValue2'); + }); + + it('should not use the query string if a full match is found.', function() { + var parameters = { + test: 'testValue', + test2: 'testValue2' + }; + + var result = routeFinder.getUrl('/{test}/{test2}/', parameters); + + result.should.equal('/testValue/testValue2/'); + }); + + it('should retain constants in a matching route.', function() { + var parameters = { + test: 'testValue', + test2: 'testValue2' + }; + + var result = routeFinder.getUrl('/{test}/hello/{test2}-!/', parameters); + + result.should.equal('/testValue/hello/testValue2-!/'); + }); + + it('should retain additional parameters in a route with too many bindings.', function() { + // This case should not be used, over-matched routes should not be passed to this method. + var parameters = { + test: 'testValue', + test2: 'testValue2' + }; + + var result = routeFinder.getUrl('/{test}/{test2}/{test3}/', parameters); + + result.should.equal('/testValue/testValue2/{test3}/'); + }); + }); + + describe('public interface', function() { + it('should parse a route to the expected URL.', function() { + var url = routeFinder([{ + url: '/{something}/{test}/', + _parameters: ['something', 'test'] + }, { + url: '/{something}/{controller}/{action}/{test}/', + _parameters: ['something', 'controller', 'action', 'test'] + }, { + url: '/{controller}/{action}/{test}/', + _parameters: ['controller', 'action', 'test'] + } + ], { + controller: 'test', + action: 'test', + test: 'test1' + }); + + url.should.equal('/test/test/test1/'); + }); + }); }); \ No newline at end of file diff --git a/test/Routes/routeHandler/routeParser.js b/test/Routes/routeHandler/routeParser.js index 7c9e0db..fc84eb7 100644 --- a/test/Routes/routeHandler/routeParser.js +++ b/test/Routes/routeHandler/routeParser.js @@ -121,4 +121,40 @@ describe('routeHandler', function() { }); }); }); + + describe('public interface', function() { + it('should parse a route that is provided as a string.', function() { + var result = routeParser('/{test}/'); + + result.should.have.property('func'); + result.data.should.eql({}); + result._parameters.should.eql(['test']); + }); + + it('should parse a route that is provided as an object.', function() { + var result = routeParser({ + url: '/{test}/' + }); + + result.should.have.property('func'); + result.data.should.eql({}); + result._parameters.should.eql(['test']); + }); + + it('should return any passed data as pre-set data.', function() { + var result = routeParser({ + url: '/{test}/', + data: { + testData: 'test' + } + }); + + result.should.have.property('func'); + result.data.should.eql({ + testData: 'test' + }); + + result._parameters.should.eql(['test']); + }); + }); }); \ No newline at end of file