Skip to content
This repository has been archived by the owner on Jan 22, 2023. It is now read-only.

Commit

Permalink
feat(transform): add function
Browse files Browse the repository at this point in the history
  • Loading branch information
David Zukowski committed Jan 23, 2017
1 parent ef4c6d3 commit 278e209
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/bundles/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,14 @@ export { default as tail } from '../tail'
export { default as take } from '../take'
export { default as takeUntil } from '../takeUntil'
export { default as takeWhile } from '../takeWhile'
export { default as tap } from '../tap'
export { default as test } from '../test'
export { default as times } from '../times'
export { default as toLower } from '../toLower'
export { default as toUpper } from '../toUpper'
export { default as tap } from '../tap'
export { default as toPairs } from '../toPairs'
export { default as trace } from '../trace'
export { default as transform } from '../transform'
export { default as trim } from '../trim'
export { default as type } from '../type'
export { default as unless } from '../unless'
Expand Down
47 changes: 47 additions & 0 deletions src/transform.js
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
})
121 changes: 121 additions & 0 deletions tests/transform.spec.js
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/
)
})

0 comments on commit 278e209

Please sign in to comment.