Compare commits

...

29 Commits

Author SHA1 Message Date
Johannes J. Schmidt a307318af3 chore: update semantic release 7 years ago
Johannes J. Schmidt 2b1db7e2d1 fix: allow inserting falsy values, excluding undefined 7 years ago
Johannes Jörg Schmidt b7c944c8dd Merge pull request #56 from apihlaja/insert-falsy-parameters
Allow inserting falsy values, excluding undefined
7 years ago
Antti Pihlaja da62ae6713 allow inserting falsy values, excluding undefined 7 years ago
Johannes Jörg Schmidt d2e9f585c6 Merge pull request #32 from jo/greenkeeper-tap-5.1.1
tap@5.1.1 breaks build 🚨
8 years ago
greenkeeperio-bot 1e37668193 chore(package): update tap to version 5.1.1
http://greenkeeper.io/
8 years ago
Johannes Jörg Schmidt 6ab41aeb94 Merge pull request #31 from jo/greenkeeper-tap-5.0.0
Update tap to version 5.0.0 🚀
8 years ago
greenkeeperio-bot e4dfe89121 chore(package): update tap to version 5.0.0
http://greenkeeper.io/
8 years ago
Johannes J. Schmidt 0da6326aa4 chore: use version ranges 9 years ago
Johannes Jörg Schmidt 0d7fae7ebd Merge pull request #27 from jo/greenkeeper-tap-2.1.1
Update tap to version 2.1.1 🚀
9 years ago
greenkeeperio-bot 6f1afbd738 chore(package): update tap to version 2.1.1
http://greenkeeper.io/
9 years ago
Johannes J. Schmidt ded3af8ec1 fix: update semantic release 9 years ago
Johannes J. Schmidt b8bc23365f chore(travis): ignore release tags [skip ci] 9 years ago
Johannes Jörg Schmidt 12ede2f977 Merge pull request #25 from jo/greenkeeper-pin
Pinned all dependencies
9 years ago
Johannes Jörg Schmidt 8c276ddb18 Merge pull request #23 from Useclark/parens-in-values
Add a test for parens not saved in the value
9 years ago
Johannes Jörg Schmidt 6bfed7c7da Merge pull request #22 from Useclark/seq-replacement
Add a test for parameters replacing all at once to prevent colon conflicts
9 years ago
greenkeeperio-bot 190f768587 chore(package): pin dependencies
http://greenkeeper.io/
9 years ago
Johannes J. Schmidt 536f16675f feat: setup semantic release 9 years ago
Johannes J. Schmidt 725d8de788 feat: change license from MIT to Apache 2.0 9 years ago
Johannes Jörg Schmidt 2b2eb939bd Merge pull request #24 from zerotacg/optional-parameter-with-trailing-slash
optional parameter with a trailing slash
9 years ago
Tobias Peters f7a90694bd unit test and adjustments for using optional parameter with a trailing slash 9 years ago
Denis Sokolov 6176d32029 Add a test for parameters replacing all at once to prevent colon conflicts 9 years ago
Denis Sokolov 47849de070 Add a test for parens not saved in the value 9 years ago
Johannes Jörg Schmidt 3ed0d3cef0 Merge pull request #21 from Useclark/object-keys-bug
Add a failing test for reliance on Object.keys
9 years ago
Denis Sokolov a0274ddc47 Add a failing test for reliance on Object.keys 9 years ago
Johannes Jörg Schmidt 0bb37b1772 Merge pull request #20 from Useclark/encode
Encode parameter values to allow slashes in values, fix #15
9 years ago
Denis Sokolov 5cc5e5a249 Encode parameter values to allow slashes in values, fix #15 9 years ago
Johannes Jörg Schmidt 4bd7a135cd Merge pull request #18 from Useclark/no-global-state
Remove global state, return rules directly
9 years ago
Denis Sokolov 824d69fd08 Remove global state, return rules directly
This breaks backwards compatibility.

This removes .routes because it's not intuitive what the result value
of that must be, and users are free to .map over .route themselves.

Also, .routes was never tested.

Fixes #14.
9 years ago

@ -1,4 +1,17 @@
language: node_js
cache:
directories:
- node_modules
notifications:
email: false
node_js:
- "0.11"
- "0.10"
- '7'
- '6'
- '4'
before_script:
- npm prune
after_success:
- npm run semantic-release
branches:
except:
- /^v\d+\.\d+\.\d+$/

