From 16a9a9a385e519dc83f4556c44e67cf937d3a779 Mon Sep 17 00:00:00 2001 From: Giuseppe Davies Date: Mon, 10 Jun 2019 12:05:20 +0100 Subject: [PATCH 1/4] Update to the latest version of immutable js --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index c2774e7..daa8a62 100644 --- a/package.json +++ b/package.json @@ -16,9 +16,9 @@ ], "author": "Intelie", "license": "MIT", - "repository" : { - "type" : "git", - "url" : "http://github.com/intelie/immutable-js-diff.git" + "repository": { + "type": "git", + "url": "http://github.com/intelie/immutable-js-diff.git" }, "devDependencies": { "gulp": "^3.8.9", @@ -28,6 +28,6 @@ "jsondiff": "brenoferreira/json-diff" }, "dependencies": { - "immutable": "^3.2.1" + "immutable": "^4.0.0-rc.12" } } From ce304e13d1390d2ce2536e369a785bda591cbe6d Mon Sep 17 00:00:00 2001 From: Giuseppe Davies Date: Mon, 10 Jun 2019 12:06:05 +0100 Subject: [PATCH 2/4] New unit tests for record diffing --- tests/RecordDiff.test.js | 73 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 tests/RecordDiff.test.js diff --git a/tests/RecordDiff.test.js b/tests/RecordDiff.test.js new file mode 100644 index 0000000..40c0088 --- /dev/null +++ b/tests/RecordDiff.test.js @@ -0,0 +1,73 @@ +'use strict'; + +var diff = require('../src/diff'); +var Immutable = require('immutable'); +var assert = require('assert'); + +describe('Record diffs', function(){ + it('diffs simple records', function(){ + var simpleRecord = Immutable.Record({ a: 1, b: 'hello', c: true }); + var a = simpleRecord(); + var b = simpleRecord({ b: 'dear', c: false }); + + var expected = Immutable.fromJS([ + {op: 'replace', path: '/b', value: 'dear'}, + {op: 'replace', path: '/c', value: false}, + ]); + + var result = diff(a, b); + + return assert.ok(Immutable.is(result, expected)); + }); + + it('diff complex records', function() { + var complexRecord = Immutable.Record({ + a: Immutable.Map({ names: Immutable.Map({ first: 'John', second: 'Smith' }), age: 21 }), + b: Immutable.List([1, 2, 3, 'hi', Immutable.Map({ greeting: 'hello' })]) + }); + + var a = complexRecord(); + var b = complexRecord({ + a: Immutable.Map({ names: Immutable.Map({ first: 'Jane', second: 'Smith' }), age: 21 }), + b: Immutable.List([1, 2, 6, 'hi', Immutable.Map({ greeting: 'there' }) ]) + }); + + // Remove the age property from b + b = b.removeIn(['a', 'age']) + + var expected = Immutable.fromJS([ + {op: 'replace', path: '/a/names/first', value: 'Jane'}, + {op: 'remove', path: '/a/age'}, + {op: 'replace', path: '/b/2', value: 6}, + {op: 'replace', path: '/b/4/greeting', value: 'there'}, + ]); + + var result = diff(a, b); + + return assert.ok(Immutable.is(result, expected)); + }); + + it('diffs complex nested records', function(){ + + var namesRecord = Immutable.Record({ first: 'Bob', second: 'Smith' }); + var bioRecord = Immutable.Record({ age: 0, nationality: 'en', hobbies: Immutable.List([]) }); + var personRecord = Immutable.Record({ names: namesRecord(), info: bioRecord() }); + + const a = personRecord({ info: bioRecord({ hobbies: Immutable.List(['football']) }) }); + const b = personRecord({ + names: namesRecord({ first: 'Jim', second: 'Bob' }), + info: bioRecord({ age: 12, nationality: 'it', hobbies: Immutable.List(['football', 'chess']) }) + }) + + var expected = Immutable.fromJS([ + {op: 'replace', path: '/names/first', value: 'Jim'}, + {op: 'replace', path: '/names/second', value: 'Bob'}, + {op: 'replace', path: '/info/age', value: 12}, + {op: 'replace', path: '/info/nationality', value: 'it'}, + {op: 'add', path: '/info/hobbies/1', value: 'chess'}, + ]); + + var result = diff(a, b); + return assert.ok(Immutable.is(result, expected)); + }); +}); \ No newline at end of file From afa4e8396b27e45da089ba008c570ae5e6795d54 Mon Sep 17 00:00:00 2001 From: Giuseppe Davies Date: Mon, 10 Jun 2019 12:06:38 +0100 Subject: [PATCH 3/4] Updates for record diffing --- src/diff.js | 31 ++++++++++++++++++++++++++++--- src/utils.js | 4 +++- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/diff.js b/src/diff.js index d36e91b..2aa04f5 100644 --- a/src/diff.js +++ b/src/diff.js @@ -8,7 +8,21 @@ var concatPath = path.concat, escape = path.escape, op = utils.op, isMap = utils.isMap, - isIndexed = utils.isIndexed; + isIndexed = utils.isIndexed, + isRecord = utils.isRecord; + +var recordDiff = function(a, b, p) { + var ops = []; + var path = p || ''; + + if(Immutable.is(a, b) || (a == b == null)){ return ops; } + + const aMap = a.toSeq().toMap(); + const bMap = b.toSeq().toMap(); + ops = ops.concat(mapDiff(aMap, bMap, path)); + + return ops; +}; var mapDiff = function(a, b, p){ var ops = []; @@ -21,9 +35,13 @@ var mapDiff = function(a, b, p){ var removeKey = null if(a.forEach){ + a.forEach(function(aValue, aKey){ if(b.has(aKey)){ - if(isMap(aValue) && isMap(b.get(aKey))){ + if(isRecord(aValue) && isRecord(b.get(aKey))){ + ops = ops.concat(recordDiff(aValue, b.get(aKey), concatPath(path, escape(aKey)))); + } + else if(isMap(aValue) && isMap(b.get(aKey))){ ops = ops.concat(mapDiff(aValue, b.get(aKey), concatPath(path, escape(aKey)))); } else if(isIndexed(b.get(aKey)) && isIndexed(aValue)){ @@ -73,7 +91,11 @@ var sequenceDiff = function (a, b, p) { lcsDiff.forEach(function (diff) { if(diff.op === '='){ pathIndex++; } else if(diff.op === '!='){ - if(isMap(diff.val) && isMap(diff.newVal)){ + if (isRecord(diff.val) && isRecord(diff.newVal)){ + var recordDiffs = recordDiff(diff.val, diff.newVal, concatPath(path, pathIndex)); + ops = ops.concat(recordDiffs); + } + else if(isMap(diff.val) && isMap(diff.newVal)){ var mapDiffs = mapDiff(diff.val, diff.newVal, concatPath(path, pathIndex)); ops = ops.concat(mapDiffs); } @@ -106,6 +128,9 @@ var diff = function(a, b, p){ if(isIndexed(a) && isIndexed(b)){ return Immutable.fromJS(sequenceDiff(a, b)); } + else if(isRecord(a) && isRecord(b)){ + return Immutable.fromJS(recordDiff(a, b)); + } else if(isMap(a) && isMap(b)){ return Immutable.fromJS(mapDiff(a, b)); } diff --git a/src/utils.js b/src/utils.js index 3011ebe..b007381 100644 --- a/src/utils.js +++ b/src/utils.js @@ -2,8 +2,9 @@ var Immutable = require('immutable'); -var isMap = function(obj){ return Immutable.Iterable.isKeyed(obj); }; +var isMap = function(obj){ return Immutable.Map.isMap(obj); }; var isIndexed = function(obj) { return Immutable.Iterable.isIndexed(obj); }; +var isRecord = function(obj) { return Immutable.Record.isRecord(obj); }; var op = function(operation, path, value){ if(operation === 'remove') { return { op: operation, path: path }; } @@ -12,6 +13,7 @@ var op = function(operation, path, value){ }; module.exports = { + isRecord: isRecord, isMap: isMap, isIndexed: isIndexed, op: op From a0d98525bcb30d4a98bcedebc4a637f69a1cede5 Mon Sep 17 00:00:00 2001 From: Giuseppe Davies Date: Mon, 10 Jun 2019 14:06:04 +0100 Subject: [PATCH 4/4] Update readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8df4b66..2a7b594 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Immutable Diff +*Forked to add support for Records in ImmutableJS 4.x.x* + Create RFC 6902 style patches between Immutable.JS data structures, such as `Maps`, `Lists`, and `Sets`. ## Getting Started