diff --git a/README.md b/README.md index aa03199..87bceb0 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ Docuris have many advantages: Give Docuris a try! ## Usage + +### `parse(string)` Parse id string: ```js var docuri = require('docuri'); @@ -32,6 +34,7 @@ docuri.parse('mytype/myid/mysubtype/myindex/myversion'); // } ``` +### `stringify(object)` Build id string from object: ```js docuri.stringify({ @@ -44,6 +47,7 @@ docuri.stringify({ // 'mytype/myid/mysubtype/myindex/myversion' ``` +### `merge(objectOrString)` Change id string components: ```js docuri.merge('mytype/myid/mysubtype/myindex/myversion', { @@ -52,6 +56,25 @@ docuri.merge('mytype/myid/mysubtype/myindex/myversion', { // 'my_new_type/myid/mysubtype/myindex/myversion' ``` +### `parts(objectOrString)` +Array of components. Trailing `undefined` components are stripped off: +```js +docuri.parts('mytype/myid/'); +// ['mytype', 'myid'] +docuri.parts({ type: 'mytype', subtype: 'mysubtype' }); +// ['mytype', undefined, 'mysubtype'] +``` + +### `arity(objectOrString)` +Length of parts: +```js +docuri.arity('mytype/myid/'); +// 2 +docuri.arity({ type: 'mytype', subtype: 'mysubtype' }); +// 3 +``` + +### `docuri([definition])` Use custom definition: ```js docuri(); @@ -65,6 +88,8 @@ docuri.parse('42/answer'); // meta: 'answer' // } ``` +Note: `definition` MUST be an array of strings with at least one item. + ## Browser support To use docid in your client-side application, browserify it like this: diff --git a/index.js b/index.js index e6a8b75..67f29ae 100644 --- a/index.js +++ b/index.js @@ -6,25 +6,41 @@ */ // type/id/subtype/index/version -var PARTS = ['type', 'id', 'subtype', 'index', 'version']; +var DEFINITION = ['type', 'id', 'subtype', 'index', 'version']; -function docuri(parts) { - if (parts) { - PARTS = parts; +function docuri(definition) { + if (definition) { + DEFINITION = definition; return docuri; } - return PARTS; + return DEFINITION; } +docuri.parts = function(obj) { + if (typeof obj === 'string') { + obj = docuri.parse(obj); + } + + var parts = DEFINITION.map(function(part) { + return obj[part]; + }); + + while (parts.length && typeof parts[parts.length - 1] === 'undefined') { + parts.pop(); + } + + return parts; +}; + docuri.parse = function(str) { str = str || ''; return str.split('/').reduce(function(obj, value, i) { if (value) { - obj[PARTS[i]] = value; + obj[DEFINITION[i]] = value; } return obj; @@ -34,17 +50,17 @@ docuri.parse = function(str) { docuri.stringify = function(obj) { obj = obj || {}; - return PARTS.map(function(part) { - return typeof obj[part] === 'undefined' ? '' : obj[part]; - }).join('/').replace(/\/+$/, ''); + return docuri.parts(obj).join('/'); }; -docuri.merge = function(str, objToMerge) { +docuri.merge = function(obj, objToMerge) { objToMerge = objToMerge || {}; - var obj = docuri.parse(str); + if (typeof obj === 'string') { + obj = docuri.parse(obj); + } - PARTS.forEach(function(part) { + DEFINITION.forEach(function(part) { if (objToMerge[part]) { obj[part] = objToMerge[part]; } @@ -53,6 +69,14 @@ docuri.merge = function(str, objToMerge) { return docuri.stringify(obj); }; +docuri.arity = function(obj) { + if (typeof obj === 'string') { + obj = docuri.parse(obj); + } + + return docuri.parts(obj).length; +}; + module.exports = docuri; diff --git a/package.json b/package.json index 1a43555..9a9be9b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "docuri", - "version": "2.1.0", + "version": "2.2.0", "description": "Rich document ids for CouchDB", "main": "index.js", "scripts": { diff --git a/test/arity_test.js b/test/arity_test.js new file mode 100644 index 0000000..d8c4a06 --- /dev/null +++ b/test/arity_test.js @@ -0,0 +1,36 @@ +var test = require('tap').test; +var arity = require('..').arity; + +test('arity of type string', function(t) { + t.equal(arity('type'), 1, 'should return 1'); + t.end(); +}); +test('arity of type/id string', function(t) { + t.equal(arity('type/id'), 2, 'should return 2'); + t.end(); +}); +test('arity of type/id/subtype string', function(t) { + t.equal(arity('type/id/subtype'), 3, 'should return 3'); + t.end(); +}); +test('arity of type//subtype string', function(t) { + t.equal(arity('type//subtype'), 3, 'should return 3'); + t.end(); +}); + +test('arity of type object', function(t) { + t.equal(arity({ type: 'type' }), 1, 'should return 1'); + t.end(); +}); +test('arity of type/id object', function(t) { + t.equal(arity({ type: 'type', id: 'id'}), 2, 'should return 2'); + t.end(); +}); +test('arity of type/id/subtype object', function(t) { + t.equal(arity({ type: 'type', id: 'id', subtype: 'subtype' }), 3, 'should return 3'); + t.end(); +}); +test('arity of type//subtype object', function(t) { + t.equal(arity({ type: 'type', subtype: 'subtype' }), 3, 'should return 3'); + t.end(); +}); diff --git a/test/config_test.js b/test/config_test.js new file mode 100644 index 0000000..8badff8 --- /dev/null +++ b/test/config_test.js @@ -0,0 +1,28 @@ +var test = require('tap').test; +var docuri = require('..'); + +test('default configuration', function(t) { + t.deepEqual(docuri(), ['type', 'id', 'subtype', 'index', 'version'], 'should return default parts'); + t.end(); +}); + +test('set configuration', function(t) { + var parts = ['my', 'parts']; + t.type(docuri(parts).merge, 'function', 'should return docuri api: merge'); + t.type(docuri(parts).parse, 'function', 'should return docuri api: parse'); + t.type(docuri(parts).stringify, 'function', 'should return docuri api: stringify'); + t.end(); +}); + +test('change configuration', function(t) { + var parts = ['my', 'parts']; + docuri(parts); + t.deepEqual(docuri(), parts, 'should return custom parts'); + t.end(); +}); + +test('use changed configuration', function(t) { + var parts = ['my', 'parts']; + t.deepEqual(docuri(parts).parse('one/two'), { my: 'one', parts: 'two'}, 'should use custom parts'); + t.end(); +}); diff --git a/test/merge_test.js b/test/merge_test.js index 989522e..4386968 100644 --- a/test/merge_test.js +++ b/test/merge_test.js @@ -38,3 +38,14 @@ test('missing second argument', function(t) { }); +test('changing type component using object', function(t) { + t.equal(merge({ + type: 'type', + id: 'id', + subtype: 'subtype', + index: 'index', + version: 'version' + }, {type:'new_type'}), 'new_type/id/subtype/index/version', 'should return docuri string with type changed'); + t.end(); +}); + diff --git a/test/parts_test.js b/test/parts_test.js index 8badff8..b88bd7b 100644 --- a/test/parts_test.js +++ b/test/parts_test.js @@ -1,28 +1,36 @@ var test = require('tap').test; -var docuri = require('..'); +var parts = require('..').parts; -test('default configuration', function(t) { - t.deepEqual(docuri(), ['type', 'id', 'subtype', 'index', 'version'], 'should return default parts'); +test('parts of type string', function(t) { + t.deepEqual(parts('type'), ['type'], 'should return array including type'); t.end(); }); - -test('set configuration', function(t) { - var parts = ['my', 'parts']; - t.type(docuri(parts).merge, 'function', 'should return docuri api: merge'); - t.type(docuri(parts).parse, 'function', 'should return docuri api: parse'); - t.type(docuri(parts).stringify, 'function', 'should return docuri api: stringify'); +test('parts of type/id string', function(t) { + t.deepEqual(parts('type/id'), ['type', 'id'], 'should return array including type and id'); t.end(); }); - -test('change configuration', function(t) { - var parts = ['my', 'parts']; - docuri(parts); - t.deepEqual(docuri(), parts, 'should return custom parts'); +test('parts of type/id/subtype string', function(t) { + t.deepEqual(parts('type/id/subtype'), ['type', 'id', 'subtype'], 'should return array including type, id and subtype'); + t.end(); +}); +test('parts of type//subtype string', function(t) { + t.deepEqual(parts('type//subtype'), ['type', undefined, 'subtype'], 'should return array including type, undefined and subtype'); t.end(); }); -test('use changed configuration', function(t) { - var parts = ['my', 'parts']; - t.deepEqual(docuri(parts).parse('one/two'), { my: 'one', parts: 'two'}, 'should use custom parts'); +test('parts of type object', function(t) { + t.deepEqual(parts({ type: 'type' }), ['type'], 'should return array including type'); + t.end(); +}); +test('parts of type/id object', function(t) { + t.deepEqual(parts({ type: 'type', id: 'id'}), ['type', 'id'], 'should return array including type and id'); + t.end(); +}); +test('parts of type/id/subtype object', function(t) { + t.deepEqual(parts({ type: 'type', id: 'id', subtype: 'subtype' }), ['type', 'id', 'subtype'], 'should return array including type, id and subtype'); + t.end(); +}); +test('parts of type//subtype object', function(t) { + t.deepEqual(parts({ type: 'type', subtype: 'subtype' }), ['type', undefined, 'subtype'], 'should return array including type, undefined and subtype'); t.end(); });