@ -0,0 +1,11 @@
Licensed under the Apache License, Version 2.0 (the "License"); you
may not use this file except in compliance with the License. You may
obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the License for the specific language governing
permissions and limitations under the License.

@ -72,46 +72,34 @@ function which parses
}
```
### `docuri.route(route, name)`
Create a single route. The `route` argument must be a routing string. The
`name` argument will be the identifier for the resulting function:
`docuri[name]`. Routes added later may override previously declared routes.
### `docuri.route(route)`
Create a single route. The `route` argument must be a routing string.
```js
// parses 'page/home' as { id: 'home' }:
docuri.route('page/:id', 'page');
var page = docuri.route('page/:id');
```
### `docuri.routes(map)`
Install a routes hash which maps DocURIs with parameters to functions:
```js
docuri.routes({
'movie/:id': 'movie',
'movie/:movie_id/:type/*path': 'movieAsset'
'movie/:movie_id/gallery-image/:id(/:version)': 'galleryImage',
});
```
### `docuri[name](strOrObj, [obj])`
### `route(strOrObj, [obj])`
The functions generated by DocURI can have a different behaviour, depending on
the type and number of the supplied arguments:
* `name(str)`: parse DocURI string to object
* `name(obj)`: generate DocURI string from object
* `name(str, obj)`: change DocURI string parts with values provided by object returning a string
* `route(str)`: parse DocURI string to object
* `route(obj)`: generate DocURI string from object
* `route(str, obj)`: change DocURI string parts with values provided by object returning a string
The function returns `false` if a string can not be parsed, enabling type
checks.
#### Example
```js
docuri.movie('movie/blade-runner');
movie('movie/blade-runner');
// { id: 'blade-runner' }
docuri.movieAsset('movie/blade-runner');
movieAsset('movie/blade-runner');
// false
docuri.galleryImage({ movie_id: 'blade-runner', id: 12 });
galleryImage({ movie_id: 'blade-runner', id: 12 });
// 'movie/blade-runner/gallery-image/12'
docuri.galleryImage('movie/blade-runner/gallery-image/12', { version: 'large' });
galleryImage('movie/blade-runner/gallery-image/12', { version: 'large' });
// 'movie/blade-runner/gallery-image/12/large'
```
@ -131,5 +119,5 @@ npm test
## License
Copyright (c) 2014 Johannes J. Schmidt, null2 GmbH
Licensed under the MIT license.
Licensed under the Apache 2.0 license.

@ -9,8 +9,6 @@
var docuri = module.exports = exports = {};
var reservedNames = ['routes', 'route'];
// Cached regular expressions for matching named param parts and splatted parts
// of route strings.
// http://backbonejs.org/docs/backbone.html#section-158
@ -65,15 +63,19 @@ function extractParameters(route, fragment) {
}
params = params.slice(1);
return Object.keys(route.keys).reduce(function(memo, key, i) {
var param = params[i];
if (param) {
if (key[0] === '*') {
param = param.split('/');
param = param.split('/').map(decodeURIComponent);
} else {
param = decodeURIComponent(param);
}
memo[key.substr(1)] = param;
}
@ -87,10 +89,12 @@ function insertParameters(route, obj) {
Object.keys(route.keys).forEach(function(key) {
var k = key.substr(1);
var value = obj[k] || '';
var value = (obj[k] !== undefined) ? obj[k] : '';
if (Array.isArray(value)) {
value = value.join('/');
value = value.map(encodeURIComponent).join('/');
} else {
value = encodeURIComponent(value);
}
str = str.replace(route.keys[key], value + '$1');
@ -98,27 +102,15 @@ function insertParameters(route, obj) {
// massage optional parameter
return str
.replace(/\(\/\)(\/|$)/g, '$1')
.replace(/\(\/\)/g, '')
.replace(/[)(]/g, '');
}
// Map routes
docuri.routes = function(map) {
Object.keys(map).forEach(function(route) {
docuri.route(route, map[route]);
});
};
// Manually bind a single named route
docuri.route = function(route, name) {
if (reservedNames.indexOf(name) > -1) {
throw('Reserved name "' + name + '" cannot be used.');
}
docuri.route = function(route) {
route = routeToRegExp(route);
docuri[name] = function(source, target) {
return function(source, target) {
source = source || {};
if (target) {

@ -1,25 +1,28 @@
{
"name": "docuri",
"version": "4.1.1",
"description": "Rich document ids for CouchDB",
"main": "index.js",
"scripts": {
"test": "tap test/*.js"
"test": "tap test/*.js",
"semantic-release": "semantic-release pre && npm publish && semantic-release post"
},
"repository": {
"type": "git",
"url": "git://github.com/jo/docuri.git"
"url": "https://github.com/jo/docuri.git"
},
"keywords": [
"couchdb"
],
"author": "Johannes J. Schmidt",
"license": "MIT",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/jo/docuri/issues"
},
"homepage": "https://github.com/jo/docuri",
"devDependencies": {
"tap": "^0.4.8"
}
"shuffle-array": "^0.1.0",
"tap": "^5.1.1",
"semantic-release": "^6.3.2"
},
"version": "0.0.0-development"
}

@ -2,11 +2,11 @@ var test = require('tap').test;
var docuri = require('..');
test('simple route', function(t) {
docuri.route('page', 'page');
var page = docuri.route('page');
t.deepEqual(docuri.page('page'), {}, 'parsed page returns empty object');
t.equal(docuri.page('image'), false, 'parsed image returns false');
t.equal(docuri.page(), 'page', 'generate page url');
t.deepEqual(page('page'), {}, 'parsed page returns empty object');
t.equal(page('image'), false, 'parsed image returns false');
t.equal(page(), 'page', 'generate page url');
t.end();
});

@ -2,26 +2,26 @@ var test = require('tap').test;
var docuri = require('..');
test('named parameter', function(t) {
docuri.route('page/:id', 'page');
var page = docuri.route('page/:id');
t.equal(docuri.page('page/mypage', { id: 'otherpage' }), 'page/otherpage', 'merged page has "id" set to "otherpage"');
t.equal(page('page/mypage', { id: 'otherpage' }), 'page/otherpage', 'merged page has "id" set to "otherpage"');
t.end();
});
test('optional parameter', function(t) {
docuri.route('page(/:id)', 'page');
var page = docuri.route('page(/:id)');
t.deepEqual(docuri.page('page/mypage'), { id: 'mypage' }, 'parsed page has "id" set to "mypage"');
t.equal(docuri.page('page/mypage', { id: 'otherpage' }), 'page/otherpage', 'merged page has "id" set to "otherpage"');
t.deepEqual(page('page/mypage'), { id: 'mypage' }, 'parsed page has "id" set to "mypage"');
t.equal(page('page/mypage', { id: 'otherpage' }), 'page/otherpage', 'merged page has "id" set to "otherpage"');
t.end();
});
test('two named parameters', function(t) {
docuri.route('page/:page_id/content/:id', 'content');
var content = docuri.route('page/:page_id/content/:id');
t.equal(docuri.content('page/mypage/content/mycontent', { id: 'othercontent' }), 'page/mypage/content/othercontent', 'merged content has "id" set to "othercontent"');
t.equal(content('page/mypage/content/mycontent', { id: 'othercontent' }), 'page/mypage/content/othercontent', 'merged content has "id" set to "othercontent"');
t.end();
});

@ -0,0 +1,36 @@
var shuffle = require('shuffle-array');
var test = require('tap').test;
var docuri = require('..');
var simulateWrongOrderObjectKeys = function(cb){
var orig = Object.keys;
Object.keys = function(obj){
var keys = orig(obj);
var real = keys.toString();
while (real === keys.toString())
shuffle(keys);
return keys;
};
try {
cb();
} catch (err) {
Object.keys = orig;
throw err;
}
Object.keys = orig;
};
test('named parameters are not reliant on Object.keys order', function(t) {
var page = docuri.route('page/:foo/:bar/:quux/:baz');
simulateWrongOrderObjectKeys(function(){
t.deepEqual(
page('page/1/2/3/4'),
{ foo: '1', bar: '2', quux: '3', baz: '4' },
'does not rely on Object.keys order',
{ todo: true }
);
});
t.end();
});

@ -2,28 +2,72 @@ var test = require('tap').test;
var docuri = require('..');
test('named parameter', function(t) {
docuri.route('page/:id', 'page');
var page = docuri.route('page/:id');
t.deepEqual(docuri.page('page/mypage'), { id: 'mypage' }, 'parsed page has "id" set to "mypage"');
t.equal(docuri.page({ id: 'mypage' }), 'page/mypage', 'stringified page results in "page/mypage"');
t.deepEqual(page('page/mypage'), { id: 'mypage' }, 'parsed page has "id" set to "mypage"');
t.equal(page({ id: 'mypage' }), 'page/mypage', 'stringified page results in "page/mypage"');
t.end();
});
test('insert falsy parameter values', function(t) {
var page = docuri.route('page/:id');
t.deepEqual(page({}), 'page/', 'stringifies without id');
t.deepEqual(page({id: undefined}), 'page/', 'stringifies without id');
t.deepEqual(page({id: 0}), 'page/0', 'inserts "0"');
t.deepEqual(page({id: false}), 'page/false', 'inserts "false"');
t.deepEqual(page({id: null}), 'page/null', 'inserts "null"');
t.end();
});
test('two named parameters', function(t) {
docuri.route('page/:page_id/content/:id', 'content');
var content = docuri.route('page/:page_id/content/:id');
t.deepEqual(docuri.content('page/mypage/content/mycontent'), { page_id: 'mypage', id: 'mycontent' }, 'parsed content has "page_id" set to "mypage" and "id" set to "mycontent"');
t.equal(docuri.content({ page_id: 'mypage', id: 'mycontent' }), 'page/mypage/content/mycontent', 'stringified content results in "page/mypage/content/mycontent"');
t.deepEqual(content('page/mypage/content/mycontent'), { page_id: 'mypage', id: 'mycontent' }, 'parsed content has "page_id" set to "mypage" and "id" set to "mycontent"');
t.equal(content({ page_id: 'mypage', id: 'mycontent' }), 'page/mypage/content/mycontent', 'stringified content results in "page/mypage/content/mycontent"');
t.end();
});
test('named parameters replaced all at once', function(t) {
var page = docuri.route('page/:id/:bar');
t.equal(page({ id: ':bar', bar: 'foo' }), 'page/:bar/foo', 'url built correctly, no conflicts', { todo: true });
t.end();
});
test('named parameter followed by optional parameter', function(t) {
docuri.route('page/:id(/:optional)', 'page');
var page = docuri.route('page/:id(/:optional)');
t.deepEqual(page('page/mypage'), { id: 'mypage' }, 'parsed page has "id" set to "mypage"');
t.equal(page({ id: 'mypage', optional: 'number' }), 'page/mypage/number', 'stringified page results in "page/mypage/number"');
t.end();
});
test('named parameter with colon in the content', function(t) {
var page = docuri.route('page/:id');
t.deepEqual(page(page({ id: 'value:colon' })), { id: 'value:colon' }, 'parsed page has colon set back correctly');
t.end();
});
test('named parameter with slash in the content', function(t) {
var page = docuri.route('page/:id');
t.deepEqual(page(page({ id: 'value/slash' })), { id: 'value/slash' }, 'parsed page has slash set back correctly');
t.end();
});
test('named parameter with parens in the content', function(t) {
var page = docuri.route('page/:id');
t.deepEqual(docuri.page('page/mypage'), { id: 'mypage' }, 'parsed page has "id" set to "mypage"');
t.equal(docuri.page({ id: 'mypage', optional: 'number' }), 'page/mypage/number', 'stringified page results in "page/mypage/number"');
t.deepEqual(page(page({ id: 'value(slash)' })), { id: 'value(slash)' }, 'parsed page has parens set back correctly', { todo: true });
t.end();
});

@ -2,23 +2,34 @@ var test = require('tap').test;
var docuri = require('..');
test('optional parameter', function(t) {
docuri.route('page(/:id)', 'page');
var page = docuri.route('page(/:id)');
t.deepEqual(docuri.page('page'), {}, 'parsed page returns empty object');
t.deepEqual(docuri.page('page/mypage'), { id: 'mypage' }, 'parsed page has "id" set to "mypage"');
t.equal(docuri.page(), 'page', 'stringified empty page results in "page"');
t.equal(docuri.page({ id: 'mypage' }), 'page/mypage', 'stringified page results in "page/mypage"');
t.deepEqual(page('page'), {}, 'parsed page returns empty object');
t.deepEqual(page('page/mypage'), { id: 'mypage' }, 'parsed page has "id" set to "mypage"');
t.equal(page(), 'page', 'stringified empty page results in "page"');
t.equal(page({ id: 'mypage' }), 'page/mypage', 'stringified page results in "page/mypage"');
t.end();
});
test('surrounded optional parameter', function(t) {
docuri.route('page(/:id)/suffix', 'page');
var page = docuri.route('page(/:id)/suffix');
t.deepEqual(docuri.page('page/suffix'), {}, 'parsed page returns empty object');
t.deepEqual(docuri.page('page/mypage/suffix'), { id: 'mypage' }, 'parsed page has "id" set to "mypage"');
t.equal(docuri.page(), 'page/suffix', 'stringified empty page results in "page"');
t.equal(docuri.page({ id: 'mypage' }), 'page/mypage/suffix', 'stringified page results in "page/mypage"');
t.deepEqual(page('page/suffix'), {}, 'parsed page returns empty object');
t.deepEqual(page('page/mypage/suffix'), { id: 'mypage' }, 'parsed page has "id" set to "mypage"');
t.equal(page(), 'page/suffix', 'stringified empty page results in "page"');
t.equal(page({ id: 'mypage' }), 'page/mypage/suffix', 'stringified page results in "page/mypage"');
t.end();
});
test('optional parameter with trailing slash', function(t) {
var page = docuri.route('(:id/)page');
t.deepEqual(page('page'), {}, 'parsed page returns empty object');
t.deepEqual(page('mypage/page'), { id: 'mypage' }, 'parsed page has "id" set to "mypage"');
t.equal(page(), 'page', 'stringified empty page results in "page"');
t.equal(page({ id: 'mypage' }), 'mypage/page', 'stringified page results in "mypage/page"');
t.end();
});

@ -1,26 +0,0 @@
var test = require('tap').test;
var docuri = require('..');
test('reserved name route', function(t) {
t.plan(1);
try {
docuri.route('page', 'route');
} catch(e) {
t.equal(e, 'Reserved name "route" cannot be used.', 'reserved name "route" should throw error');
}
t.end();
});
test('reserved name routes', function(t) {
t.plan(1);
try {
docuri.route('page', 'routes');
} catch(e) {
t.equal(e, 'Reserved name "routes" cannot be used.', 'reserved name "routes" should throw error');
}
t.end();
});

@ -2,10 +2,10 @@ var test = require('tap').test;
var docuri = require('..');
test('splat and named parameter', function(t) {
docuri.route('*splat/page/:named', 'page');
var page = docuri.route('*splat/page/:named');
t.deepEqual(docuri.page('splat/page/named'), { splat: [ 'splat' ], named: 'named' }, 'splat and named parameter parsed correctly');
t.equal(docuri.page({ splat: [ 'splat' ], named: 'named' }), 'splat/page/named', 'stringified result is in right order');
t.deepEqual(page('splat/page/named'), { splat: [ 'splat' ], named: 'named' }, 'splat and named parameter parsed correctly');
t.equal(page({ splat: [ 'splat' ], named: 'named' }), 'splat/page/named', 'stringified result is in right order');
t.end();
});

@ -2,10 +2,10 @@ var test = require('tap').test;
var docuri = require('..');
test('splat parameter', function(t) {
docuri.route('page/*path', 'page');
var page = docuri.route('page/*path');
t.deepEqual(docuri.page('page/mypage/otherpage'), { path: ['mypage', 'otherpage'] }, 'parsed page has "parts" set to ["mypage","otherpage"]');
t.equal(docuri.page({ path: ['mypage', 'otherpage'] }), 'page/mypage/otherpage', 'stringified page results in "page/mypage/otherpage"');
t.deepEqual(page('page/mypage/otherpage'), { path: ['mypage', 'otherpage'] }, 'parsed page has "parts" set to ["mypage","otherpage"]');
t.equal(page({ path: ['mypage', 'otherpage'] }), 'page/mypage/otherpage', 'stringified page results in "page/mypage/otherpage"');
t.end();
});

Loading…
Cancel
Save