Skip to content

Commit

Permalink
feat: add argsPass (#437)
Browse files Browse the repository at this point in the history
Adds function for mapping arguments to predicates and combining those values using a further function.

Closes #370
  • Loading branch information
Undistraction committed Apr 23, 2018
1 parent 347c25f commit f7d7f7c
Show file tree
Hide file tree
Showing 4 changed files with 229 additions and 0 deletions.
38 changes: 38 additions & 0 deletions src/argsPass.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { useWith, curry, compose } from 'ramda';

import list from './list';
import isTruthy from './isTruthy';

/**
* Takes a combining predicate and a list of functions and returns a function which will map the
* arguments it receives to the list of functions and returns the result of passing the values
* returned from each function to the combining predicate. A combining predicate is a function that
* combines a list of Boolean values into a single Boolean value, such as `R.any` or `R.all`. It
* will test each value using `RA.isTruthy`, meaning the functions don't necessarily have to be
* predicates.
*
* The function returned is curried to the number of functions supplied, and if called with more
* arguments than functions, any remaining arguments are passed in to the combining predicate
* untouched.
*
* @func argsPass
* @memberOf RA
* @since {@link https://char0n.github.io/ramda-adjunct/2.7.0|v2.7.0}
* @category Logic
* @sig ((* -> Boolean) -> [*] -> Boolean) -> [(* -> Boolean), ...] -> (*...) -> Boolean
* @param {Function} combiningPredicate The predicate used to combine the values returned from the
* list of functions
* @param {Array} functions List of functions
* @return {Boolean} Returns the combined result of mapping arguments to functions
* @example
*
* RA.argsPass(R.all, [RA.isArray, RA.isBoolean, RA.isString])([], false, 'abc') //=> true
* RA.argsPass(R.all, [RA.isArray, RA.isBoolean, RA.isString])([], false, 1) //=> false
* RA.argsPass(R.any, [RA.isArray, RA.isBoolean, RA.isString])({}, 1, 'abc') //=> true
* RA.argsPass(R.any, [RA.isArray, RA.isBoolean, RA.isString])({}, 1, false) //=> false
* RA.argsPass(R.none, [RA.isArray, RA.isBoolean, RA.isString])({}, 1, false) //=> true
* RA.argsPass(R.none, [RA.isArray, RA.isBoolean, RA.isString])({}, 1, 'abc') //=> false
*/
export default curry((combiningPredicate, predicates) =>
useWith(compose(combiningPredicate(isTruthy), list), predicates)
);
15 changes: 15 additions & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,21 @@ declare namespace RamdaAdjunct {
*/
nonePass(predicates: Function[]): Function;

/**
* Takes a combining predicate and a list of functions and returns a function which will map
* the arguments it receives to the list of functions and returns the result of passing the
* values returned from each function to the combining predicate. A combining predicate is a
* function that combines a list of Boolean values into a single Boolean value, such as
* `R.any` or `R.all`. It will test each value using `RA.isTruthy`, meaning the functions
* don't necessarily have to be predicates.
*
* The function returned is curried to the number of functions supplied, and if called with
* more arguments than functions, any remaining arguments are passed in to the combining
* predicate untouched.
*/
argsPass(combiningPredicate: Function, predicates: Function[]): Function;
argsPass(combiningPredicate: Function): (predicates: Function[]) => Function;

/**
* Creates an array with all falsy values removed.
* The values false, null, 0, "", undefined, and NaN are falsy.
Expand Down
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,5 +143,6 @@ export { default as notBoth } from './notBoth';
export { default as neither } from './neither';
export { default as notAllPass } from './notAllPass';
export { default as nonePass } from './nonePass';
export { default as argsPass } from './argsPass';
// Types
export { default as Identity } from './fantasy-land/Identity';
175 changes: 175 additions & 0 deletions test/argsPass.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import { stub } from 'sinon';
import { all, any, none } from 'ramda';
import { assert } from 'chai';

import { argsPass } from '../src/index';

describe('argsPass', function() {
let p1False;
let p1True;
let p2True;
let p2False;
let p3True;
let p3False;
let c2True;
let c1True;
let c2False;
let c1False;

beforeEach(function() {
p1False = stub().returns(false);
p1True = stub().returns(true);
p2False = stub().returns(false);
p2True = stub().returns(true);
p3False = stub().returns(false);
p3True = stub().returns(true);
c2True = stub().returns(true);
c1True = stub().returns(c2True);
c2False = stub().returns(false);
c1False = stub().returns(c2False);
});

it('should return a new curried function for each argument supplied', function() {
const f = argsPass(c1True, [p1True, p2True, p3True]);
assert.isTrue(f(1)(2)(3));
assert.isTrue(p1True.calledWith(1));
assert.isTrue(p2True.calledWith(2));
assert.isTrue(p3True.calledWith(3));
assert.isTrue(c2True.calledWith([true, true, true]));
assert.isTrue(p1True.calledOnce);
assert.isTrue(p1True.calledOnce);
assert.isTrue(p2True.calledOnce);
assert.isTrue(c1True.calledOnce);
assert.isTrue(c2True.calledOnce);
});

context('with same number of functions as arguments', function() {
context('with all functions returning truthy values', function() {
specify('should return `true`', function() {
const f = argsPass(c1True, [p1True, p2True, p3True]);
assert.isTrue(f(1, 2, 3));
assert.isTrue(p1True.calledWith(1));
assert.isTrue(p2True.calledWith(2));
assert.isTrue(p3True.calledWith(3));
assert.isTrue(c2True.calledWith([true, true, true]));
assert.isTrue(p1True.calledOnce);
assert.isTrue(p2True.calledOnce);
assert.isTrue(p3True.calledOnce);
assert.isTrue(c1True.calledOnce);
assert.isTrue(c2True.calledOnce);
});
});

context('with one function returning a falsy value', function() {
specify('should return `false`', function() {
const f = argsPass(c1False, [p1True, p2True, p3False]);
assert.isFalse(f(1, 2, 3));
assert.isTrue(p1True.calledWith(1));
assert.isTrue(p2True.calledWith(2));
assert.isTrue(p3False.calledWith(3));
assert.isTrue(c2False.calledWith([true, true, false]));
assert.isTrue(p1True.calledOnce);
assert.isTrue(p2True.calledOnce);
assert.isTrue(p3False.calledOnce);
assert.isTrue(c1False.calledOnce);
assert.isTrue(c2False.calledOnce);
});
});
});

context('with more arguments than predicates', function() {
context('with all functions returning truthy values', function() {
specify('should return `true`', function() {
const f = argsPass(c1True, [p1True, p2True, p3True]);
assert.isTrue(f(1, 2, 3, 4));
assert.isTrue(p1True.calledWith(1));
assert.isTrue(p2True.calledWith(2));
assert.isTrue(p3True.calledWith(3));
assert.isTrue(c2True.calledWith([true, true, true, 4]));
assert.isTrue(p1True.calledOnce);
assert.isTrue(p2True.calledOnce);
assert.isTrue(p3True.calledOnce);
assert.isTrue(c1True.calledOnce);
assert.isTrue(c2True.calledOnce);
});
});

context('with one predicate failing', function() {
specify('should return `false`', function() {
const f = argsPass(c1False, [p1True, p2True, p3False]);
assert.isFalse(f(1, 2, 3, 4));
assert.isTrue(p1True.calledWith(1));
assert.isTrue(p2True.calledWith(2));
assert.isTrue(p3False.calledWith(3));
assert.isTrue(c2False.calledWith([true, true, false, 4]));
assert.isTrue(p1True.calledOnce);
assert.isTrue(p2True.calledOnce);
assert.isTrue(p3False.calledOnce);
assert.isTrue(c1False.calledOnce);
assert.isTrue(c2False.calledOnce);
});
});
});

context('with `all` as the combinator', function() {
context('with all functions returning truthy values', function() {
specify('should return `true`', function() {
const f = argsPass(all, [p1True, p2True, p3True]);
assert.isTrue(f(1, 2, 3));
});
});

context('with one function returning a falsy value', function() {
specify('should return `true`', function() {
const f = argsPass(all, [p1True, p2True, p3False]);
assert.isFalse(f(1, 2, 3));
});
});
});

context('with `any` as the combining predicate', function() {
context('with all functions returning truthy values', function() {
specify('should return `true`', function() {
const f = argsPass(any, [p1True, p2True, p3True]);
assert.isTrue(f(1, 2, 3));
});
});

context('with one function returning a truthy value', function() {
specify('should return `true`', function() {
const f = argsPass(any, [p1True, p2False, p3True]);
assert.isTrue(f(1, 2, 3));
});
});

context('with all functions returning falsy values', function() {
specify('should return `true`', function() {
const f = argsPass(any, [p1False, p2False, p3False]);
assert.isFalse(f(1, 2, 3));
});
});
});

context('with `none` as the combining predicate', function() {
context('with all functions returning truthy values', function() {
specify('should return `true`', function() {
const f = argsPass(none, [p1False, p2False, p3False]);
assert.isTrue(f(1, 2, 3));
});
});

context('with all functions returning falsy values', function() {
specify('should return `true`', function() {
const f = argsPass(none, [p1False, p2False, p3False]);
assert.isTrue(f(1, 2, 3));
});
});

context('with one function returning a truthy value', function() {
specify('should return `true`', function() {
const f = argsPass(none, [p1False, p2False, p3True]);
assert.isFalse(f(1, 2, 3));
});
});
});
});

0 comments on commit f7d7f7c

Please sign in to comment.