From b4a2fe5fbaa2c2f26503025d984e61e79e3fe96d Mon Sep 17 00:00:00 2001 From: Evgeny Kochetkov Date: Fri, 25 Mar 2016 20:08:41 +0300 Subject: [PATCH 1/3] Add update and updateIn functions --- seamless-immutable.development.js | 25 ++++++++ seamless-immutable.development.min.js | 2 +- seamless-immutable.production.min.js | 2 +- src/seamless-immutable.js | 25 ++++++++ test/ImmutableArray.spec.js | 2 + test/ImmutableArray/test-update.js | 66 ++++++++++++++++++++ test/ImmutableObject.spec.js | 4 +- test/ImmutableObject/test-update.js | 88 +++++++++++++++++++++++++++ 8 files changed, 211 insertions(+), 3 deletions(-) create mode 100644 test/ImmutableArray/test-update.js create mode 100644 test/ImmutableObject/test-update.js diff --git a/seamless-immutable.development.js b/seamless-immutable.development.js index 484e72c..f0dccab 100644 --- a/seamless-immutable.development.js +++ b/seamless-immutable.development.js @@ -151,6 +151,8 @@ addPropertyTo(array, "asMutable", asMutableArray); addPropertyTo(array, "set", arraySet); addPropertyTo(array, "setIn", arraySetIn); + addPropertyTo(array, "update", update); + addPropertyTo(array, "updateIn", updateIn); for(var i = 0, length = array.length; i < length; i++) { array[i] = Immutable(array[i]); @@ -430,6 +432,27 @@ return makeImmutableObject(mutable, this); } + function update(property, updater) { + var restArgs = Array.prototype.slice.call(arguments, 2); + var initialVal = this[property]; + return this.set(property, updater.apply(initialVal, [initialVal].concat(restArgs))) + } + + function getInPath(obj, path) { + for (var i = 0, l = path.length; obj != null && i < l; i++) { + obj = obj[path[i]]; + } + + return (i && i == l) ? obj : undefined; + } + + function updateIn(path, updater) { + var restArgs = Array.prototype.slice.call(arguments, 2); + var initialVal = getInPath(this, path); + + return this.setIn(path, updater.apply(initialVal, [initialVal].concat(restArgs))) + } + function asMutableObject(opts) { var result = this.instantiateEmptyObject(), key; @@ -467,6 +490,8 @@ addPropertyTo(obj, "instantiateEmptyObject", instantiateEmptyObject); addPropertyTo(obj, "set", objectSet); addPropertyTo(obj, "setIn", objectSetIn); + addPropertyTo(obj, "update", update); + addPropertyTo(obj, "updateIn", updateIn); return makeImmutable(obj, mutatingObjectMethods); } diff --git a/seamless-immutable.development.min.js b/seamless-immutable.development.min.js index 77dbc46..c685691 100644 --- a/seamless-immutable.development.min.js +++ b/seamless-immutable.development.min.js @@ -1 +1 @@ -/* (c) 2016, Richard Feldman, github.com/rtfeldman/seamless-immutable/blob/master/LICENSE */!function(){"use strict";function a(a,b,c){Object.defineProperty(a,b,{enumerable:!1,configurable:!1,writable:!1,value:c})}function b(b,c){a(b,c,function(){throw new f("The "+c+" method cannot be invoked on an Immutable data structure.")})}function c(b){a(b,A,!0)}function d(a){return"object"==typeof a?null===a||Boolean(Object.getOwnPropertyDescriptor(a,A)):!0}function e(a){return!(null===a||"object"!=typeof a||a instanceof Array||a instanceof Date)}function f(a){var b=new Error(a);return b.__proto__=f,b}function g(a,d){c(a);for(var e in d)d.hasOwnProperty(e)&&b(a,d[e]);return Object.freeze(a),a}function h(b,c){var d=b[c];a(b,c,function(){return z(d.apply(b,arguments))})}function i(a,b){if(a in this&&this[a]===b)return this;var c=p.call(this);return c[a]=z(b),k(c)}function j(a,b){var c=a[0];if(1===a.length)return i.call(this,c,b);var d,e=a.slice(1),f=this[c];if(d="object"==typeof f&&null!==f&&"function"==typeof f.setIn?f.setIn(e,b):j.call(G,e,b),c in this&&f===d)return this;var g=p.call(this);return g[c]=d,k(g)}function k(b){for(var c in E)if(E.hasOwnProperty(c)){var d=E[c];h(b,d)}a(b,"flatMap",n),a(b,"asObject",q),a(b,"asMutable",p),a(b,"set",i),a(b,"setIn",j);for(var e=0,f=b.length;f>e;e++)b[e]=z(b[e]);return g(b,D)}function l(b){return a(b,"asMutable",m),g(b,F)}function m(){return new Date(this.getTime())}function n(a){if(0===arguments.length)return this;var b,c=[],d=this.length;for(b=0;d>b;b++){var e=a(this[b],b,this);e instanceof Array?c.push.apply(c,e):c.push(e)}return k(c)}function o(a){if("undefined"==typeof a&&0===arguments.length)return this;if("function"!=typeof a){var b=a instanceof Array?a:Array.prototype.slice.call(arguments);a=function(a,c){return-1!==b.indexOf(c)}}var c=this.instantiateEmptyObject();for(var d in this)this.hasOwnProperty(d)&&a(this[d],d)===!1&&(c[d]=this[d]);return y(c,{instantiateEmptyObject:this.instantiateEmptyObject})}function p(a){var b,c,d=[];if(a&&a.deep)for(b=0,c=this.length;c>b;b++)d.push(r(this[b]));else for(b=0,c=this.length;c>b;b++)d.push(this[b]);return d}function q(a){"function"!=typeof a&&(a=function(a){return a});var b,c={},d=this.length;for(b=0;d>b;b++){var e=a(this[b],b,this),f=e[0],g=e[1];c[f]=g}return y(c)}function r(a){return!a||"object"!=typeof a||!Object.getOwnPropertyDescriptor(a,A)||a instanceof Date?a:a.asMutable({deep:!0})}function s(a,b){for(var c in a)Object.getOwnPropertyDescriptor(a,c)&&(b[c]=a[c]);return b}function t(a,b){function c(a,c,f){var g=z(c[f]),j=i&&i(a[f],g,b),k=a[f];if(void 0!==d||void 0!==j||!a.hasOwnProperty(f)||g!==k&&g===g){var l;l=j?j:h&&e(k)&&e(g)?k.merge(g,b):g,(k!==l&&l===l||!a.hasOwnProperty(f))&&(void 0===d&&(d=s(a,a.instantiateEmptyObject())),d[f]=l)}}if(0===arguments.length)return this;if(null===a||"object"!=typeof a)throw new TypeError("Immutable#merge can only be invoked with objects or arrays, not "+JSON.stringify(a));var d,f,g=a instanceof Array,h=b&&b.deep,i=b&&b.merger;if(g)for(var j=0;je;e++)b[e]=C(b[e]);return g(b,G)}function l(b){return a(b,"asMutable",m),g(b,I)}function m(){return new Date(this.getTime())}function n(a){if(0===arguments.length)return this;var b,c=[],d=this.length;for(b=0;d>b;b++){var e=a(this[b],b,this);e instanceof Array?c.push.apply(c,e):c.push(e)}return k(c)}function o(a){if("undefined"==typeof a&&0===arguments.length)return this;if("function"!=typeof a){var b=a instanceof Array?a:Array.prototype.slice.call(arguments);a=function(a,c){return-1!==b.indexOf(c)}}var c=this.instantiateEmptyObject();for(var d in this)this.hasOwnProperty(d)&&a(this[d],d)===!1&&(c[d]=this[d]);return B(c,{instantiateEmptyObject:this.instantiateEmptyObject})}function p(a){var b,c,d=[];if(a&&a.deep)for(b=0,c=this.length;c>b;b++)d.push(r(this[b]));else for(b=0,c=this.length;c>b;b++)d.push(this[b]);return d}function q(a){"function"!=typeof a&&(a=function(a){return a});var b,c={},d=this.length;for(b=0;d>b;b++){var e=a(this[b],b,this),f=e[0],g=e[1];c[f]=g}return B(c)}function r(a){return!a||"object"!=typeof a||!Object.getOwnPropertyDescriptor(a,D)||a instanceof Date?a:a.asMutable({deep:!0})}function s(a,b){for(var c in a)Object.getOwnPropertyDescriptor(a,c)&&(b[c]=a[c]);return b}function t(a,b){function c(a,c,f){var g=C(c[f]),j=i&&i(a[f],g,b),k=a[f];if(void 0!==d||void 0!==j||!a.hasOwnProperty(f)||g!==k&&g===g){var l;l=j?j:h&&e(k)&&e(g)?k.merge(g,b):g,(k!==l&&l===l||!a.hasOwnProperty(f))&&(void 0===d&&(d=s(a,a.instantiateEmptyObject())),d[f]=l)}}if(0===arguments.length)return this;if(null===a||"object"!=typeof a)throw new TypeError("Immutable#merge can only be invoked with objects or arrays, not "+JSON.stringify(a));var d,f,g=a instanceof Array,h=b&&b.deep,i=b&&b.merger;if(g)for(var j=0;jc;c++)a=a[b[c]];return c&&c==d?a:void 0}function y(a,b){var c=Array.prototype.slice.call(arguments,2),d=x(this,a);return this.setIn(a,b.apply(d,[d].concat(c)))}function z(a){var b,c=this.instantiateEmptyObject();if(a&&a.deep)for(b in this)this.hasOwnProperty(b)&&(c[b]=r(this[b]));else for(b in this)this.hasOwnProperty(b)&&(c[b]=this[b]);return c}function A(){return{}}function B(b,c){var d=c&&c.instantiateEmptyObject?c.instantiateEmptyObject:A;return a(b,"merge",t),a(b,"without",o),a(b,"asMutable",z),a(b,"instantiateEmptyObject",d),a(b,"set",v),a(b,"setIn",u),a(b,"update",w),a(b,"updateIn",y),g(b,E)}function C(a,b){if(d(a))return a;if(a instanceof Array)return k(a.slice());if(a instanceof Date)return l(new Date(a.getTime()));var c=b&&b.prototype,e=c&&c!==Object.prototype?function(){return Object.create(c)}:A,f=e();for(var g in a)Object.getOwnPropertyDescriptor(a,g)&&(f[g]=C(a[g]));return B(f,{instantiateEmptyObject:e})}var D="__immutable_invariants_hold",E=["setPrototypeOf"],F=["keys"],G=E.concat(["push","pop","sort","splice","shift","unshift","reverse"]),H=F.concat(["map","filter","slice","concat","reduce","reduceRight"]),I=E.concat(["setDate","setFullYear","setHours","setMilliseconds","setMinutes","setMonth","setSeconds","setTime","setUTCDate","setUTCFullYear","setUTCHours","setUTCMilliseconds","setUTCMinutes","setUTCMonth","setUTCSeconds","setYear"]);f.prototype=Error.prototype;var J=C([]),K=C({});C.isImmutable=d,C.ImmutableError=f,Object.freeze(C),"object"==typeof module?module.exports=C:"object"==typeof exports?exports.Immutable=C:"object"==typeof window?window.Immutable=C:"object"==typeof global&&(global.Immutable=C)}(); \ No newline at end of file diff --git a/seamless-immutable.production.min.js b/seamless-immutable.production.min.js index fb07a64..098f333 100644 --- a/seamless-immutable.production.min.js +++ b/seamless-immutable.production.min.js @@ -1 +1 @@ -/* (c) 2016, Richard Feldman, github.com/rtfeldman/seamless-immutable/blob/master/LICENSE */!function(){"use strict";function a(a,b,c){Object.defineProperty(a,b,{enumerable:!1,configurable:!1,writable:!1,value:c})}function b(b){a(b,z,!0)}function c(a){return"object"==typeof a?null===a||Boolean(Object.getOwnPropertyDescriptor(a,z)):!0}function d(a){return!(null===a||"object"!=typeof a||a instanceof Array||a instanceof Date)}function e(a){var b=new Error(a);return b.__proto__=e,b}function f(a,c){b(a);return a}function g(b,c){var d=b[c];a(b,c,function(){return y(d.apply(b,arguments))})}function h(a,b){if(a in this&&this[a]===b)return this;var c=o.call(this);return c[a]=y(b),j(c)}function i(a,b){var c=a[0];if(1===a.length)return h.call(this,c,b);var d,e=a.slice(1),f=this[c];if(d="object"==typeof f&&null!==f&&"function"==typeof f.setIn?f.setIn(e,b):i.call(F,e,b),c in this&&f===d)return this;var g=o.call(this);return g[c]=d,j(g)}function j(b){for(var c in D)if(D.hasOwnProperty(c)){var d=D[c];g(b,d)}a(b,"flatMap",m),a(b,"asObject",p),a(b,"asMutable",o),a(b,"set",h),a(b,"setIn",i);for(var e=0,j=b.length;j>e;e++)b[e]=y(b[e]);return f(b,C)}function k(b){return a(b,"asMutable",l),f(b,E)}function l(){return new Date(this.getTime())}function m(a){if(0===arguments.length)return this;var b,c=[],d=this.length;for(b=0;d>b;b++){var e=a(this[b],b,this);e instanceof Array?c.push.apply(c,e):c.push(e)}return j(c)}function n(a){if("undefined"==typeof a&&0===arguments.length)return this;if("function"!=typeof a){var b=a instanceof Array?a:Array.prototype.slice.call(arguments);a=function(a,c){return-1!==b.indexOf(c)}}var c=this.instantiateEmptyObject();for(var d in this)this.hasOwnProperty(d)&&a(this[d],d)===!1&&(c[d]=this[d]);return x(c,{instantiateEmptyObject:this.instantiateEmptyObject})}function o(a){var b,c,d=[];if(a&&a.deep)for(b=0,c=this.length;c>b;b++)d.push(q(this[b]));else for(b=0,c=this.length;c>b;b++)d.push(this[b]);return d}function p(a){"function"!=typeof a&&(a=function(a){return a});var b,c={},d=this.length;for(b=0;d>b;b++){var e=a(this[b],b,this),f=e[0],g=e[1];c[f]=g}return x(c)}function q(a){return!a||"object"!=typeof a||!Object.getOwnPropertyDescriptor(a,z)||a instanceof Date?a:a.asMutable({deep:!0})}function r(a,b){for(var c in a)Object.getOwnPropertyDescriptor(a,c)&&(b[c]=a[c]);return b}function s(a,b){function c(a,c,f){var g=y(c[f]),j=i&&i(a[f],g,b),k=a[f];if(void 0!==e||void 0!==j||!a.hasOwnProperty(f)||g!==k&&g===g){var l;l=j?j:h&&d(k)&&d(g)?k.merge(g,b):g,(k!==l&&l===l||!a.hasOwnProperty(f))&&(void 0===e&&(e=r(a,a.instantiateEmptyObject())),e[f]=l)}}if(0===arguments.length)return this;if(null===a||"object"!=typeof a)throw new TypeError("Immutable#merge can only be invoked with objects or arrays, not "+JSON.stringify(a));var e,f,g=a instanceof Array,h=b&&b.deep,i=b&&b.merger;if(g)for(var j=0;je;e++)b[e]=B(b[e]);return f(b,F)}function k(b){return a(b,"asMutable",l),f(b,H)}function l(){return new Date(this.getTime())}function m(a){if(0===arguments.length)return this;var b,c=[],d=this.length;for(b=0;d>b;b++){var e=a(this[b],b,this);e instanceof Array?c.push.apply(c,e):c.push(e)}return j(c)}function n(a){if("undefined"==typeof a&&0===arguments.length)return this;if("function"!=typeof a){var b=a instanceof Array?a:Array.prototype.slice.call(arguments);a=function(a,c){return-1!==b.indexOf(c)}}var c=this.instantiateEmptyObject();for(var d in this)this.hasOwnProperty(d)&&a(this[d],d)===!1&&(c[d]=this[d]);return A(c,{instantiateEmptyObject:this.instantiateEmptyObject})}function o(a){var b,c,d=[];if(a&&a.deep)for(b=0,c=this.length;c>b;b++)d.push(q(this[b]));else for(b=0,c=this.length;c>b;b++)d.push(this[b]);return d}function p(a){"function"!=typeof a&&(a=function(a){return a});var b,c={},d=this.length;for(b=0;d>b;b++){var e=a(this[b],b,this),f=e[0],g=e[1];c[f]=g}return A(c)}function q(a){return!a||"object"!=typeof a||!Object.getOwnPropertyDescriptor(a,C)||a instanceof Date?a:a.asMutable({deep:!0})}function r(a,b){for(var c in a)Object.getOwnPropertyDescriptor(a,c)&&(b[c]=a[c]);return b}function s(a,b){function c(a,c,f){var g=B(c[f]),j=i&&i(a[f],g,b),k=a[f];if(void 0!==e||void 0!==j||!a.hasOwnProperty(f)||g!==k&&g===g){var l;l=j?j:h&&d(k)&&d(g)?k.merge(g,b):g,(k!==l&&l===l||!a.hasOwnProperty(f))&&(void 0===e&&(e=r(a,a.instantiateEmptyObject())),e[f]=l)}}if(0===arguments.length)return this;if(null===a||"object"!=typeof a)throw new TypeError("Immutable#merge can only be invoked with objects or arrays, not "+JSON.stringify(a));var e,f,g=a instanceof Array,h=b&&b.deep,i=b&&b.merger;if(g)for(var j=0;jc;c++)a=a[b[c]];return c&&c==d?a:void 0}function x(a,b){var c=Array.prototype.slice.call(arguments,2),d=w(this,a);return this.setIn(a,b.apply(d,[d].concat(c)))}function y(a){var b,c=this.instantiateEmptyObject();if(a&&a.deep)for(b in this)this.hasOwnProperty(b)&&(c[b]=q(this[b]));else for(b in this)this.hasOwnProperty(b)&&(c[b]=this[b]);return c}function z(){return{}}function A(b,c){var d=c&&c.instantiateEmptyObject?c.instantiateEmptyObject:z;return a(b,"merge",s),a(b,"without",n),a(b,"asMutable",y),a(b,"instantiateEmptyObject",d),a(b,"set",u),a(b,"setIn",t),a(b,"update",v),a(b,"updateIn",x),f(b,D)}function B(a,b){if(c(a))return a;if(a instanceof Array)return j(a.slice());if(a instanceof Date)return k(new Date(a.getTime()));var d=b&&b.prototype,e=d&&d!==Object.prototype?function(){return Object.create(d)}:z,f=e();for(var g in a)Object.getOwnPropertyDescriptor(a,g)&&(f[g]=B(a[g]));return A(f,{instantiateEmptyObject:e})}var C="__immutable_invariants_hold",D=["setPrototypeOf"],E=["keys"],F=D.concat(["push","pop","sort","splice","shift","unshift","reverse"]),G=E.concat(["map","filter","slice","concat","reduce","reduceRight"]),H=D.concat(["setDate","setFullYear","setHours","setMilliseconds","setMinutes","setMonth","setSeconds","setTime","setUTCDate","setUTCFullYear","setUTCHours","setUTCMilliseconds","setUTCMinutes","setUTCMonth","setUTCSeconds","setYear"]);e.prototype=Error.prototype;var I=B([]),J=B({});B.isImmutable=c,B.ImmutableError=e,Object.freeze(B),"object"==typeof module?module.exports=B:"object"==typeof exports?exports.Immutable=B:"object"==typeof window?window.Immutable=B:"object"==typeof global&&(global.Immutable=B)}(); \ No newline at end of file diff --git a/src/seamless-immutable.js b/src/seamless-immutable.js index 50104e4..496c0dc 100644 --- a/src/seamless-immutable.js +++ b/src/seamless-immutable.js @@ -151,6 +151,8 @@ addPropertyTo(array, "asMutable", asMutableArray); addPropertyTo(array, "set", arraySet); addPropertyTo(array, "setIn", arraySetIn); + addPropertyTo(array, "update", update); + addPropertyTo(array, "updateIn", updateIn); for(var i = 0, length = array.length; i < length; i++) { array[i] = Immutable(array[i]); @@ -430,6 +432,27 @@ return makeImmutableObject(mutable, this); } + function update(property, updater) { + var restArgs = Array.prototype.slice.call(arguments, 2); + var initialVal = this[property]; + return this.set(property, updater.apply(initialVal, [initialVal].concat(restArgs))) + } + + function getInPath(obj, path) { + for (var i = 0, l = path.length; obj != null && i < l; i++) { + obj = obj[path[i]]; + } + + return (i && i == l) ? obj : undefined; + } + + function updateIn(path, updater) { + var restArgs = Array.prototype.slice.call(arguments, 2); + var initialVal = getInPath(this, path); + + return this.setIn(path, updater.apply(initialVal, [initialVal].concat(restArgs))) + } + function asMutableObject(opts) { var result = this.instantiateEmptyObject(), key; @@ -467,6 +490,8 @@ addPropertyTo(obj, "instantiateEmptyObject", instantiateEmptyObject); addPropertyTo(obj, "set", objectSet); addPropertyTo(obj, "setIn", objectSetIn); + addPropertyTo(obj, "update", update); + addPropertyTo(obj, "updateIn", updateIn); return makeImmutable(obj, mutatingObjectMethods); } diff --git a/test/ImmutableArray.spec.js b/test/ImmutableArray.spec.js index 16b463d..716306d 100644 --- a/test/ImmutableArray.spec.js +++ b/test/ImmutableArray.spec.js @@ -3,6 +3,7 @@ var testFlatMap = require("./ImmutableArray/test-flatMap.js"); var testAsObject = require("./ImmutableArray/test-asObject.js"); var testAsMutable = require("./ImmutableArray/test-asMutable.js"); var testSet = require("./ImmutableArray/test-set.js"); +var testUpdate = require("./ImmutableArray/test-update.js"); var devBuild = require("../seamless-immutable.development.js"); var prodBuild = require("../seamless-immutable.production.min.js"); var getTestUtils = require("./TestUtils.js"); @@ -21,6 +22,7 @@ var getTestUtils = require("./TestUtils.js"); testAsObject(config); testAsMutable(config); testSet(config); + testUpdate(config); }); }); }); diff --git a/test/ImmutableArray/test-update.js b/test/ImmutableArray/test-update.js new file mode 100644 index 0000000..acbd208 --- /dev/null +++ b/test/ImmutableArray/test-update.js @@ -0,0 +1,66 @@ +var JSC = require("jscheck"); +var assert = require("chai").assert; +var _ = require("lodash"); +var getTestUtils = require("../TestUtils.js"); + + +module.exports = function(config) { + var Immutable = config.implementation; + var TestUtils = getTestUtils(Immutable); + var check = TestUtils.check; + + function getPathComponent() { + // It's very convenient to use lodash.set, but it has funky behaviour + // with numeric keys. + var s = JSC.string()().replace(/[^\w]/g, '_'); + return /^\d+$/.test(s) ? s + 'a' : s; + } + + function dummyUpdater (x) { + return x + "updated"; + } + + describe("#update", function() { + it("updates an array element using updater function", function () { + check(100, [ JSC.array([TestUtils.TraversableObjectSpecifier, JSC.any()]) ], function(array) { + var immutable = Immutable(array); + var mutable = immutable.asMutable(); + var index = JSC.integer(0, array.length); + + immutable = immutable.update(index, dummyUpdater); + mutable[index] = dummyUpdater(mutable[index]); + + TestUtils.assertJsonEqual(immutable, mutable); + }); + }); + }); + + describe("#updateIn", function() { + it("updates a property by path using updater function", function () { + check(100, [ JSC.array([TestUtils.TraversableObjectSpecifier]) ], function(array) { + var immutable = Immutable(array); + // var value = JSC.any()(); + + var idx = JSC.integer(0, array.length-1); + var key = JSC.one_of(_.keys(immutable[idx]))(); + + var util = require('util'); + function printArr(arr) { + return '[\n\t>'+_.map(arr, util.inspect).join('\n\t>')+'\n]'; + } + + var mutable = immutable.asMutable(); + TestUtils.assertJsonEqual(immutable, mutable); + if (Immutable.isImmutable(mutable[idx])) { + mutable[idx] = mutable[idx].asMutable(); + } + mutable[idx][key] = dummyUpdater(mutable[idx][key]); + + TestUtils.assertJsonEqual( + immutable.updateIn([idx, key], dummyUpdater), + mutable + ); + }); + }); + }); +}; diff --git a/test/ImmutableObject.spec.js b/test/ImmutableObject.spec.js index 40c4eac..c6e9d68 100644 --- a/test/ImmutableObject.spec.js +++ b/test/ImmutableObject.spec.js @@ -2,7 +2,8 @@ var testMerge = require("./ImmutableObject/test-merge.js"); var testCompat = require("./ImmutableObject/test-compat.js"); var testWithout = require("./ImmutableObject/test-without.js"); var testAsMutable = require("./ImmutableObject/test-asMutable.js"); -var testSet = require("./ImmutableObject/test-set.js"); +var testSet = require("./ImmutableObject/test-set.js"); +var testUpdate = require("./ImmutableObject/test-update.js"); var testInstantiateEmptyObject = require("./ImmutableObject/test-instantiateEmptyObject.js"); var devBuild = require("../seamless-immutable.development.js"); var prodBuild = require("../seamless-immutable.production.min.js"); @@ -23,6 +24,7 @@ var getTestUtils = require("./TestUtils.js"); testAsMutable(config); testInstantiateEmptyObject(config); testSet(config); + testUpdate(config); }); }); }); diff --git a/test/ImmutableObject/test-update.js b/test/ImmutableObject/test-update.js new file mode 100644 index 0000000..a25bf9c --- /dev/null +++ b/test/ImmutableObject/test-update.js @@ -0,0 +1,88 @@ +var JSC = require("jscheck"); +var assert = require("chai").assert; +var _ = require("lodash"); +var getTestUtils = require("../TestUtils.js"); + + +module.exports = function(config) { + var Immutable = config.implementation; + var TestUtils = getTestUtils(Immutable); + var check = TestUtils.check; + + function getPathComponent() { + // It's very convenient to use lodash.set, but it has funky behaviour + // with numeric keys. + var s = JSC.string()().replace(/[^\w]/g, '_'); + return /^\d+$/.test(s) ? s + 'a' : s; + } + + function dummyUpdater (x) { + return JSON.stringify(x) + "_updated"; + } + + function dummyUpdaterWithAggitionalArgs (x, y, z) { + return dummyUpdater(x) + y + z; + } + + describe("#update", function() { + it("updates a property using updater function", function () { + check(100, [TestUtils.TraversableObjectSpecifier], function(ob) { + var immutable = Immutable(ob); + var mutable = immutable.asMutable({deep: true}); + var prop = 'complex'; + + TestUtils.assertJsonEqual( + immutable.update(prop, dummyUpdater), + _.set(mutable, prop, dummyUpdater(_.get(mutable, prop))) + ); + }); + }); + + it("allows passing additional parameters to updater function", function () { + check(100, [TestUtils.TraversableObjectSpecifier], function(ob) { + var immutable = Immutable(ob); + var mutable = immutable.asMutable({deep: true}); + var prop = 'complex'; + + TestUtils.assertJsonEqual( + immutable.update(prop, dummyUpdater, "agr1", 42), + _.set(mutable, prop, dummyUpdater(_.get(mutable, prop), "agr1", 42)) + ); + }); + }); + }); + + describe("#updateIn", function() { + it("updates a property in path using updater function", function () { + check(100, [TestUtils.TraversableObjectSpecifier], function(ob) { + var immutable = Immutable(ob); + var mutable = immutable.asMutable({deep: true}); + + TestUtils.assertJsonEqual(immutable, mutable); + + var path = ['deep', 'complex']; + + TestUtils.assertJsonEqual( + immutable.updateIn(path, dummyUpdater), + _.set(mutable, path, dummyUpdater(_.get(mutable, path))) + ); + }); + }); + + it("allows passing additional parameters to updater function", function () { + check(100, [TestUtils.TraversableObjectSpecifier], function(ob) { + var immutable = Immutable(ob); + var mutable = immutable.asMutable({deep: true}); + + TestUtils.assertJsonEqual(immutable, mutable); + + var path = ['deep', 'complex']; + + TestUtils.assertJsonEqual( + immutable.updateIn(path, dummyUpdater, "agr1", 42), + _.set(mutable, path, dummyUpdater(_.get(mutable, path), "agr1", 42)) + ); + }); + }); + }); +}; From 57fbdbaa0dc42a9df9b6be0f73d277c5f9375b5f Mon Sep 17 00:00:00 2001 From: Evgeny Kochetkov Date: Fri, 25 Mar 2016 20:32:46 +0300 Subject: [PATCH 2/3] add examples to README --- README.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/README.md b/README.md index 6fe1740..391e072 100644 --- a/README.md +++ b/README.md @@ -199,6 +199,34 @@ Immutable({type: {main: "parrot", sub: "Norwegian Blue"}, status: "alive"}).setI // returns Immutable({type: {main: "parrot", sub: "Norwegian Ridgeback"}, status: "alive"}) ``` +### update + +Returns an Immutable Object with a single property updated using the provided updater function. + +```javascript +function inc (x) { return x + 1 } +Immutable({foo: 1}).update("foo", inc) +// returns Immutable({foo: 2}) +``` + +All additional arguments will be passed to the updater function. + +```javascript +function add (x, y) { return x + y } +Immutable({foo: 1}).update("foo", add, 10) +// returns Immutable({foo: 11}) +``` + +### updateIn + +Like [update](#update), but accepts a nested path to the property. + +```javascript +function add (x, y) { return x + y } +Immutable({foo: {bar: 1}}).updateIn(["foo", "bar"], add, 10) +// returns Immutable({foo: {bar: 11}}) +``` + ### without ```javascript From 17e51ff5c0b0b2ab5460920129faa2f687d2814e Mon Sep 17 00:00:00 2001 From: Evgeny Kochetkov Date: Fri, 25 Mar 2016 20:50:12 +0300 Subject: [PATCH 3/3] fixed jshint warnings --- seamless-immutable.development.js | 5 +++-- src/seamless-immutable.js | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/seamless-immutable.development.js b/seamless-immutable.development.js index f0dccab..26d67c3 100644 --- a/seamless-immutable.development.js +++ b/seamless-immutable.development.js @@ -435,10 +435,11 @@ function update(property, updater) { var restArgs = Array.prototype.slice.call(arguments, 2); var initialVal = this[property]; - return this.set(property, updater.apply(initialVal, [initialVal].concat(restArgs))) + return this.set(property, updater.apply(initialVal, [initialVal].concat(restArgs))); } function getInPath(obj, path) { + /*jshint eqnull:true */ for (var i = 0, l = path.length; obj != null && i < l; i++) { obj = obj[path[i]]; } @@ -450,7 +451,7 @@ var restArgs = Array.prototype.slice.call(arguments, 2); var initialVal = getInPath(this, path); - return this.setIn(path, updater.apply(initialVal, [initialVal].concat(restArgs))) + return this.setIn(path, updater.apply(initialVal, [initialVal].concat(restArgs))); } function asMutableObject(opts) { diff --git a/src/seamless-immutable.js b/src/seamless-immutable.js index 496c0dc..46eebce 100644 --- a/src/seamless-immutable.js +++ b/src/seamless-immutable.js @@ -435,10 +435,11 @@ function update(property, updater) { var restArgs = Array.prototype.slice.call(arguments, 2); var initialVal = this[property]; - return this.set(property, updater.apply(initialVal, [initialVal].concat(restArgs))) + return this.set(property, updater.apply(initialVal, [initialVal].concat(restArgs))); } function getInPath(obj, path) { + /*jshint eqnull:true */ for (var i = 0, l = path.length; obj != null && i < l; i++) { obj = obj[path[i]]; } @@ -450,7 +451,7 @@ var restArgs = Array.prototype.slice.call(arguments, 2); var initialVal = getInPath(this, path); - return this.setIn(path, updater.apply(initialVal, [initialVal].concat(restArgs))) + return this.setIn(path, updater.apply(initialVal, [initialVal].concat(restArgs))); } function asMutableObject(opts) {