Skip to content

Commit

Permalink
✨ seq.chain path parameter fix #18
Browse files Browse the repository at this point in the history
  • Loading branch information
nlepage committed May 3, 2017
1 parent 33e26b1 commit 74831d6
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 16 deletions.
37 changes: 26 additions & 11 deletions src/seq/ChainWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import * as core from '../core'
import * as lang from '../lang'
import * as math from '../math'

import concat from 'lodash/concat'
import flow from 'lodash/flow'
import mapValues from 'lodash/mapValues'
import toPath from 'lodash/toPath'

/**
* Wrapper allowing to make sequences of immutadot functions calls on a value.<br/>
* Wrapper allowing to make sequences of immutadot functions calls on an object.<br/>
* Instances are created by calling {@link seq.chain}.<br/>
* The result of such sequences must be unwrapped with {@link seq.ChainWrapper#value}.
* @memberof seq.
Expand All @@ -21,30 +23,43 @@ class ChainWrapper {
/**
* This constructor should not be called directly.<br/>
* Instances are created by calling {@link seq.chain}.
* @param {*} value The value to wrap.
* @param {Object} object The object to wrap.
* @param {Array|string} [path] The path of the object on which functions are called.
* @see {@link seq.chain} for more information.
* @since 0.1.8
*/
constructor(value) {
this._value = value
constructor(object, path) {
this._object = object
this._path = path
this._flow = []
}

/**
* Translates a relative path to an absolute path, using <code>this._path</code> as a base path.
* @param {Array|string} path A relative path.
* @return {Array} Absolute path.
* @since 0.1.11
*/
_absolutePath(path) {
return concat(toPath(this._path), toPath(path))
}

/**
* Add a function call to the sequence.
* @param {function} fn The function to call.
* @param {Array|string} path The path of the property to be set.
* @param {...*} args The arguments for the function call.
* @returns {seq.ChainWrapper} The wrapper instance.
* @since 0.1.8
*/
_call(fn, args) {
this._flow.push(value => fn(value, ...args))
_call(fn, path, args) {
this._flow.push(object => fn(object, this._absolutePath(path), ...args))
return this
}

/**
* Executes the chain sequence to resolve the unwrapped value.
* @returns {Object} Returns the resolved unwrapped value.
* Executes the chain sequence to resolve the unwrapped object.
* @returns {Object} Returns the resolved unwrapped object.
* @example
* chain({ nested1: { prop: 'old' }, nested2: { prop: 1 } })
* .set('nested1.prop', 'new')
Expand All @@ -54,7 +69,7 @@ class ChainWrapper {
* @since 0.1.8
*/
value() {
return flow(this._flow)(this._value)
return flow(this._flow)(this._object)
}
}

Expand All @@ -69,8 +84,8 @@ class ChainWrapper {
ChainWrapper.prototype,
mapValues(
namespace,
fn => function(...args) {
return this._call(fn, args) // eslint-disable-line no-invalid-this
fn => function(path, ...args) {
return this._call(fn, path, args) // eslint-disable-line no-invalid-this
}
)
))
Expand Down
17 changes: 12 additions & 5 deletions src/seq/chain.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
import ChainWrapper from './ChainWrapper'

/**
* Creates an immutadot wrapper instance that wraps value allowing to make sequences of immutadot functions calls on it.<br/>
* The result of such sequences must be unwrapped with {@link seq#value|value}.
* Creates an immutadot wrapper instance that wraps <code>object</code> allowing to make sequences of immutadot functions calls on it.<br/>
* The result of such sequences must be unwrapped with {@link seq#value|value}.<br/>
* The object on which the functions are called may be changed with an optional <code>path</code>.
* @function chain
* @memberof seq
* @param {*} value The value to wrap.
* @param {Object} object The object to wrap.
* @param {Array|string} [path] The path of the object on which functions are called.
* @return {Object} Returns the new immutadot wrapper instance.
* @example
* @example <caption>Chain several operations</caption>
* chain({ nested1: { prop: 'old' }, nested2: { prop: 1 } })
* .set('nested1.prop', 'new')
* .unset('nested2.prop')
* .value() // => { nested1: { prop: 'new' }, nested2: {} }
* @example <caption>Chain on a path</caption>
* chain({ nested: { prop1: 'old', prop2: 1 } }, 'nested')
* .set('prop1', 'new')
* .unset('prop2')
* .value() // => { nested: { prop1: 'new' } }
* @see {@link seq#value|value} for more information.
* @since 0.1.8
*/
export default value => new ChainWrapper(value)
export default (object, path) => new ChainWrapper(object, path)
14 changes: 14 additions & 0 deletions src/seq/chain.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,18 @@ describe('Chain', () => {
nested2: { prop3: 'VALUE3' },
})
})

it('should apply modifications at a path', () => {
expect(chain(object, 'nested1')
.set('prop1', 'value5')
.unset('prop2')
.value())
.to.be.deep.equal({
nested1: { prop1: 'value5' },
nested2: {
prop3: 'value3',
prop4: 'value4',
},
})
})
})

0 comments on commit 74831d6

Please sign in to comment.