Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

immutable-ops

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

immutable-ops - npm Package Compare versions

Comparing version 0.1.0 to 0.2.0

25

lib/index.js

@@ -25,2 +25,6 @@ 'use strict';

var _ramdaSrcWrap = require('ramda/src/wrap');
var _ramdaSrcWrap2 = _interopRequireDefault(_ramdaSrcWrap);
var _ramdaSrc__ = require('ramda/src/__');

@@ -100,3 +104,3 @@

var newObj = {};
prepareNewObject(newObj);
prepareNewObject(opts, newObj);
acc[curr] = newObj;

@@ -260,6 +264,2 @@ return newObj;

if (hasChanges) {
addCanMutateTag(opts, nextObject);
}
return nextObject;

@@ -338,3 +338,2 @@ }

var vals = forceArray(_vals);
var newArr = arr.slice();

@@ -348,3 +347,3 @@ prepareNewObject(opts, newArr);

function immutableArrInsert(opts, index, _vals, arr) {
if (canMutate(arr)) return mutableArrInsert(index, _vals, arr);
if (canMutate(arr)) return mutableArrInsert(opts, index, _vals, arr);
return immutableArrSplice(opts, index, 0, _vals, arr);

@@ -462,9 +461,15 @@ }

boundOperations.batched = function (func) {
function batchWrapper() {
var func = arguments[0];
var args = Array.prototype.slice.call(arguments, 1);
opts.open();
var returnValue = func();
var returnValue = func.apply(null, args);
opts.close();
return returnValue;
};
}
boundOperations.batched = batchWrapper;
boundOperations.batch = (0, _ramdaSrcWrap2['default'])(_ramdaSrc__2['default'], batchWrapper);
boundOperations.mutatedObjects = opts.mutatedObjects;

@@ -471,0 +476,0 @@

211

lib/test/testOperations.js

@@ -17,2 +17,10 @@ 'use strict';

var _deepFreeze = require('deep-freeze');
var _deepFreeze2 = _interopRequireDefault(_deepFreeze);
var _ramdaSrcCompose = require('ramda/src/compose');
var _ramdaSrcCompose2 = _interopRequireDefault(_ramdaSrcCompose);
_chai2['default'].use(_sinonChai2['default']);

@@ -28,6 +36,18 @@ var expect = _chai2['default'].expect;

