Skip to content

Commit

Permalink
[changed] createChainedFunction to chain many functions, and to throw…
Browse files Browse the repository at this point in the history
… if non-functions are provided.
  • Loading branch information
mtscout6 committed Jun 13, 2015
1 parent 4d7c29d commit eb1e1c9
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 12 deletions.
30 changes: 18 additions & 12 deletions src/utils/createChainedFunction.js
@@ -1,25 +1,31 @@
import isFunction from 'lodash.isfunction';

/**
* Safe chained function
*
* Will only create a new function if needed,
* otherwise will pass back existing functions or null.
*
* @param {function} one
* @param {function} two
* @param {function} functions to chain
* @returns {function|null}
*/
function createChainedFunction(one, two) {
let hasOne = typeof one === 'function';
let hasTwo = typeof two === 'function';
function createChainedFunction(...funcs) {
return funcs
.filter(f => f != null)
.reduce((acc, f) => {
if (!isFunction(f)) {
throw new Error('Invalid Argument Type, must only provide functions, undefined, or null.');
}

if (!hasOne && !hasTwo) { return null; }
if (!hasOne) { return two; }
if (!hasTwo) { return one; }
if (acc === null) {
return f;
}

return function chainedFunction() {
one.apply(this, arguments);
two.apply(this, arguments);
};
return function chainedFunction(...args) {
acc.apply(this, args);
f.apply(this, args);
};
}, null);
}

export default createChainedFunction;
71 changes: 71 additions & 0 deletions test/utils/createChainedFunctionSpec.js
@@ -0,0 +1,71 @@
/* eslint no-new-func: 0 */
import createChainedFunction from '../../src/utils/createChainedFunction';

describe('createChainedFunction', function() {
it('returns null with no arguments', function() {
expect(createChainedFunction()).to.equal(null);
});

it('returns original function when single function is provided', function() {
const func1 = sinon.stub();
createChainedFunction(func1).should.equal(func1);
});

it('wraps two functions with another that invokes both when called', function() {
const func1 = sinon.stub();
const func2 = sinon.stub();
const chained = createChainedFunction(func1, func2);

chained
.should.not.equal(func1)
.and.should.not.equal(func2);

func1.should.not.have.been.called;
func2.should.not.have.been.called;

chained();

func1.should.have.been.calledOnce;
func2.should.have.been.calledOnce;
});

it('wraps multiple functions and invokes them in the order provided', function() {
const results = [];
const func1 = () => results.push(1);
const func2 = () => results.push(2);
const func3 = () => results.push(3);
const chained = createChainedFunction(func1, func2, func3);
chained();
results.should.eql([1, 2, 3]);
});

it('forwards arguments to all chained functions', function() {
const in1 = 'herpa derpa';
const in2 = {
herpa: 'derpa'
};

const func = (arg1, arg2) => {
arg1.should.equal(in1);
arg2.should.equal(in2);
};

const chained = createChainedFunction(func, func, func);
chained(in1, in2);
});

it('throws when func is not provided', function() {
expect(() => {
createChainedFunction({ herpa: 'derpa' });
}).to.throw(/Invalid Argument Type/);
});

it('works with new Function call', function() {
const results = [];
const func1 = new Function('results', 'results.push(1);');
const func2 = new Function('results', 'results.push(2);');
const chained = createChainedFunction(func1, func2);
chained(results);
results.should.eql([1, 2]);
});
});

0 comments on commit eb1e1c9

Please sign in to comment.