This repository has been archived by the owner on Jan 22, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
David Zukowski
committed
Jan 23, 2017
1 parent
ef4c6d3
commit 278e209
Showing
3 changed files
with
170 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import _curry2 from './internal/_curry2' | ||
import _hasOwn from './internal/_hasOwn' | ||
|
||
/** | ||
* @name transform | ||
* @signature String k, Any v => {k: (v -> v)} -> {k:v} -> {k:v} | ||
* @since v0.18.0 | ||
* @description | ||
* Recursively applies property-based transforms to the target object's own | ||
* properties. Transforms defined for non-existent properties are ignored, | ||
* and properties without a corresponding transform are passed through | ||
* unmodified. | ||
* @example | ||
* transform( | ||
* { name: toUpper, details: { location: toUpper } }, | ||
* { | ||
* name: 'joe', | ||
* details: { | ||
* age: 20, | ||
* location: 'usa' | ||
* } | ||
* } | ||
* ) // => { name: 'JOE', details: { age: 40, location: 'USA' } } | ||
*/ | ||
export default _curry2(function transform (transforms, obj) { | ||
var res = {} | ||
, prop | ||
|
||
for (prop in obj) { | ||
if (_hasOwn.call(obj, prop) && _hasOwn.call(transforms, prop)) { | ||
if (typeof transforms[prop] === 'object') { | ||
res[prop] = transform(transforms[prop], obj[prop]) | ||
} else if (typeof transforms[prop] === 'function') { | ||
res[prop] = transforms[prop](obj[prop]) | ||
} else { | ||
throw new Error( | ||
'Invalid transformation supplied under the key "' + prop + '". ' + | ||
'Transformation must be either a function or object, but was ' + | ||
'"' + typeof transforms[prop] + '".' | ||
) | ||
} | ||
} else { | ||
res[prop] = obj[prop] | ||
} | ||
} | ||
return res | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
const test = require('ava') | ||
, sinon = require('sinon') | ||
, { | ||
filter | ||
, gte | ||
, head | ||
, toUpper | ||
, transform | ||
} = require('../dist/redash') | ||
|
||
test('properly reports its arity (is binary)', (t) => { | ||
t.is(transform.length, 2) | ||
}) | ||
|
||
test('is curried', (t) => { | ||
t.is(typeof transform({}), 'function') | ||
}) | ||
|
||
test('shallowly transforms based on a spec', (t) => { | ||
const spec = { | ||
name: x => x.toUpperCase() | ||
, friends: () => [] | ||
} | ||
t.deepEqual(transform(spec, { | ||
name: 'joe', friends: [1, 2, 3] | ||
}), { | ||
name: 'JOE' | ||
, friends: [] | ||
}) | ||
}) | ||
|
||
test('only runs the transform if the property exists', (t) => { | ||
const spy = sinon.spy() | ||
|
||
transform({ foo: spy }, { name: 'joe' }) | ||
t.is(spy.callCount, 0) | ||
|
||
transform({ name: spy }, { name: 'joe' }) | ||
t.is(spy.callCount, 1) | ||
}) | ||
|
||
test('passes through properties without a transform', (t) => { | ||
const spec = { | ||
name: x => x.toUpperCase() | ||
} | ||
t.deepEqual(transform(spec, { | ||
name: 'joe' | ||
, friends: [1, 2, 3] | ||
}), { | ||
name: 'JOE' | ||
, friends: [1, 2, 3] | ||
}) | ||
}) | ||
|
||
test('transforms recursively', (t) => { | ||
/* eslint-disable */ | ||
const spec = { | ||
info: { | ||
things: head | ||
, data: { | ||
name: toUpper | ||
, nums: filter(gte(10)) | ||
} | ||
} | ||
} | ||
/* eslint-enasble */ | ||
|
||
t.deepEqual(transform(spec, { | ||
foo: 'bar', | ||
info: { | ||
things: [1, 2, 3, 4] | ||
, bar: 'baz' | ||
, data: { | ||
name: 'joe' | ||
, nums: [7, 8, 9, 10, 11, 12] | ||
} | ||
} | ||
}), { | ||
foo: 'bar', | ||
info: { | ||
things: 1 | ||
, bar: 'baz' | ||
, data: { | ||
name: 'JOE' | ||
, nums: [10, 11, 12] | ||
} | ||
} | ||
}) | ||
}) | ||
|
||
test('transforms undefined/null properties', (t) => { | ||
t.deepEqual(transform({ | ||
foo: () => true | ||
}, { | ||
foo: null | ||
}), { | ||
foo: true | ||
}) | ||
}) | ||
|
||
test('does not transform inherited properties', (t) => { | ||
function A () {} | ||
A.prototype.bar = () => {} | ||
const obj = new A() | ||
obj.foo = 'foo' | ||
|
||
t.deepEqual(transform({ | ||
bar: () => 'BAR' | ||
, foo: () => 'FOO' | ||
}, obj), { | ||
bar: A.prototype.bar | ||
, foo: 'FOO' | ||
}) | ||
}) | ||
|
||
test('throws for invalid transforms', (t) => { | ||
t.throws( | ||
() => transform({ bar: 'baz' }, { bar: 'bar' }), | ||
/Invalid transformation/ | ||
) | ||
}) |