Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Immutable JS 4 Record support #24

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -28,6 +28,6 @@
"jsondiff": "brenoferreira/json-diff"
},
"dependencies": {
"immutable": "^3.2.1"
"immutable": "^4.0.0-rc.12"
}
}
31 changes: 28 additions & 3 deletions src/diff.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [];
Expand All @@ -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)){
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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));
}
Expand Down
4 changes: 3 additions & 1 deletion src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 }; }
Expand All @@ -12,6 +13,7 @@ var op = function(operation, path, value){
};

module.exports = {
isRecord: isRecord,
isMap: isMap,
isIndexed: isIndexed,
op: op
Expand Down
73 changes: 73 additions & 0 deletions tests/RecordDiff.test.js
Original file line number Diff line number Diff line change
@@ -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));
});
});