simple-diff
Advanced tools
Comparing version 1.4.0 to 1.5.0
{ | ||
"name": "simple-diff", | ||
"main": "simple-diff.js", | ||
"version": "1.4.0", | ||
"version": "1.5.0", | ||
"homepage": "https://github.com/redexp/simple-diff", | ||
@@ -6,0 +6,0 @@ "authors": [ |
{ | ||
"name": "simple-diff", | ||
"version": "1.4.0", | ||
"version": "1.5.0", | ||
"description": "simple diff for object and arrays with options", | ||
@@ -28,4 +28,6 @@ "main": "simple-diff.js", | ||
"gulp-uglify": "^1.5.4", | ||
"mocha": "^2.5.3" | ||
"mocha": "^2.5.3", | ||
"sinon": "^1.17.7", | ||
"sinon-chai": "^2.8.0" | ||
} | ||
} |
@@ -143,2 +143,30 @@ simple-diff | ||
## `comparators: [[Class, function (oldValue, newValue, options)], ...]` | ||
Array of comparators to handle comparison of objects which do not have own properties. For example instead of `user.name` you have `user.getName()` or you need to compare `Date` objects or you just know quick way to compare large objects like by `id` property and you do not need full list of changes of those objects. First value of comparator should be some class and second is function which should return `true` if objects are equal and `false` if they not. In `options` will be `oldPath` and `newPath`. | ||
```javascript | ||
var now = new Date(); | ||
var prevHour = new Date(); | ||
prevHour.setHours(-1); | ||
var changes = diff( | ||
{ | ||
createdAt: now | ||
}, | ||
{ | ||
createdAt: prevHour | ||
}, | ||
{ | ||
comparators: [ | ||
[Date, function(oldValue, newValue, options) { | ||
options.oldPath; // ['createdAt'] | ||
options.newPath; // ['createdAt'] | ||
return oldValue.toString() === newValue.toString(); | ||
}] | ||
] | ||
} | ||
); | ||
``` | ||
## `callback: function (event)` | ||
@@ -145,0 +173,0 @@ |
@@ -31,2 +31,3 @@ (function (root, factory) { | ||
}, | ||
comparators = ops.comparators || [], | ||
i, len, prop, id; | ||
@@ -184,2 +185,25 @@ | ||
else { | ||
if (comparators.length > 0) { | ||
for (i = 0, len = comparators.length; i < len; i++) { | ||
if (oldObj instanceof comparators[i][0] === false && newObj instanceof comparators[i][0] === false) continue; | ||
var objEqual = comparators[i][1](oldObj, newObj, { | ||
oldPath: oldPath, | ||
newPath: newPath | ||
}); | ||
if (!objEqual) { | ||
callback({ | ||
oldPath: oldPath, | ||
newPath: newPath, | ||
type: CHANGE_EVENT, | ||
oldValue: oldObj, | ||
newValue: newObj | ||
}); | ||
} | ||
return changes; | ||
} | ||
} | ||
for (prop in oldObj) { | ||
@@ -186,0 +210,0 @@ if (!oldObj.hasOwnProperty(prop)) continue; |
@@ -1,1 +0,1 @@ | ||
!function(root,factory){"function"==typeof define&&define.amd?define([],factory):"object"==typeof exports?module.exports=factory():root.simpleDiff=factory()}(this,function(){function diff(oldObj,newObj,ops){ops=ops||{};var i,len,prop,id,changes=[],oldPath=ops.oldPath||[],newPath=ops.newPath||[],ID_PROP=ops.idProp||"id",ADD_EVENT=ops.addEvent||"add",REMOVE_EVENT=ops.removeEvent||"remove",CHANGE_EVENT=ops.changeEvent||"change",ADD_ITEM_EVENT=ops.addItemEvent||"add-item",REMOVE_ITEM_EVENT=ops.removeItemEvent||"remove-item",MOVE_ITEM_EVENT=ops.moveItemEvent||"move-item",callback=ops.callback||function(item){changes.push(item)};if(!(isObject(oldObj)&&isObject(newObj)||oldObj===newObj))return callback({oldPath:oldPath,newPath:newPath,type:CHANGE_EVENT,oldValue:oldObj,newValue:newObj}),changes;if(isArray(oldObj)){var idProp=ops.idProps&&(ops.idProps[oldPath.map(numberToAsterisk).join(".")]||ops.idProps[oldPath.join(".")])||ID_PROP;if("*"===idProp){var oldLength=oldObj.length,newLength=newObj.length;for(i=0,len=oldLength>newLength?oldLength:newLength;i<len;i++)i<oldLength&&i<newLength?diff(oldObj[i],newObj[i],extend({},ops,{callback:callback,oldPath:oldPath.concat(i),newPath:newPath.concat(i)})):i>=oldLength?callback({oldPath:oldPath,newPath:newPath,type:ADD_ITEM_EVENT,oldIndex:-1,curIndex:-1,newIndex:i,newValue:newObj[i]}):i>=newLength&&callback({oldPath:oldPath,newPath:newPath,type:REMOVE_ITEM_EVENT,oldIndex:i,curIndex:newLength,newIndex:-1,oldValue:oldObj[i]});return changes}var sample=oldObj.length>0?oldObj[0]:newObj[0];if(sample===UNDEFINED)return changes;var curIndex,oldIndex,objective="object"==typeof sample,oldHash=objective?indexBy(oldObj,idProp):hashOf(oldObj),newHash=objective?indexBy(newObj,idProp):hashOf(newObj),curArray=[].concat(oldObj);for(i=0,len=oldObj.length;i<len;i++)id=objective?oldObj[i][idProp]:oldObj[i],newHash.hasOwnProperty(id)||(curIndex=curArray.indexOf(oldObj[i]),curArray.splice(curIndex,1),callback({oldPath:oldPath,newPath:newPath,type:REMOVE_ITEM_EVENT,oldIndex:i,curIndex:curIndex,newIndex:-1,oldValue:oldObj[i]}));for(i=0,len=newObj.length;i<len;i++)id=objective?newObj[i][idProp]:newObj[i],oldHash.hasOwnProperty(id)||(callback({oldPath:oldPath,newPath:newPath,type:ADD_ITEM_EVENT,oldIndex:-1,curIndex:-1,newIndex:i,newValue:newObj[i]}),i>=curArray.length?curArray.push(newObj[i]):curArray.splice(i,0,newObj[i]));for(i=0,len=newObj.length;i<len;i++)id=objective?newObj[i][idProp]:newObj[i],oldHash.hasOwnProperty(id)&&(oldIndex=oldObj.indexOf(oldHash[id]),curIndex=curArray.indexOf(oldHash[id]),i!==curIndex&&(callback({oldPath:oldPath,newPath:newPath,type:MOVE_ITEM_EVENT,oldIndex:oldIndex,curIndex:curIndex,newIndex:i}),curArray.splice(curIndex,1),curArray.splice(i,0,oldHash[id])),diff(oldHash[id],newObj[i],extend({},ops,{callback:callback,oldPath:oldPath.concat(oldIndex),newPath:newPath.concat(i)})))}else{for(prop in oldObj)oldObj.hasOwnProperty(prop)&&(newObj.hasOwnProperty(prop)?isObject(oldObj[prop])&&isObject(newObj[prop])?diff(oldObj[prop],newObj[prop],extend({},ops,{callback:callback,oldPath:oldPath.concat(prop),newPath:newPath.concat(prop)})):oldObj[prop]!==newObj[prop]&&callback({oldPath:oldPath.concat(prop),newPath:newPath.concat(prop),type:CHANGE_EVENT,oldValue:oldObj[prop],newValue:newObj[prop]}):callback({oldPath:oldPath.concat(prop),newPath:newPath.concat(prop),type:REMOVE_EVENT,oldValue:oldObj[prop],newValue:UNDEFINED}));for(prop in newObj)newObj.hasOwnProperty(prop)&&(oldObj.hasOwnProperty(prop)||callback({oldPath:oldPath.concat(prop),newPath:newPath.concat(prop),type:ADD_EVENT,oldValue:UNDEFINED,newValue:newObj[prop]}))}return changes}function isObject(object){return!!object&&"object"==typeof object}function isArray(array){return Array.isArray?Array.isArray(array):"[object Array]"===Object.prototype.toString.call(array)}function indexBy(array,id){for(var hash={},i=0,len=array.length;i<len;i++)hash[array[i][id]]=array[i];return hash}function hashOf(array){for(var hash={},i=0,len=array.length;i<len;i++)hash[array[i]]=array[i];return hash}function extend(target){for(var i=1,len=arguments.length;i<len;i++){var source=arguments[i];for(var prop in source)source.hasOwnProperty(prop)&&(target[prop]=source[prop])}return target}function numberToAsterisk(value){return"number"==typeof value?"*":value}var UNDEFINED;return diff}); | ||
!function(root,factory){"function"==typeof define&&define.amd?define([],factory):"object"==typeof exports?module.exports=factory():root.simpleDiff=factory()}(this,function(){function diff(oldObj,newObj,ops){ops=ops||{};var i,len,prop,id,changes=[],oldPath=ops.oldPath||[],newPath=ops.newPath||[],ID_PROP=ops.idProp||"id",ADD_EVENT=ops.addEvent||"add",REMOVE_EVENT=ops.removeEvent||"remove",CHANGE_EVENT=ops.changeEvent||"change",ADD_ITEM_EVENT=ops.addItemEvent||"add-item",REMOVE_ITEM_EVENT=ops.removeItemEvent||"remove-item",MOVE_ITEM_EVENT=ops.moveItemEvent||"move-item",callback=ops.callback||function(item){changes.push(item)},comparators=ops.comparators||[];if(!(isObject(oldObj)&&isObject(newObj)||oldObj===newObj))return callback({oldPath:oldPath,newPath:newPath,type:CHANGE_EVENT,oldValue:oldObj,newValue:newObj}),changes;if(isArray(oldObj)){var idProp=ops.idProps&&(ops.idProps[oldPath.map(numberToAsterisk).join(".")]||ops.idProps[oldPath.join(".")])||ID_PROP;if("*"===idProp){var oldLength=oldObj.length,newLength=newObj.length;for(i=0,len=oldLength>newLength?oldLength:newLength;i<len;i++)i<oldLength&&i<newLength?diff(oldObj[i],newObj[i],extend({},ops,{callback:callback,oldPath:oldPath.concat(i),newPath:newPath.concat(i)})):i>=oldLength?callback({oldPath:oldPath,newPath:newPath,type:ADD_ITEM_EVENT,oldIndex:-1,curIndex:-1,newIndex:i,newValue:newObj[i]}):i>=newLength&&callback({oldPath:oldPath,newPath:newPath,type:REMOVE_ITEM_EVENT,oldIndex:i,curIndex:newLength,newIndex:-1,oldValue:oldObj[i]});return changes}var sample=oldObj.length>0?oldObj[0]:newObj[0];if(sample===UNDEFINED)return changes;var curIndex,oldIndex,objective="object"==typeof sample,oldHash=objective?indexBy(oldObj,idProp):hashOf(oldObj),newHash=objective?indexBy(newObj,idProp):hashOf(newObj),curArray=[].concat(oldObj);for(i=0,len=oldObj.length;i<len;i++)id=objective?oldObj[i][idProp]:oldObj[i],newHash.hasOwnProperty(id)||(curIndex=curArray.indexOf(oldObj[i]),curArray.splice(curIndex,1),callback({oldPath:oldPath,newPath:newPath,type:REMOVE_ITEM_EVENT,oldIndex:i,curIndex:curIndex,newIndex:-1,oldValue:oldObj[i]}));for(i=0,len=newObj.length;i<len;i++)id=objective?newObj[i][idProp]:newObj[i],oldHash.hasOwnProperty(id)||(callback({oldPath:oldPath,newPath:newPath,type:ADD_ITEM_EVENT,oldIndex:-1,curIndex:-1,newIndex:i,newValue:newObj[i]}),i>=curArray.length?curArray.push(newObj[i]):curArray.splice(i,0,newObj[i]));for(i=0,len=newObj.length;i<len;i++)id=objective?newObj[i][idProp]:newObj[i],oldHash.hasOwnProperty(id)&&(oldIndex=oldObj.indexOf(oldHash[id]),curIndex=curArray.indexOf(oldHash[id]),i!==curIndex&&(callback({oldPath:oldPath,newPath:newPath,type:MOVE_ITEM_EVENT,oldIndex:oldIndex,curIndex:curIndex,newIndex:i}),curArray.splice(curIndex,1),curArray.splice(i,0,oldHash[id])),diff(oldHash[id],newObj[i],extend({},ops,{callback:callback,oldPath:oldPath.concat(oldIndex),newPath:newPath.concat(i)})))}else{if(comparators.length>0)for(i=0,len=comparators.length;i<len;i++)if(oldObj instanceof comparators[i][0]!=!1||newObj instanceof comparators[i][0]!=!1){var objEqual=comparators[i][1](oldObj,newObj,{oldPath:oldPath,newPath:newPath});return objEqual||callback({oldPath:oldPath,newPath:newPath,type:CHANGE_EVENT,oldValue:oldObj,newValue:newObj}),changes}for(prop in oldObj)oldObj.hasOwnProperty(prop)&&(newObj.hasOwnProperty(prop)?isObject(oldObj[prop])&&isObject(newObj[prop])?diff(oldObj[prop],newObj[prop],extend({},ops,{callback:callback,oldPath:oldPath.concat(prop),newPath:newPath.concat(prop)})):oldObj[prop]!==newObj[prop]&&callback({oldPath:oldPath.concat(prop),newPath:newPath.concat(prop),type:CHANGE_EVENT,oldValue:oldObj[prop],newValue:newObj[prop]}):callback({oldPath:oldPath.concat(prop),newPath:newPath.concat(prop),type:REMOVE_EVENT,oldValue:oldObj[prop],newValue:UNDEFINED}));for(prop in newObj)newObj.hasOwnProperty(prop)&&(oldObj.hasOwnProperty(prop)||callback({oldPath:oldPath.concat(prop),newPath:newPath.concat(prop),type:ADD_EVENT,oldValue:UNDEFINED,newValue:newObj[prop]}))}return changes}function isObject(object){return!!object&&"object"==typeof object}function isArray(array){return Array.isArray?Array.isArray(array):"[object Array]"===Object.prototype.toString.call(array)}function indexBy(array,id){for(var hash={},i=0,len=array.length;i<len;i++)hash[array[i][id]]=array[i];return hash}function hashOf(array){for(var hash={},i=0,len=array.length;i<len;i++)hash[array[i]]=array[i];return hash}function extend(target){for(var i=1,len=arguments.length;i<len;i++){var source=arguments[i];for(var prop in source)source.hasOwnProperty(prop)&&(target[prop]=source[prop])}return target}function numberToAsterisk(value){return"number"==typeof value?"*":value}var UNDEFINED;return diff}); |
var expect = require('chai').expect, | ||
sinon = require('sinon'), | ||
diff = require('../simple-diff'), | ||
undefined; | ||
require('chai').use(require('sinon-chai')); | ||
describe('diff', function () { | ||
@@ -553,2 +556,83 @@ it('should find changes in objects', function () { | ||
}); | ||
it('should handle comparators: [] to compare custom class objects', function () { | ||
var cb = sinon.spy(function (a, b, ops) { | ||
expect(a).to.be.instanceOf(Date); | ||
expect(b).to.be.instanceOf(Date); | ||
expect(ops).to.deep.equal({ | ||
oldPath: ['prop', 'date'], | ||
newPath: ['prop', 'date'] | ||
}); | ||
return a.toString() === b.toString(); | ||
}); | ||
var changes = diff( | ||
{ | ||
prop: { | ||
test1: {}, | ||
test2: [{}], | ||
date: new Date() | ||
} | ||
}, | ||
{ | ||
prop: { | ||
test1: {}, | ||
test2: [{}], | ||
date: new Date() | ||
} | ||
}, | ||
{ | ||
comparators: [ | ||
[Date, cb] | ||
] | ||
} | ||
); | ||
expect(changes).to.deep.equal([]); | ||
expect(cb).to.have.callCount(1); | ||
var nowDate = new Date(); | ||
var prevDate = new Date(); | ||
prevDate.setHours(-1); | ||
changes = diff( | ||
{ | ||
prop: { | ||
test1: 1, | ||
date: nowDate | ||
} | ||
}, | ||
{ | ||
prop: { | ||
test1: 2, | ||
date: prevDate | ||
} | ||
}, | ||
{ | ||
comparators: [ | ||
[Date, cb] | ||
] | ||
} | ||
); | ||
expect(cb).to.have.callCount(2); | ||
expect(changes).to.deep.equal([ | ||
{ | ||
oldPath: ['prop', 'test1'], | ||
newPath: ['prop', 'test1'], | ||
type: 'change', | ||
oldValue: 1, | ||
newValue: 2 | ||
}, | ||
{ | ||
oldPath: ['prop', 'date'], | ||
newPath: ['prop', 'date'], | ||
type: 'change', | ||
oldValue: nowDate, | ||
newValue: prevDate | ||
} | ||
]); | ||
}); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
37982
901
199
8