diff options
Diffstat (limited to 'addon-sdk/source/lib/diffpatcher')
-rw-r--r-- | addon-sdk/source/lib/diffpatcher/.travis.yml | 5 | ||||
-rw-r--r-- | addon-sdk/source/lib/diffpatcher/History.md | 14 | ||||
-rw-r--r-- | addon-sdk/source/lib/diffpatcher/License.md | 18 | ||||
-rw-r--r-- | addon-sdk/source/lib/diffpatcher/Readme.md | 70 | ||||
-rw-r--r-- | addon-sdk/source/lib/diffpatcher/diff.js | 45 | ||||
-rw-r--r-- | addon-sdk/source/lib/diffpatcher/index.js | 5 | ||||
-rw-r--r-- | addon-sdk/source/lib/diffpatcher/package.json | 54 | ||||
-rw-r--r-- | addon-sdk/source/lib/diffpatcher/patch.js | 21 | ||||
-rw-r--r-- | addon-sdk/source/lib/diffpatcher/rebase.js | 36 | ||||
-rw-r--r-- | addon-sdk/source/lib/diffpatcher/test/common.js | 3 | ||||
-rw-r--r-- | addon-sdk/source/lib/diffpatcher/test/diff.js | 59 | ||||
-rw-r--r-- | addon-sdk/source/lib/diffpatcher/test/index.js | 14 | ||||
-rw-r--r-- | addon-sdk/source/lib/diffpatcher/test/patch.js | 83 | ||||
-rw-r--r-- | addon-sdk/source/lib/diffpatcher/test/tap.js | 3 |
14 files changed, 430 insertions, 0 deletions
diff --git a/addon-sdk/source/lib/diffpatcher/.travis.yml b/addon-sdk/source/lib/diffpatcher/.travis.yml new file mode 100644 index 000000000..780731a47 --- /dev/null +++ b/addon-sdk/source/lib/diffpatcher/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - 0.4 + - 0.5 + - 0.6 diff --git a/addon-sdk/source/lib/diffpatcher/History.md b/addon-sdk/source/lib/diffpatcher/History.md new file mode 100644 index 000000000..d38978805 --- /dev/null +++ b/addon-sdk/source/lib/diffpatcher/History.md @@ -0,0 +1,14 @@ +# Changes + +## 1.0.1 / 2013-05-01 + + - Update method library version. + +## 1.0.0 / 2012-11-09 + + - Test integration for browsers. + - New method library. + +## 0.0.1 / 2012-10-22 + + - Initial release diff --git a/addon-sdk/source/lib/diffpatcher/License.md b/addon-sdk/source/lib/diffpatcher/License.md new file mode 100644 index 000000000..ed76489a3 --- /dev/null +++ b/addon-sdk/source/lib/diffpatcher/License.md @@ -0,0 +1,18 @@ +Copyright 2012 Irakli Gozalishvili. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/addon-sdk/source/lib/diffpatcher/Readme.md b/addon-sdk/source/lib/diffpatcher/Readme.md new file mode 100644 index 000000000..1520b1c37 --- /dev/null +++ b/addon-sdk/source/lib/diffpatcher/Readme.md @@ -0,0 +1,70 @@ +# diffpatcher + +[![Build Status](https://secure.travis-ci.org/Gozala/diffpatcher.png)](http://travis-ci.org/Gozala/diffpatcher) + +[![Browser support](https://ci.testling.com/Gozala/diffpatcher.png)](http://ci.testling.com/Gozala/diffpatcher) + +Diffpatcher is a small library that lets you treat hashes as if they were +git repositories. + +## diff + +Diff function that takes two hashes and returns delta hash. + +```js +var diff = require("diffpatcher/diff") + +diff({ a: { b: 1 }, c: { d: 2 } }, // hash#1 + { a: { e: 3 }, c: { d: 4 } }) // hash#2 + +// => { // delta +// a: { +// b: null, // - +// e: 3 // + +// }, +// c: { +// d: 4 // ± +// } +// } +``` + +As you can see from the example above `delta` makes no real distinction between +proprety upadate and property addition. Try to think of additions as an update +from `undefined` to whatever it's being updated to. + +## patch + +Patch fuction takes a `hash` and a `delta` and returns a new `hash` which is +just like orginial but with delta applied to it. Let's apply delta from the +previous example to the first hash from the same example + + +```js +var patch = require("diffpatcher/patch") + +patch({ a: { b: 1 }, c: { d: 2 } }, // hash#1 + { // delta + a: { + b: null, // - + e: 3 // + + }, + c: { + d: 4 // ± + } + }) + +// => { a: { e: 3 }, c: { d: 4 } } // hash#2 +``` + +That's about it really, just diffing hashes and applying thes diffs on them. + + +### rebase + +And as Linus mentioned everything in git can be expressed with `rebase`, that +also pretty much the case for `diffpatcher`. `rebase` takes `target` hash, +and rebases `parent` onto it with `diff` applied. + +## Install + + npm install diffpatcher diff --git a/addon-sdk/source/lib/diffpatcher/diff.js b/addon-sdk/source/lib/diffpatcher/diff.js new file mode 100644 index 000000000..967c137e6 --- /dev/null +++ b/addon-sdk/source/lib/diffpatcher/diff.js @@ -0,0 +1,45 @@ +"use strict"; + +var method = require("../method/core") + +// Method is designed to work with data structures representing application +// state. Calling it with a state should return object representing `delta` +// that has being applied to a previous state to get to a current state. +// +// Example +// +// diff(state) // => { "item-id-1": { title: "some title" } "item-id-2": null } +var diff = method("diff@diffpatcher") + +// diff between `null` / `undefined` to any hash is a hash itself. +diff.define(null, function(from, to) { return to }) +diff.define(undefined, function(from, to) { return to }) +diff.define(Object, function(from, to) { + return calculate(from, to || {}) || {} +}) + +function calculate(from, to) { + var diff = {} + var changes = 0 + Object.keys(from).forEach(function(key) { + changes = changes + 1 + if (!(key in to) && from[key] != null) diff[key] = null + else changes = changes - 1 + }) + Object.keys(to).forEach(function(key) { + changes = changes + 1 + var previous = from[key] + var current = to[key] + if (previous === current) return (changes = changes - 1) + if (typeof(current) !== "object") return diff[key] = current + if (typeof(previous) !== "object") return diff[key] = current + var delta = calculate(previous, current) + if (delta) diff[key] = delta + else changes = changes - 1 + }) + return changes ? diff : null +} + +diff.calculate = calculate + +module.exports = diff diff --git a/addon-sdk/source/lib/diffpatcher/index.js b/addon-sdk/source/lib/diffpatcher/index.js new file mode 100644 index 000000000..91ddba425 --- /dev/null +++ b/addon-sdk/source/lib/diffpatcher/index.js @@ -0,0 +1,5 @@ +"use strict"; + +exports.diff = require("./diff") +exports.patch = require("./patch") +exports.rebase = require("./rebase") diff --git a/addon-sdk/source/lib/diffpatcher/package.json b/addon-sdk/source/lib/diffpatcher/package.json new file mode 100644 index 000000000..54e085d2e --- /dev/null +++ b/addon-sdk/source/lib/diffpatcher/package.json @@ -0,0 +1,54 @@ +{ + "name": "diffpatcher", + "id": "diffpatcher", + "version": "1.2.0", + "description": "Utilities for diff-ing & patch-ing hashes", + "keywords": [ + "diff", "patch", "rebase", "hash", "changes", "versions" + ], + "author": "Irakli Gozalishvili <rfobic@gmail.com> (http://jeditoolkit.com)", + "homepage": "https://github.com/Gozala/diffpatcher", + "repository": { + "type": "git", + "url": "https://github.com/Gozala/diffpatcher.git", + "web": "https://github.com/Gozala/diffpatcher" + }, + "bugs": { + "url": "http://github.com/Gozala/diffpatcher/issues/" + }, + "dependencies": { + "method": "~2.0.0" + }, + "devDependencies": { + "test": "~0.x.0", + "phantomify": "~0.x.0", + "retape": "~0.x.0", + "tape": "~0.1.5" + }, + "main": "./index.js", + "scripts": { + "test": "npm run test-node && npm run test-browser", + "test-browser": "node ./node_modules/phantomify/bin/cmd.js ./test/common.js", + "test-node": "node ./test/common.js", + "test-tap": "node ./test/tap.js" + }, + "testling": { + "files": "test/tap.js", + "browsers": [ + "ie/9..latest", + "chrome/25..latest", + "firefox/20..latest", + "safari/6..latest", + "opera/11.0..latest", + "iphone/6..latest", + "ipad/6..latest", + "android-browser/4.2..latest" + ] + }, + "licenses": [ + { + "type": "MIT", + "url": "https://github.com/Gozala/diffpatcher/License.md" + } + ] +} diff --git a/addon-sdk/source/lib/diffpatcher/patch.js b/addon-sdk/source/lib/diffpatcher/patch.js new file mode 100644 index 000000000..9271e8893 --- /dev/null +++ b/addon-sdk/source/lib/diffpatcher/patch.js @@ -0,0 +1,21 @@ +"use strict"; + +var method = require("../method/core") +var rebase = require("./rebase") + +// Method is designed to work with data structures representing application +// state. Calling it with a state and delta should return object representing +// new state, with changes in `delta` being applied to previous. +// +// ## Example +// +// patch(state, { +// "item-id-1": { completed: false }, // update +// "item-id-2": null // delete +// }) +var patch = method("patch@diffpatcher") +patch.define(Object, function patch(hash, delta) { + return rebase({}, hash, delta) +}) + +module.exports = patch diff --git a/addon-sdk/source/lib/diffpatcher/rebase.js b/addon-sdk/source/lib/diffpatcher/rebase.js new file mode 100644 index 000000000..03c756fee --- /dev/null +++ b/addon-sdk/source/lib/diffpatcher/rebase.js @@ -0,0 +1,36 @@ +"use strict"; + +var nil = {} +var owns = ({}).hasOwnProperty + +function rebase(result, parent, delta) { + var key, current, previous, update + for (key in parent) { + if (owns.call(parent, key)) { + previous = parent[key] + update = owns.call(delta, key) ? delta[key] : nil + if (previous === null) continue + else if (previous === void(0)) continue + else if (update === null) continue + else if (update === void(0)) continue + else result[key] = previous + } + } + for (key in delta) { + if (owns.call(delta, key)) { + update = delta[key] + current = owns.call(result, key) ? result[key] : nil + if (current === update) continue + else if (update === null) continue + else if (update === void(0)) continue + else if (current === nil) result[key] = update + else if (typeof(update) !== "object") result[key] = update + else if (typeof(current) !== "object") result[key] = update + else result[key]= rebase({}, current, update) + } + } + + return result +} + +module.exports = rebase diff --git a/addon-sdk/source/lib/diffpatcher/test/common.js b/addon-sdk/source/lib/diffpatcher/test/common.js new file mode 100644 index 000000000..dbc79013c --- /dev/null +++ b/addon-sdk/source/lib/diffpatcher/test/common.js @@ -0,0 +1,3 @@ +"use strict"; + +require("test").run(require("./index")) diff --git a/addon-sdk/source/lib/diffpatcher/test/diff.js b/addon-sdk/source/lib/diffpatcher/test/diff.js new file mode 100644 index 000000000..d1d674005 --- /dev/null +++ b/addon-sdk/source/lib/diffpatcher/test/diff.js @@ -0,0 +1,59 @@ +"use strict"; + +var diff = require("../diff") + +exports["test diff from null"] = function(assert) { + var to = { a: 1, b: 2 } + assert.equal(diff(null, to), to, "diff null to x returns x") + assert.equal(diff(void(0), to), to, "diff undefined to x returns x") + +} + +exports["test diff to null"] = function(assert) { + var from = { a: 1, b: 2 } + assert.deepEqual(diff({ a: 1, b: 2 }, null), + { a: null, b: null }, + "diff x null returns x with all properties nullified") +} + +exports["test diff identical"] = function(assert) { + assert.deepEqual(diff({}, {}), {}, "diff on empty objects is {}") + + assert.deepEqual(diff({ a: 1, b: 2 }, { a: 1, b: 2 }), {}, + "if properties match diff is {}") + + assert.deepEqual(diff({ a: 1, b: { c: { d: 3, e: 4 } } }, + { a: 1, b: { c: { d: 3, e: 4 } } }), {}, + "diff between identical nested hashes is {}") + +} + +exports["test diff delete"] = function(assert) { + assert.deepEqual(diff({ a: 1, b: 2 }, { b: 2 }), { a: null }, + "missing property is deleted") + assert.deepEqual(diff({ a: 1, b: 2 }, { a: 2 }), { a: 2, b: null }, + "missing property is deleted another updated") + assert.deepEqual(diff({ a: 1, b: 2 }, {}), { a: null, b: null }, + "missing propertes are deleted") + assert.deepEqual(diff({ a: 1, b: { c: { d: 2 } } }, {}), + { a: null, b: null }, + "missing deep propertes are deleted") + assert.deepEqual(diff({ a: 1, b: { c: { d: 2 } } }, { b: { c: {} } }), + { a: null, b: { c: { d: null } } }, + "missing nested propertes are deleted") +} + +exports["test add update"] = function(assert) { + assert.deepEqual(diff({ a: 1, b: 2 }, { b: 2, c: 3 }), { a: null, c: 3 }, + "delete and add") + assert.deepEqual(diff({ a: 1, b: 2 }, { a: 2, c: 3 }), { a: 2, b: null, c: 3 }, + "delete and adds") + assert.deepEqual(diff({}, { a: 1, b: 2 }), { a: 1, b: 2 }, + "diff on empty objcet returns equivalen of to") + assert.deepEqual(diff({ a: 1, b: { c: { d: 2 } } }, { d: 3 }), + { a: null, b: null, d: 3 }, + "missing deep propertes are deleted") + assert.deepEqual(diff({ b: { c: {} }, d: null }, { a: 1, b: { c: { d: 2 } } }), + { a: 1, b: { c: { d: 2 } } }, + "missing nested propertes are deleted") +} diff --git a/addon-sdk/source/lib/diffpatcher/test/index.js b/addon-sdk/source/lib/diffpatcher/test/index.js new file mode 100644 index 000000000..c06407e7c --- /dev/null +++ b/addon-sdk/source/lib/diffpatcher/test/index.js @@ -0,0 +1,14 @@ +"use strict"; + +var diff = require("../diff") +var patch = require("../patch") + +exports["test diff"] = require("./diff") +exports["test patch"] = require("./patch") + +exports["test patch(a, diff(a, b)) => b"] = function(assert) { + var a = { a: { b: 1 }, c: { d: 2 } } + var b = { a: { e: 3 }, c: { d: 4 } } + + assert.deepEqual(patch(a, diff(a, b)), b, "patch(a, diff(a, b)) => b") +} diff --git a/addon-sdk/source/lib/diffpatcher/test/patch.js b/addon-sdk/source/lib/diffpatcher/test/patch.js new file mode 100644 index 000000000..dc2e38229 --- /dev/null +++ b/addon-sdk/source/lib/diffpatcher/test/patch.js @@ -0,0 +1,83 @@ +"use strict"; + +var patch = require("../patch") + +exports["test patch delete"] = function(assert) { + var hash = { a: 1, b: 2 } + + assert.deepEqual(patch(hash, { a: null }), { b: 2 }, "null removes property") +} + +exports["test patch delete with void"] = function(assert) { + var hash = { a: 1, b: 2 } + + assert.deepEqual(patch(hash, { a: void(0) }), { b: 2 }, + "void(0) removes property") +} + +exports["test patch delete missing"] = function(assert) { + assert.deepEqual(patch({ a: 1, b: 2 }, { c: null }), + { a: 1, b: 2 }, + "null removes property if exists"); + + assert.deepEqual(patch({ a: 1, b: 2 }, { c: void(0) }), + { a: 1, b: 2 }, + "void removes property if exists"); +} + +exports["test delete deleted"] = function(assert) { + assert.deepEqual(patch({ a: null, b: 2, c: 3, d: void(0)}, + { a: void(0), b: null, d: null }), + {c: 3}, + "removed all existing and non existing"); +} + +exports["test update deleted"] = function(assert) { + assert.deepEqual(patch({ a: null, b: void(0), c: 3}, + { a: { b: 2 } }), + { a: { b: 2 }, c: 3 }, + "replace deleted"); +} + +exports["test patch delete with void"] = function(assert) { + var hash = { a: 1, b: 2 } + + assert.deepEqual(patch(hash, { a: void(0) }), { b: 2 }, + "void(0) removes property") +} + + +exports["test patch addition"] = function(assert) { + var hash = { a: 1, b: 2 } + + assert.deepEqual(patch(hash, { c: 3 }), { a: 1, b: 2, c: 3 }, + "new properties are added") +} + +exports["test patch addition"] = function(assert) { + var hash = { a: 1, b: 2 } + + assert.deepEqual(patch(hash, { c: 3 }), { a: 1, b: 2, c: 3 }, + "new properties are added") +} + +exports["test hash on itself"] = function(assert) { + var hash = { a: 1, b: 2 } + + assert.deepEqual(patch(hash, hash), hash, + "applying hash to itself returns hash itself") +} + +exports["test patch with empty delta"] = function(assert) { + var hash = { a: 1, b: 2 } + + assert.deepEqual(patch(hash, {}), hash, + "applying empty delta results in no changes") +} + +exports["test patch nested data"] = function(assert) { + assert.deepEqual(patch({ a: { b: 1 }, c: { d: 2 } }, + { a: { b: null, e: 3 }, c: { d: 4 } }), + { a: { e: 3 }, c: { d: 4 } }, + "nested structures can also be patched") +} diff --git a/addon-sdk/source/lib/diffpatcher/test/tap.js b/addon-sdk/source/lib/diffpatcher/test/tap.js new file mode 100644 index 000000000..e550b82f5 --- /dev/null +++ b/addon-sdk/source/lib/diffpatcher/test/tap.js @@ -0,0 +1,3 @@ +"use strict"; + +require("retape")(require("./index")) |