This commit is contained in:
Aaron Jensen 2015-08-04 23:36:40 -07:00
parent c5ab11e176
commit 750200df76
9 changed files with 89 additions and 17 deletions

View File

@ -1,6 +1,7 @@
# Change Log # Change Log
## [unreleased] ## [unreleased]
* Add `u.if` to conditionally update objects.
## [0.3.1] ## [0.3.1]
* Actually expose `u.in`. * Actually expose `u.in`.

View File

@ -128,6 +128,22 @@ u({
// => { x: { a: { b: 3 } } }; // => { x: { a: { b: 3 } } };
``` ```
### `u.if(predicate(, updates)(, object))`
Apply updates only if `predicate` is truthy or, if `predicate` is a function,
if it evaluates to truthy when called with `object`.
```js
var obj = { a: 2 };
function isEven(x) { return x % 2 === 0; }
function inc(x) { return x + 1; }
u({
a: u.if(isEven, inc),
}, obj);
// => { a: 3 }
```
### `u.omit(predicate(, object))` ### `u.omit(predicate(, object))`
Remove properties. See [`_.omit`](https://lodash.com/docs#omit). Remove properties. See [`_.omit`](https://lodash.com/docs#omit).

16
lib/if.js Normal file
View File

@ -0,0 +1,16 @@
import curry from 'lodash/function/curry';
import update from './update';
function updateIf(predicate, updates, object) {
const test = typeof predicate === 'function' ?
predicate(object) :
predicate;
if (!test) {
return object;
}
return update(updates, object);
}
export default curry(updateIf);

View File

@ -1,5 +1,6 @@
import update from './update'; import update from './update';
import updateIn from './updateIn'; import _in from './in';
import _if from './if';
import omit from './omit'; import omit from './omit';
import reject from './reject'; import reject from './reject';
import withDefault from './withDefault'; import withDefault from './withDefault';
@ -12,7 +13,8 @@ function updateAndFreeze(updates, obj) {
const updeep = curry(updateAndFreeze); const updeep = curry(updateAndFreeze);
updeep.in = updateIn; updeep.if = _if;
updeep.in = _in;
updeep.freeze = freeze; updeep.freeze = freeze;
updeep.omit = omit; updeep.omit = omit;
updeep.reject = reject; updeep.reject = reject;

View File

@ -1,5 +1,5 @@
import { expect } from 'chai'; import { expect } from 'chai';
import freeze from '../lib/freeze'; import u from '../lib';
describe('u.freeze', () => { describe('u.freeze', () => {
afterEach(() => { afterEach(() => {
@ -8,21 +8,21 @@ describe('u.freeze', () => {
it('freezes objects', () => { it('freezes objects', () => {
const obj = {}; const obj = {};
freeze(obj); u.freeze(obj);
expect(Object.isFrozen(obj)).to.be.true; expect(Object.isFrozen(obj)).to.be.true;
}); });
it('freezes nested objects', () => { it('freezes nested objects', () => {
const obj = { foo: { bar: 3 } }; const obj = { foo: { bar: 3 } };
freeze(obj); u.freeze(obj);
expect(Object.isFrozen(obj.foo)).to.be.true; expect(Object.isFrozen(obj.foo)).to.be.true;
}); });
it('freezes nested arrays', () => { it('freezes nested arrays', () => {
const obj = [[0]]; const obj = [[0]];
freeze(obj); u.freeze(obj);
expect(Object.isFrozen(obj)).to.be.true; expect(Object.isFrozen(obj)).to.be.true;
expect(Object.isFrozen(obj[0])).to.be.true; expect(Object.isFrozen(obj[0])).to.be.true;
@ -30,7 +30,7 @@ describe('u.freeze', () => {
it('ignores functions', () => { it('ignores functions', () => {
const obj = { foo: () => 1 }; const obj = { foo: () => 1 };
freeze(obj); u.freeze(obj);
expect(Object.isFrozen(obj.foo)).to.be.false; expect(Object.isFrozen(obj.foo)).to.be.false;
}); });
@ -38,7 +38,7 @@ describe('u.freeze', () => {
it('does not freeze children if the parent is already frozen', () => { it('does not freeze children if the parent is already frozen', () => {
const obj = { foo: {} }; const obj = { foo: {} };
Object.freeze(obj); Object.freeze(obj);
freeze(obj); u.freeze(obj);
expect(Object.isFrozen(obj.foo)).to.be.false; expect(Object.isFrozen(obj.foo)).to.be.false;
}); });
@ -46,20 +46,20 @@ describe('u.freeze', () => {
it('does not freeze in production', () => { it('does not freeze in production', () => {
process.env.NODE_ENV = 'production'; process.env.NODE_ENV = 'production';
const obj = {}; const obj = {};
freeze(obj); u.freeze(obj);
expect(Object.isFrozen(obj)).to.be.false; expect(Object.isFrozen(obj)).to.be.false;
}); });
it('handles null objects', () => { it('handles null objects', () => {
const obj = { foo: null }; const obj = { foo: null };
freeze(obj); u.freeze(obj);
expect(Object.isFrozen(obj)).to.be.true; expect(Object.isFrozen(obj)).to.be.true;
}); });
it('returns the same object', () => { it('returns the same object', () => {
const obj = {}; const obj = {};
const result = freeze(obj); const result = u.freeze(obj);
expect(result).to.equal(obj); expect(result).to.equal(obj);
}); });
}); });

36
test/if-spec.js Normal file
View File

@ -0,0 +1,36 @@
import { expect } from 'chai';
import u from '../lib';
describe('u.if', () => {
it('does not update if the predicate is false', () => {
const obj = { a: 0 };
const result = u.if(false, { b: 1 }, obj);
expect(result).to.eql(obj);
});
it('does update if the predicate is true', () => {
const obj = { a: 0 };
const result = u.if(true, { b: 1 }, obj);
expect(result).to.eql({ a: 0, b: 1 });
});
it('will use the result of a function passed as a predicate', () => {
const obj = { a: 0 };
const aIsThree = x => x.a === 3;
const result = u.if(aIsThree, { b: 1 }, obj);
expect(result).to.eql({ a: 0 });
});
it('can be partially applied', () => {
const obj = { a: 2 };
const isEven = x => x % 2 === 0;
const inc = x => x + 1;
const result = u({
a: u.if(isEven, inc),
}, obj);
expect(result).to.eql({ a: 3 });
});
});

View File

@ -1,35 +1,35 @@
import { expect } from 'chai'; import { expect } from 'chai';
import updateIn from '../lib/updateIn'; import u from '../lib';
describe('u.in', () => { describe('u.in', () => {
it('can update a single path described with a string', () => { it('can update a single path described with a string', () => {
const obj = { a: { b: 0 } }; const obj = { a: { b: 0 } };
const result = updateIn('a.b', 3, obj); const result = u.in('a.b', 3, obj);
expect(result).to.eql({ a: { b: 3 } }); expect(result).to.eql({ a: { b: 3 } });
}); });
it('can update a single path described with a string with a function', () => { it('can update a single path described with a string with a function', () => {
const inc = x => x + 1; const inc = x => x + 1;
const obj = { a: { b: 0 } }; const obj = { a: { b: 0 } };
const result = updateIn('a.b', inc, obj); const result = u.in('a.b', inc, obj);
expect(result).to.eql({ a: { b: 1 } }); expect(result).to.eql({ a: { b: 1 } });
}); });
it('can update a single path described with an array', () => { it('can update a single path described with an array', () => {
const obj = { a: { b: 0 } }; const obj = { a: { b: 0 } };
const result = updateIn(['a', 'b'], 3, obj); const result = u.in(['a', 'b'], 3, obj);
expect(result).to.eql({ a: { b: 3 } }); expect(result).to.eql({ a: { b: 3 } });
}); });
it('can update arrays', () => { it('can update arrays', () => {
const obj = { a: [0, 0, 0] }; const obj = { a: [0, 0, 0] };
const result = updateIn('a.1', 3, obj); const result = u.in('a.1', 3, obj);
expect(result).to.eql({ a: [0, 3, 0] }); expect(result).to.eql({ a: [0, 3, 0] });
}); });
it('can be partially applied', () => { it('can be partially applied', () => {
const obj = { a: { b: 0 } }; const obj = { a: { b: 0 } };
const result = updateIn('a.b')(3)(obj); const result = u.in('a.b')(3)(obj);
expect(result).to.eql({ a: { b: 3 } }); expect(result).to.eql({ a: { b: 3 } });
}); });
}); });

View File

@ -116,6 +116,7 @@ describe('updeep', () => {
it('has additional functions', () => { it('has additional functions', () => {
expect(u.freeze).to.be.a('function'); expect(u.freeze).to.be.a('function');
expect(u.if).to.be.a('function');
expect(u.in).to.be.a('function'); expect(u.in).to.be.a('function');
expect(u.omit).to.be.a('function'); expect(u.omit).to.be.a('function');
expect(u.withDefault).to.be.a('function'); expect(u.withDefault).to.be.a('function');