it('wrapBatched', function () {
var pushFour = ops.push(4);
var pushFive = ops.push(5);
var arr = (0, _deepFreeze2['default'])([1, 2, 3]);
var pusher = ops.batch((0, _ramdaSrcCompose2['default'])(pushFive, pushFour));
expect(pusher).to.be.a('function');
var result = pusher(arr);
expect(result).to.deep.equal([1, 2, 3, 4, 5]);
});
describe('object', function () {
describe('batched mutations', function () {
it('deepMerges', function () {
var baseObj = {
var baseObj = (0, _deepFreeze2['default'])({
change: 'Tommi',

@@ -39,4 +59,4 @@ dontChange: 25,

}
};
var mergeObj = {
});
var mergeObj = (0, _deepFreeze2['default'])({
change: 'None',

@@ -48,3 +68,3 @@ add: 'US',

}
};
});
var result = undefined;

@@ -74,6 +94,6 @@ var merger = ops.deepMerge(mergeObj);

it('omits a single key', function () {
var obj = {
var obj = (0, _deepFreeze2['default'])({
name: 'Tommi',
age: 25
};
});

@@ -93,6 +113,6 @@ var result = undefined;

it('omits an array of keys', function () {
var obj = {
var obj = (0, _deepFreeze2['default'])({
name: 'Tommi',
age: 25
};
});

@@ -113,3 +133,3 @@ var result = undefined;

it('sets a value in path', function () {
var obj = {
var obj = (0, _deepFreeze2['default'])({
first: {

@@ -123,3 +143,3 @@ second: {

maintain: true
};
});
var result = undefined;

@@ -143,2 +163,90 @@

});
describe('immutable ops', function () {
it('deepMerges', function () {
var baseObj = (0, _deepFreeze2['default'])({
change: 'Tommi',
dontChange: 25,
deeper: {
dontChange: 'John',
change: 30
}
});
var mergeObj = (0, _deepFreeze2['default'])({
change: 'None',
add: 'US',
deeper: {
add: 'US',
change: 35
}
});
var merger = ops.deepMerge(mergeObj);
var result = merger(baseObj);
expect((0, _index.canMutate)(result)).to.be['false'];
expect((0, _index.canMutate)(result.deeper)).to.be['false'];
expect(result).to.not.equal(baseObj);
expect(result).to.contain.all.keys(['change', 'dontChange', 'add', 'deeper']);
expect(result.change).to.not.equal(baseObj.change);
expect(result.dontChange).to.equal(baseObj.dontChange);
expect(result.deeper).to.not.equal(baseObj.deeper);
expect(result.deeper).to.contain.all.keys(['dontChange', 'change', 'add']);
expect(result.deeper.dontChange).to.equal(baseObj.deeper.dontChange);
expect(result.deeper.change).to.not.equal(baseObj.deeper.change);
});
it('omits a single key', function () {
var obj = (0, _deepFreeze2['default'])({
name: 'Tommi',
age: 25
});
var omitter = ops.omit('age');
var result = omitter(obj);
expect((0, _index.canMutate)(result)).to.be['false'];
expect(result).to.not.contain.keys(['age']);
});
it('omits an array of keys', function () {
var obj = (0, _deepFreeze2['default'])({
name: 'Tommi',
age: 25
});
var omitter = ops.omit(['age']);
var result = omitter(obj);
expect((0, _index.canMutate)(result)).to.be['false'];
expect(result).to.not.contain.keys(['age']);
});
it('sets a value in path', function () {
var obj = (0, _deepFreeze2['default'])({
first: {
second: {
value: 'value',
maintain: true
},
maintain: true
},
maintain: true
});
var setter = ops.setIn('first.second.value', 'anotherValue');
var result = setter(obj);
expect((0, _index.canMutate)(result)).to.be['false'];
expect(result).not.to.equal(obj);
expect(result.first.second.value).to.equal('anotherValue');
expect(result.maintain).to.be['true'];
expect(result.first.maintain).to.be['true'];
expect(result.first.second.maintain).to.be['true'];
});
});
});

@@ -150,4 +258,4 @@

var push = ops.push;
var arr = [5, 4];
var pusher = push([1, 2, 3]);
var arr = (0, _deepFreeze2['default'])([5, 4]);
var pusher = push((0, _deepFreeze2['default'])([1, 2, 3]));
var result = ops.batched(function () {

@@ -164,4 +272,4 @@ return pusher(arr);

var insert = ops.insert;
var arr = [1, 2, 5];
var inserter = insert(2, [3, 4]);
var arr = (0, _deepFreeze2['default'])([1, 2, 5]);
var inserter = insert(2, (0, _deepFreeze2['default'])([3, 4]));
var result = ops.batched(function () {

@@ -175,3 +283,3 @@ return inserter(arr);

it('filter', function () {
var arr = [0, 1, 2, 3];
var arr = (0, _deepFreeze2['default'])([0, 1, 2, 3]);
var result = undefined;

@@ -191,3 +299,3 @@

it('set', function () {
var arr = [1, 2, 987, 4];
var arr = (0, _deepFreeze2['default'])([1, 2, 987, 4]);

@@ -207,5 +315,72 @@ var result = ops.batched(function () {

var splice = ops.splice;
var arr = [1, 2, 3, 3, 3, 4];
var arr = (0, _deepFreeze2['default'])([1, 2, 3, 3, 3, 4]);
var splicer = splice(2, 2, []);
var result = ops.batched(function () {
return splicer(arr);
});
expect(result).to.deep.equal([1, 2, 3, 4]);
});
it('splice with additions', function () {
var splice = ops.splice;
var arr = (0, _deepFreeze2['default'])([1, 5]);
var splicer = splice(1, 0, [2, 3, 4]);
var result = ops.batched(function () {
return splicer(arr);
});
expect(result).to.deep.equal([1, 2, 3, 4, 5]);
});
});
describe('immutable ops', function () {
it('push', function () {
var push = ops.push;
var arr = (0, _deepFreeze2['default'])([5, 4]);
var pusher = push((0, _deepFreeze2['default'])([1, 2, 3]));
var result = pusher(arr);
expect(result).to.not.equal(arr);
expect(result).to.deep.equal([5, 4, 1, 2, 3]);
});
it('insert', function () {
var insert = ops.insert;
var arr = (0, _deepFreeze2['default'])([1, 2, 5]);
var inserter = insert(2, (0, _deepFreeze2['default'])([3, 4]));
var result = inserter(arr);
expect(result).to.deep.equal([1, 2, 3, 4, 5]);
});
it('filter', function () {
var arr = (0, _deepFreeze2['default'])([0, 1, 2, 3]);
var result = ops.filter(function (item) {
return item % 2 === 0;
}, arr);
expect(result).to.deep.equal([0, 2]);
expect((0, _index.canMutate)(result)).to.be['false'];
});
it('set', function () {
var arr = (0, _deepFreeze2['default'])([1, 2, 987, 4]);
var setter = ops.set(2, 3);
var result = setter(arr);
expect((0, _index.canMutate)(result)).to.be['false'];
expect(result).to.deep.equal([1, 2, 3, 4]);
});
it('splice with deletions', function () {
var splice = ops.splice;
var arr = (0, _deepFreeze2['default'])([1, 2, 3, 3, 3, 4]);
var splicer = splice(2, 2, []);
var result = splicer(arr);

@@ -217,3 +392,3 @@ expect(result).to.deep.equal([1, 2, 3, 4]);

var splice = ops.splice;
var arr = [1, 5];
var arr = (0, _deepFreeze2['default'])([1, 5]);
var splicer = splice(1, 0, [2, 3, 4]);

@@ -220,0 +395,0 @@

{
"name": "immutable-ops",
"version": "0.1.0",
"version": "0.2.0",
"description": "A collection of functions to perform immutable operations on plain JavaScript objects",

@@ -10,4 +10,3 @@ "main": "lib/index.js",

},
"keywords": [
],
"keywords": [],
"author": "Tommi Kaikkonen <tommi.kaikkonen@aalto.fi>",

@@ -24,2 +23,3 @@ "repository": {

"chai": "^3.0.0",
"deep-freeze": "0.0.1",
"eslint": "^1.10.1",

@@ -26,0 +26,0 @@ "eslint-config-airbnb": "1.0.0",

immutable-ops
===============
A collection of functions to perform immutable operations on plain JavaScript objects and arrays. Aims to have the simplicity and small size of `seamless-immutable`, but with a functional, curried API, no special immutable object type, and batched mutations.
A collection of functions to perform immutable operations on plain JavaScript objects and arrays.
Like [updeep](https://github.com/substantial/updeep) but with batched mutations and no freezing.
Like [icepick](https://github.com/aearly/icepick), but with batched mutations and a curried API that puts the target object as the last argument. No freezing.
## Features

@@ -19,7 +23,7 @@

## Usage
## Example Usage
```javascript
import compose from 'ramda/src/compose';
import getImmutableOps from 'immutable-ops';
import getOps from 'immutable-ops';

@@ -48,3 +52,3 @@ // These are all the available functions.

__,
} = getImmutableOps({
} = getOps({
// These are the default options.

@@ -54,60 +58,25 @@ curried: true

const person = {
name: 'Tommi',
age: 25,
location: {
city: 'New York',
country: 'US',
},
};
const arr = [1, 2, 3];
// All functions are curried. This returns a function that should be
// called with the last argument, the object to be operated on.
const moveToNY = mergeDeep({ location: { city: 'New York' }});
const moveToSF = mergeDeep({ location: { city: 'San Francisco' }});
const pushFour = ops.push(4);
const pushFive = ops.push(5);
const updatedPerson = moveToNY(person);
// All functions are curried. These functions
// still need the final argument, the array to
// operate on.
expect(pushFive).to.be.a('function');
// If any changes are not made, the same object is returned.
updatedPerson === person
// true
const pushFourAndFive = compose(pushFive, pushFour);
const becomeABanker = setIn('occupation.title', 'Investment Banker');
const advanceCareer = compose(becomeABanker, moveToNY)
const result = pushFourAndFive(arr);
// Two new arrays were created during `pushFourAndFive` eecution.
expect(result).to.deep.equal([1, 2, 3, 4, 5]);
const personWithJob = advanceCareer(person);
console.log(personWithJob === person);
// false
const batchedPushFourAndFive = ops.batch(pushFourAndFive);
console.log(personWithJob);
// {
// name: 'Tommi',
// age: 25,
// location: {
// city: 'New York',
// country: 'US',
// },
// occupation: {
// title: 'Investment Banker',
// }
// }
const runOperations = compose(advanceCareer, moveToSf);
const personWithJobTwo = ops.batched(() => runOperations(person));
console.log(person === personWithJobTwo)
// false
// All data is still immutable. `ops.batched(() => runOperations(person))` returns a deeply equal result to the just running `runOperations(person)`. The difference is in the amount of objects created during `runOperations`. When `moveToSF` is first called, it creates a new object for the `location` key with the updated `city`. When `advanceCareer` calls `moveToNY`, that `location` object is mutated instead of a new one being created.
console.log(personWithJobTwo);
// {
// name: 'Tommi',
// age: 25,
// location: {
// city: 'New York',
// country: 'US',
// },
// occupation: {
// title: 'Investment Banker',
// }
// }
const sameResult = batchedPushFourAndFive(arr);
// Only one new array is created during `batchedPushFourAndFive` execution.
// `immutable-ops` keeps track of objects mutated during the wrapped
// function, and applies the same operations with mutations to those objects.
expect(result).to.deep.equal([1, 2, 3, 4, 5]);
```

@@ -117,3 +86,3 @@

You can batch operations by calling `ops.batched(func)` with a function.
You can run operations in a mutation batch by calling `ops.batched(func)` with a function, and you can create a batch-wrapped function with `const batchedFunc = ops.batch(funcToWrap)`.

@@ -124,2 +93,4 @@ When `immutable-ops` creates a new object or array during batched mutations to preserve immutability, it tags it as a mutable object (by adding an unenumerable `@@_____canMutate` property) and pushes its reference to an array of `mutatedObjects`. All consecutive functions applied will execute a mutating operations for objects that have the tag. This applies for tagged objects found in nested structures too.

The overhead of keeping track of mutated objects should be a sufficient tradeoff to creating lots of new objects to applyi multiple consecutive operations, unless you're working on a really big set of data.
## Currying

@@ -138,2 +109,12 @@

## Batched Mutations API
### batched(functionToRun)
Executes `functionToRun` as a batched mutation and returns the return value of `functionToRun`.`functionToRun` will be called without arguments. During `functionToRun` execution, `immutable-ops` will keep track of new objects created during operations, and apply further operations with mutations to those objects.
### batch(functionToWrap)
Like `batched`, but returns a function that wraps `functionToWrap` to be executed as a batch. `functionToWrap` is also curried. When `functionToWrap` is executed (all arguments are passed), all operations run during its execution will apply mutations instead of creating new objects whenever possible.
## Object API

@@ -140,0 +121,0 @@

import forOwn from 'lodash/forOwn';
import isArrayLike from 'lodash/isArrayLike';
import curry from 'ramda/src/curry';
import wrap from 'ramda/src/wrap';
import placeholder from 'ramda/src/__';

@@ -76,3 +77,3 @@

const newObj = {};
prepareNewObject(newObj);
prepareNewObject(opts, newObj);
acc[curr] = newObj;

@@ -238,6 +239,2 @@ return newObj;

if (hasChanges) {
addCanMutateTag(opts, nextObject);
}
return nextObject;

@@ -316,3 +313,2 @@ }

const vals = forceArray(_vals);
const newArr = arr.slice();

@@ -326,3 +322,3 @@ prepareNewObject(opts, newArr);

function immutableArrInsert(opts, index, _vals, arr) {
if (canMutate(arr)) return mutableArrInsert(index, _vals, arr);
if (canMutate(arr)) return mutableArrInsert(opts, index, _vals, arr);
return immutableArrSplice(opts, index, 0, _vals, arr);

@@ -440,9 +436,15 @@ }

boundOperations.batched = func => {
function batchWrapper() {
const func = arguments[0];
const args = Array.prototype.slice.call(arguments, 1);
opts.open();
const returnValue = func();
const returnValue = func.apply(null, args);
opts.close();
return returnValue;
};
}
boundOperations.batched = batchWrapper;
boundOperations.batch = wrap(placeholder, batchWrapper);
boundOperations.mutatedObjects = opts.mutatedObjects;

@@ -449,0 +451,0 @@

import chai from 'chai';
import sinonChai from 'sinon-chai';
import getOps, { canMutate } from '../index';
import freeze from 'deep-freeze';
import compose from 'ramda/src/compose';

@@ -15,6 +17,18 @@ chai.use(sinonChai);

it('wrapBatched', () => {
const pushFour = ops.push(4);
const pushFive = ops.push(5);
const arr = freeze([1, 2, 3]);
const pusher = ops.batch(compose(pushFive, pushFour));
expect(pusher).to.be.a('function');
const result = pusher(arr);
expect(result).to.deep.equal([1, 2, 3, 4, 5]);
});
describe('object', () => {
describe('batched mutations', () => {
it('deepMerges', () => {
const baseObj = {
const baseObj = freeze({
change: 'Tommi',

@@ -26,4 +40,4 @@ dontChange: 25,

},
};
const mergeObj = {
});
const mergeObj = freeze({
change: 'None',

@@ -35,3 +49,3 @@ add: 'US',

},
};
});
let result;

@@ -61,6 +75,6 @@ const merger = ops.deepMerge(mergeObj);

it('omits a single key', () => {
const obj = {
const obj = freeze({
name: 'Tommi',
age: 25,
};
});

@@ -80,6 +94,6 @@ let result;

it('omits an array of keys', () => {
const obj = {
const obj = freeze({
name: 'Tommi',
age: 25,
};
});

@@ -100,3 +114,3 @@ let result;

it('sets a value in path', () => {
const obj = {
const obj = freeze({
first: {

@@ -110,3 +124,3 @@ second: {

maintain: true,
};
});
let result;

@@ -130,2 +144,90 @@

});
describe('immutable ops', () => {
it('deepMerges', () => {
const baseObj = freeze({
change: 'Tommi',
dontChange: 25,
deeper: {
dontChange: 'John',
change: 30,
},
});
const mergeObj = freeze({
change: 'None',
add: 'US',
deeper: {
add: 'US',
change: 35,
},
});
const merger = ops.deepMerge(mergeObj);
const result = merger(baseObj);
expect(canMutate(result)).to.be.false;
expect(canMutate(result.deeper)).to.be.false;
expect(result).to.not.equal(baseObj);
expect(result).to.contain.all.keys(['change', 'dontChange', 'add', 'deeper']);
expect(result.change).to.not.equal(baseObj.change);
expect(result.dontChange).to.equal(baseObj.dontChange);
expect(result.deeper).to.not.equal(baseObj.deeper);
expect(result.deeper).to.contain.all.keys(['dontChange', 'change', 'add']);
expect(result.deeper.dontChange).to.equal(baseObj.deeper.dontChange);
expect(result.deeper.change).to.not.equal(baseObj.deeper.change);
});
it('omits a single key', () => {
const obj = freeze({
name: 'Tommi',
age: 25,
});
const omitter = ops.omit('age');
const result = omitter(obj);
expect(canMutate(result)).to.be.false;
expect(result).to.not.contain.keys(['age']);
});
it('omits an array of keys', () => {
const obj = freeze({
name: 'Tommi',
age: 25,
});
const omitter = ops.omit(['age']);
const result = omitter(obj);
expect(canMutate(result)).to.be.false;
expect(result).to.not.contain.keys(['age']);
});
it('sets a value in path', () => {
const obj = freeze({
first: {
second: {
value: 'value',
maintain: true,
},
maintain: true,
},
maintain: true,
});
const setter = ops.setIn('first.second.value', 'anotherValue');
const result = setter(obj);
expect(canMutate(result)).to.be.false;
expect(result).not.to.equal(obj);
expect(result.first.second.value).to.equal('anotherValue');
expect(result.maintain).to.be.true;
expect(result.first.maintain).to.be.true;
expect(result.first.second.maintain).to.be.true;
});
});
});

@@ -137,4 +239,4 @@

const push = ops.push;
const arr = [5, 4];
const pusher = push([1, 2, 3]);
const arr = freeze([5, 4]);
const pusher = push(freeze([1, 2, 3]));
const result = ops.batched(() => pusher(arr));

@@ -149,4 +251,4 @@

const insert = ops.insert;
const arr = [1, 2, 5];
const inserter = insert(2, [3, 4]);
const arr = freeze([1, 2, 5]);
const inserter = insert(2, freeze([3, 4]));
const result = ops.batched(() => inserter(arr));

@@ -158,3 +260,3 @@

it('filter', () => {
const arr = [0, 1, 2, 3];
const arr = freeze([0, 1, 2, 3]);
let result;

@@ -172,3 +274,3 @@

it('set', () => {
const arr = [1, 2, 987, 4];
const arr = freeze([1, 2, 987, 4]);

@@ -188,5 +290,66 @@ const result = ops.batched(() => {

const splice = ops.splice;
const arr = [1, 2, 3, 3, 3, 4];
const arr = freeze([1, 2, 3, 3, 3, 4]);
const splicer = splice(2, 2, []);
const result = ops.batched(() => splicer(arr));
expect(result).to.deep.equal([1, 2, 3, 4]);
});
it('splice with additions', () => {
const splice = ops.splice;
const arr = freeze([1, 5]);
const splicer = splice(1, 0, [2, 3, 4]);
const result = ops.batched(() => splicer(arr));
expect(result).to.deep.equal([1, 2, 3, 4, 5]);
});
});
describe('immutable ops', () => {
it('push', () => {
const push = ops.push;
const arr = freeze([5, 4]);
const pusher = push(freeze([1, 2, 3]));
const result = pusher(arr);
expect(result).to.not.equal(arr);
expect(result).to.deep.equal([5, 4, 1, 2, 3]);
});
it('insert', () => {
const insert = ops.insert;
const arr = freeze([1, 2, 5]);
const inserter = insert(2, freeze([3, 4]));
const result = inserter(arr);
expect(result).to.deep.equal([1, 2, 3, 4, 5]);
});
it('filter', () => {
const arr = freeze([0, 1, 2, 3]);
const result = ops.filter(item => item % 2 === 0, arr);
expect(result).to.deep.equal([0, 2]);
expect(canMutate(result)).to.be.false;
});
it('set', () => {
const arr = freeze([1, 2, 987, 4]);
const setter = ops.set(2, 3);
const result = setter(arr);
expect(canMutate(result)).to.be.false;
expect(result).to.deep.equal([1, 2, 3, 4]);
});
it('splice with deletions', () => {
const splice = ops.splice;
const arr = freeze([1, 2, 3, 3, 3, 4]);
const splicer = splice(2, 2, []);
const result = splicer(arr);

@@ -198,3 +361,3 @@ expect(result).to.deep.equal([1, 2, 3, 4]);

const splice = ops.splice;
const arr = [1, 5];
const arr = freeze([1, 5]);
const splicer = splice(1, 0, [2, 3, 4]);

@@ -201,0 +364,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc