Skip to content

Commit

Permalink
[added] formatPattern util method
Browse files Browse the repository at this point in the history
  • Loading branch information
mjackson committed Jun 15, 2015
1 parent aef8747 commit 2447ecb
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 1 deletion.
50 changes: 50 additions & 0 deletions modules/URLUtils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import qs from 'qs';
import invariant from 'invariant';

export var parseQueryString = qs.parse;

Expand Down Expand Up @@ -134,3 +135,52 @@ export function matchPattern(pattern, pathname) {
export function getParamNames(pattern) {
return compilePattern(pattern).paramNames;
}

/**
* Returns a version of the given pattern with params interpolated. Throws
* if there is a dynamic segment of the pattern for which there is no param.
*/
export function formatPattern(pattern, params) {
params = params || {};

var { tokens } = compilePattern(pattern);
var parenCount = 0, pathname = '', splatIndex = 0;

var token, paramName, paramValue;
for (var i = 0, len = tokens.length; i < len; ++i) {
token = tokens[i];

if (token === '*') {
paramValue = Array.isArray(params.splat) ? params.splat[splatIndex++] : params.splat;

invariant(
paramValue != null || parenCount > 0,
'Missing splat #%s for path "%s"',
splatIndex, pattern
);

if (paramValue != null)
pathname += paramValue;
} else if (token === '(') {
parenCount += 1;
} else if (token === ')') {
parenCount -= 1;
} else if (token.charAt(0) === ':') {
paramName = token.substring(1);
paramValue = params[paramName];

invariant(
paramValue != null || parenCount > 0,
'Missing "%s" parameter for path "%s"',
paramName, pattern
);

if (paramValue != null)
pathname += paramValue;
} else {
pathname += token;
}
}

return pathname.replace(/\/+/g, '/');
}
100 changes: 99 additions & 1 deletion modules/__tests__/URLUtils-test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import expect from 'expect';
import { getPathname, getQueryString } from '../URLUtils';
import { getPathname, getQueryString, formatPattern } from '../URLUtils';

describe('getPathname', function () {
it('returns the pathname portion of a path', function () {
Expand All @@ -16,3 +16,101 @@ describe('getQueryString', function () {
describe('matchPattern', function () {
it('ignores trailing slashes');
});

describe('formatPattern', function () {
describe('when a pattern does not have dynamic segments', function () {
var pattern = 'a/b/c';

it('returns the pattern', function () {
expect(formatPattern(pattern, {})).toEqual(pattern);
});
});

describe('when a pattern has dynamic segments', function () {
var pattern = 'comments/:id/edit';

describe('and a param is missing', function () {
it('throws an Error', function () {
expect(function () {
formatPattern(pattern, {});
}).toThrow(Error);
});
});

describe('and a param is optional', function () {
var pattern = 'comments/(:id)/edit';

it('returns the correct path when param is supplied', function () {
expect(formatPattern(pattern, { id:'123' })).toEqual('comments/123/edit');
});

it('returns the correct path when param is not supplied', function () {
expect(formatPattern(pattern, {})).toEqual('comments/edit');
});
});

describe('and a param and forward slash are optional', function () {
var pattern = 'comments(/:id)/edit';

it('returns the correct path when param is supplied', function () {
expect(formatPattern(pattern, { id:'123' })).toEqual('comments/123/edit');
});

it('returns the correct path when param is not supplied', function () {
expect(formatPattern(pattern, {})).toEqual('comments/edit');
});
});

describe('and all params are present', function () {
it('returns the correct path', function () {
expect(formatPattern(pattern, { id: 'abc' })).toEqual('comments/abc/edit');
});

it('returns the correct path when the value is 0', function () {
expect(formatPattern(pattern, { id: 0 })).toEqual('comments/0/edit');
});
});

describe('and some params have special URL encoding', function () {
it('returns the correct path', function () {
expect(formatPattern(pattern, { id: 'one, two' })).toEqual('comments/one, two/edit');
});
});

describe('and a param has a forward slash', function () {
it('preserves the forward slash', function () {
expect(formatPattern(pattern, { id: 'the/id' })).toEqual('comments/the/id/edit');
});
});

describe('and some params contain dots', function () {
it('returns the correct path', function () {
expect(formatPattern(pattern, { id: 'alt.black.helicopter' })).toEqual('comments/alt.black.helicopter/edit');
});
});
});

describe('when a pattern has one splat', function () {
it('returns the correct path', function () {
expect(formatPattern('/a/*/d', { splat: 'b/c' })).toEqual('/a/b/c/d');
});
});

describe('when a pattern has multiple splats', function () {
it('returns the correct path', function () {
expect(formatPattern('/a/*/c/*', { splat: [ 'b', 'd' ] })).toEqual('/a/b/c/d');
});

it('complains if not given enough splat values', function () {
expect(function () {
formatPattern('/a/*/c/*', { splat: [ 'b' ] });
}).toThrow(Error);
});
});

describe('when a pattern has dots', function () {
it('returns the correct path', function () {
expect(formatPattern('/foo.bar.baz')).toEqual('/foo.bar.baz');
});
});
});

0 comments on commit 2447ecb

Please sign in to comment.