From ed801a9cbbe7bc44098eff35f75526b8819d4422 Mon Sep 17 00:00:00 2001 From: dsc Date: Tue, 7 Aug 2012 01:34:19 -0700 Subject: [PATCH] Initial commit. --- .gitignore | 29 ++++++++++++ README.md | 97 ++++++++++++++++++++++++++++++++++++++++ index.js | 1 + package.co | 21 +++++++++ package.json | 35 +++++++++++++++ underscore.kv.co | 97 ++++++++++++++++++++++++++++++++++++++++ underscore.kv.js | 112 +++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 392 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 index.js create mode 100644 package.co create mode 100644 package.json create mode 100644 underscore.kv.co create mode 100644 underscore.kv.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b3bb404 --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +# git-ls-files --others --exclude-from=.git/info/exclude +*~ + +# OSX +.VolumeIcon.icns +.fseventsd +.Spotlight-V100 +.Trashes +.DS_Store +._* +Icon? + +# Node +node_modules/ +node_modules/* +npm-debug.log +test/*.log +test/fixtures/*.json +test/fixtures/logs/*.log +lib-cov/ +*.sass-cache +.cache/ +.tmp/ + +build/ +dist/ +var/ +log/ +tmp/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..c4a83f9 --- /dev/null +++ b/README.md @@ -0,0 +1,97 @@ +# underscore.kv + +Key-value pairs serialization (aka, `www-form-encoding`) of objects for [Underscore.js][underscore]. + + +## Usage + +For usage in [node.js][node], install it via [npm][npm]: `npm install underscore.kv`. + +You can use `underscore.kv` as a stand-alone library, though it depends on Underscore. +Most people mix it into the Underscore namespace, which gains you the chaining wrappers. + +```js +// standalone +var _kv = require('underscore.kv'); + +// mixin +var _ = require('underscore'); +_.mixin require('underscore.kv'); +``` + + +## API + +### _.**toKV**(*object*[, *item_delim*='&'[, *kv_delim*='=']]) + +Transforms an object to a string of URL-encoded KV-pairs (aka "www-form-encoding"). +You may optionally override the delimiter inserted between items (`&` by default), +or the delimiter inserted between keys and values (`=`). + +Note: +- All values end up as a string, implying all type information is lost. +- Both keys and values are URL-encoded once. + +```js +_.toKV({ "foo":"bar", "feh":1, "lol":true }) +// --> "foo=bar&feh=1&lol=true" +``` + + +### _.**fromKV**(*string*[, *item_delim*=`'&'`[, *kv_delim*=`'='`]]) + +Restores an object from a string of URL-encoded KV-pairs (aka "www-form-encoding"). +You may optionally override the delimiter inserted between items (`&` by default), +or the delimiter inserted between keys and values (`=`). + +Note: +- All resulting values will be strings as all type information is lost. +- Both keys and values will be URL-decoded once. + +```js +_.fromKV("foo=bar&feh=1&lol=true") +// --> { "foo":"bar", "feh":"1", "lol":"true" } +``` + + +### _.**collapseObject**(*source*[, *target*=`{}`[, *prefix*=`''`]]) + +Copies and flattens a tree of sub-objects into namespaced keys on the target object. + +```js +_.collapseObject({ "foo":{ "bar":1 } }) +// --> { "foo.bar":1 } +``` + + +### _.**uncollapseObject**(*source*[, *target*=`{}`]) + +Inverse of `_.collapseObject()` — copies all keys onto the target object, +expanding any dot-namespaced keys found. + +```js +_.uncollapseObject({ "foo.bar":1 }) +// --> { "foo":{ "bar":1 } } +``` + + + +## Feedback + +Find a bug or want to contribute? Open a ticket (or fork the source!) on [github][project]. +You're also welcome to send me email at [dsc@less.ly][dsc_email]. + +-- + +`underscore.kv` was written by [David Schoonover][dsc]; it is open-source software and freely available under the [MIT License][mit_license]. + + + +[project]: http://github.com/dsc/underscore.kv "underscore.kv on GitHub" +[dsc]: https://github.com/dsc/ "David Schoonover" +[dsc_email]: mailto:dsc+underscore.kv@less.ly?subject=underscore.kv "dsc@less.ly" +[mit_license]: http://dsc.mit-license.org/ "MIT License" + +[node]: http://nodejs.org/ "node.js" +[npm]: http://npmjs.org/ "npm" +[underscore]: http://underscorejs.org "Underscore.js" diff --git a/index.js b/index.js new file mode 100644 index 0000000..595ea56 --- /dev/null +++ b/index.js @@ -0,0 +1 @@ +module.exports = require('./underscore.kv'); diff --git a/package.co b/package.co new file mode 100644 index 0000000..ed683c9 --- /dev/null +++ b/package.co @@ -0,0 +1,21 @@ +name : 'underscore.kv' +version : '0.1.0' +description : 'Key-value pairs serialization (aka "www-form-encoding") of objects for Underscore.js' +homepage : 'https://github.com/dsc/underscore.kv' +keywords : <[ underscore kv key value pairs serialization util server client browser ]> +author : 'David Schoonover (http://less.ly)' + +main : "./underscore.kv" + +dependencies : + 'underscore' : '== 1.3.x' + 'underscore.nested' : '== 0.1.x' +devDependencies : + 'coco' : '== 0.7.x' +# 'expresso' : '== 0.9.2' + +# scripts : test:'expresso' +repository : type:'git', url:'https://github.com/dsc/underscore.kv.git' +bugs : url:'https://github.com/dsc/underscore.kv/issues' + +license : 'MIT' diff --git a/package.json b/package.json new file mode 100644 index 0000000..2ac7fba --- /dev/null +++ b/package.json @@ -0,0 +1,35 @@ +{ + "name": "underscore.kv", + "version": "0.1.0", + "description": "Key-value pairs serialization (aka \"www-form-encoding\") of objects for Underscore.js", + "homepage": "https://github.com/dsc/underscore.kv", + "keywords": [ + "underscore", + "kv", + "key", + "value", + "pairs", + "serialization", + "util", + "server", + "client", + "browser" + ], + "author": "David Schoonover (http://less.ly)", + "main": "./underscore.kv", + "dependencies": { + "underscore": "== 1.3.x", + "underscore.nested": "== 0.1.x" + }, + "devDependencies": { + "coco": "== 0.7.x" + }, + "repository": { + "type": "git", + "url": "https://github.com/dsc/underscore.kv.git" + }, + "bugs": { + "url": "https://github.com/dsc/underscore.kv/issues" + }, + "license": "MIT" +} diff --git a/underscore.kv.co b/underscore.kv.co new file mode 100644 index 0000000..51d2417 --- /dev/null +++ b/underscore.kv.co @@ -0,0 +1,97 @@ +_ = require 'underscore' +_nest = require 'underscore.nested' + + +/** + * @namespace Functions for key-value pairs serialization (aka, www-form-encoding) of objects + */ +_kv = do + + /** + * Transforms an object to a string of URL-encoded KV-pairs (aka "www-form-encoding"). + * + * Note: + * - All values end up as a string, implying all type information is lost. + * - Both keys and values are URL-encoded once. + * + * @param {Object} object The object to be serialized. + * @param {String} [item_delim='&'] String delimiting each pair. + * @param {String} [kv_delim='='] String delimiting key from value. + * @returns {String} Serialized and encoded KV-pairs. + */ + toKV: (object, item_delim='&', kv_delim='=') -> + _.reduce do + object + (acc, v, k) -> + acc.push encodeURIComponent(k) + kv_delim + encodeURIComponent(v) if k + acc + [] + .join item_delim + + /** + * Restores an object from a string of URL-encoded KV-pairs (aka "www-form-encoding"). + * + * Note: + * - All resulting values will be strings as all type information is lost. + * - Both keys and values will be URL-decoded once. + * + * @param {String} string String of serialized KV-pairs. + * @param {String} [item_delim='&'] String delimiting each pair. + * @param {String} [kv_delim='='] String delimiting key from value. + * @returns {Object} Deserialized object containing the KV-pairs. + */ + fromKV: (string, item_delim='&', kv_delim='=') -> + _.reduce do + string.split item_delim + (acc, pair) -> + idx = pair.indexOf kv_delim + if idx is not -1 + [k, v] = [pair.slice(0, idx), pair.slice(idx+1)] + else + [k, v] = [pair, ''] + acc[ decodeURIComponent k ] = decodeURIComponent v if k + acc + {} + + /** + * Copies and flattens a tree of sub-objects into namespaced keys on the target object, such + * that `{ "foo":{ "bar":1 } }` becomes `{ "foo.bar":1 }`. + * + * @param {Object} source Object to collapse. + * @param {Object} [target={}] Target of the collapsed keys. + * @param {String} [prefix=''] Prefix applied to copied keys. + * @returns {Object} The collapsed object. + */ + collapseObject: (source, target={}, prefix='') -> + prefix += '.' if prefix + _.each source, (v, k) -> + if _nest.isPlainObject v + _kv.collapseObject v, parent, prefix+k + else + parent[prefix+k] = v + parent + + /** + * Inverse of `_.collapseObject()` -- copies all keys onto the target object, expanding any + * dot-namespaced keys found, such that `{ "foo.bar":1 }` becomes `{ "foo":{ "bar":1 }}`. + * + * @param {Object} source Collapsed source object. + * @param {Object} [target={}] Target of the uncollapsed keys. + * @returns {Object} The uncollapsed object -- either `target` or a new object. + */ + uncollapseObject: (source, target={}) -> + _.reduce do + source + (acc, v, k) -> + _nest.setNested acc, k, v, {+ensure} + acc + target + + +if module? + module.exports = _kv +else if exports? + exports import _kv +else if window? + window.UnderscoreKV = _kv + diff --git a/underscore.kv.js b/underscore.kv.js new file mode 100644 index 0000000..35c2a86 --- /dev/null +++ b/underscore.kv.js @@ -0,0 +1,112 @@ +var _, _nest, _kv; +_ = require('underscore'); +_nest = require('underscore.nested'); +/** + * @namespace Functions for key-value pairs serialization (aka, www-form-encoding) of objects + */ +_kv = { + /** + * Transforms an object to a string of URL-encoded KV-pairs (aka "www-form-encoding"). + * + * Note: + * - All values end up as a string, implying all type information is lost. + * - Both keys and values are URL-encoded once. + * + * @param {Object} object The object to be serialized. + * @param {String} [item_delim='&'] String delimiting each pair. + * @param {String} [kv_delim='='] String delimiting key from value. + * @returns {String} Serialized and encoded KV-pairs. + */ + toKV: function(object, item_delim, kv_delim){ + item_delim == null && (item_delim = '&'); + kv_delim == null && (kv_delim = '='); + return _.reduce(object, function(acc, v, k){ + if (k) { + acc.push(encodeURIComponent(k) + kv_delim + encodeURIComponent(v)); + } + return acc; + }, []).join(item_delim); + } + /** + * Restores an object from a string of URL-encoded KV-pairs (aka "www-form-encoding"). + * + * Note: + * - All resulting values will be strings as all type information is lost. + * - Both keys and values will be URL-decoded once. + * + * @param {String} string String of serialized KV-pairs. + * @param {String} [item_delim='&'] String delimiting each pair. + * @param {String} [kv_delim='='] String delimiting key from value. + * @returns {Object} Deserialized object containing the KV-pairs. + */, + fromKV: function(string, item_delim, kv_delim){ + item_delim == null && (item_delim = '&'); + kv_delim == null && (kv_delim = '='); + return _.reduce(string.split(item_delim), function(acc, pair){ + var idx, k, v, __ref; + idx = pair.indexOf(kv_delim); + if (idx !== -1) { + __ref = [pair.slice(0, idx), pair.slice(idx + 1)], k = __ref[0], v = __ref[1]; + } else { + __ref = [pair, ''], k = __ref[0], v = __ref[1]; + } + if (k) { + acc[decodeURIComponent(k)] = decodeURIComponent(v); + } + return acc; + }, {}); + } + /** + * Copies and flattens a tree of sub-objects into namespaced keys on the target object, such + * that `{ "foo":{ "bar":1 } }` becomes `{ "foo.bar":1 }`. + * + * @param {Object} source Object to collapse. + * @param {Object} [target={}] Target of the collapsed keys. + * @param {String} [prefix=''] Prefix applied to copied keys. + * @returns {Object} The collapsed object. + */, + collapseObject: function(source, target, prefix){ + target == null && (target = {}); + prefix == null && (prefix = ''); + if (prefix) { + prefix += '.'; + } + _.each(source, function(v, k){ + if (_nest.isPlainObject(v)) { + return _kv.collapseObject(v, parent, prefix + k); + } else { + return parent[prefix + k] = v; + } + }); + return parent; + } + /** + * Inverse of `_.collapseObject()` -- copies all keys onto the target object, expanding any + * dot-namespaced keys found, such that `{ "foo.bar":1 }` becomes `{ "foo":{ "bar":1 }}`. + * + * @param {Object} source Collapsed source object. + * @param {Object} [target={}] Target of the uncollapsed keys. + * @returns {Object} The uncollapsed object -- either `target` or a new object. + */, + uncollapseObject: function(source, target){ + target == null && (target = {}); + return _.reduce(source, function(acc, v, k){ + _nest.setNested(acc, k, v, { + ensure: true + }); + return acc; + }, target); + } +}; +if (typeof module != 'undefined' && module !== null) { + module.exports = _kv; +} else if (typeof exports != 'undefined' && exports !== null) { + __import(exports, _kv); +} else if (typeof window != 'undefined' && window !== null) { + window.UnderscoreKV = _kv; +} +function __import(obj, src){ + var own = {}.hasOwnProperty; + for (var key in src) if (own.call(src, key)) obj[key] = src[key]; + return obj; +}