summaryrefslogtreecommitdiffstats
path: root/toolkit/jetpack/diffpatcher
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/jetpack/diffpatcher')
-rw-r--r--toolkit/jetpack/diffpatcher/.travis.yml5
-rw-r--r--toolkit/jetpack/diffpatcher/History.md14
-rw-r--r--toolkit/jetpack/diffpatcher/License.md18
-rw-r--r--toolkit/jetpack/diffpatcher/Readme.md70
-rw-r--r--toolkit/jetpack/diffpatcher/diff.js45
-rw-r--r--toolkit/jetpack/diffpatcher/index.js5
-rw-r--r--toolkit/jetpack/diffpatcher/package.json54
-rw-r--r--toolkit/jetpack/diffpatcher/patch.js21
-rw-r--r--toolkit/jetpack/diffpatcher/rebase.js36
-rw-r--r--toolkit/jetpack/diffpatcher/test/common.js3
-rw-r--r--toolkit/jetpack/diffpatcher/test/diff.js59
-rw-r--r--toolkit/jetpack/diffpatcher/test/index.js14
-rw-r--r--toolkit/jetpack/diffpatcher/test/patch.js83
-rw-r--r--toolkit/jetpack/diffpatcher/test/tap.js3
14 files changed, 430 insertions, 0 deletions
diff --git a/toolkit/jetpack/diffpatcher/.travis.yml b/toolkit/jetpack/diffpatcher/.travis.yml
new file mode 100644
index 000000000..780731a47
--- /dev/null
+++ b/toolkit/jetpack/diffpatcher/.travis.yml
@@ -0,0 +1,5 @@
+language: node_js
+node_js:
+ - 0.4
+ - 0.5
+ - 0.6
diff --git a/toolkit/jetpack/diffpatcher/History.md b/toolkit/jetpack/diffpatcher/History.md
new file mode 100644
index 000000000..d38978805
--- /dev/null
+++ b/toolkit/jetpack/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/toolkit/jetpack/diffpatcher/License.md b/toolkit/jetpack/diffpatcher/License.md
new file mode 100644
index 000000000..ed76489a3
--- /dev/null
+++ b/toolkit/jetpack/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/toolkit/jetpack/diffpatcher/Readme.md b/toolkit/jetpack/diffpatcher/Readme.md
new file mode 100644
index 000000000..1520b1c37
--- /dev/null
+++ b/toolkit/jetpack/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/toolkit/jetpack/diffpatcher/diff.js b/toolkit/jetpack/diffpatcher/diff.js
new file mode 100644
index 000000000..967c137e6
--- /dev/null
+++ b/toolkit/jetpack/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/toolkit/jetpack/diffpatcher/index.js b/toolkit/jetpack/diffpatcher/index.js
new file mode 100644
index 000000000..91ddba425
--- /dev/null
+++ b/toolkit/jetpack/diffpatcher/index.js
@@ -0,0 +1,5 @@
+"use strict";
+
+exports.diff = require("./diff")
+exports.patch = require("./patch")
+exports.rebase = require("./rebase")
diff --git a/toolkit/jetpack/diffpatcher/package.json b/toolkit/jetpack/diffpatcher/package.json
new file mode 100644
index 000000000..54e085d2e
--- /dev/null
+++ b/toolkit/jetpack/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/toolkit/jetpack/diffpatcher/patch.js b/toolkit/jetpack/diffpatcher/patch.js
new file mode 100644
index 000000000..9271e8893
--- /dev/null
+++ b/toolkit/jetpack/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/toolkit/jetpack/diffpatcher/rebase.js b/toolkit/jetpack/diffpatcher/rebase.js
new file mode 100644
index 000000000..03c756fee
--- /dev/null
+++ b/toolkit/jetpack/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/toolkit/jetpack/diffpatcher/test/common.js b/toolkit/jetpack/diffpatcher/test/common.js
new file mode 100644
index 000000000..dbc79013c
--- /dev/null
+++ b/toolkit/jetpack/diffpatcher/test/common.js
@@ -0,0 +1,3 @@
+"use strict";
+
+require("test").run(require("./index"))
diff --git a/toolkit/jetpack/diffpatcher/test/diff.js b/toolkit/jetpack/diffpatcher/test/diff.js
new file mode 100644
index 000000000..d1d674005
--- /dev/null
+++ b/toolkit/jetpack/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/toolkit/jetpack/diffpatcher/test/index.js b/toolkit/jetpack/diffpatcher/test/index.js
new file mode 100644
index 000000000..c06407e7c
--- /dev/null
+++ b/toolkit/jetpack/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/toolkit/jetpack/diffpatcher/test/patch.js b/toolkit/jetpack/diffpatcher/test/patch.js
new file mode 100644
index 000000000..dc2e38229
--- /dev/null
+++ b/toolkit/jetpack/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/toolkit/jetpack/diffpatcher/test/tap.js b/toolkit/jetpack/diffpatcher/test/tap.js
new file mode 100644
index 000000000..e550b82f5
--- /dev/null
+++ b/toolkit/jetpack/diffpatcher/test/tap.js
@@ -0,0 +1,3 @@
+"use strict";
+
+require("retape")(require("./index"))