Skip to content

Commit

Permalink
allow passing values/resolvers or all values for all
Browse files Browse the repository at this point in the history
  • Loading branch information
richytong committed Dec 15, 2024
1 parent d8e3877 commit e8c7db0
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 51 deletions.
20 changes: 16 additions & 4 deletions _internal/areAllValuesNonfunctions.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
const isArray = require('./isArray')

/**
* @name areAllValuesNonfunctions
*
* @synopsis
* ```coffeescript [specscript]
* areAllValuesNonfunctions(values Array<function|value>) -> boolean
* areAllValuesNonfunctions(values Object<function|value>) -> boolean
* ```
*/
const areAllValuesNonfunctions = function (values) {
const length = values.length
let index = -1
while (++index < length) {
if (typeof values[index] == 'function') {
if (isArray(values)) {
const length = values.length
let index = -1
while (++index < length) {
if (typeof values[index] == 'function') {
return false
}
}
return true
}

for (const key in values) {
if (typeof values[key] == 'function') {
return false
}
}
Expand Down
20 changes: 16 additions & 4 deletions _internal/areAnyValuesPromises.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const isArray = require('./isArray')
const isPromise = require('./isPromise')

/**
Expand All @@ -6,13 +7,24 @@ const isPromise = require('./isPromise')
* @synopsis
* ```coffeescript [specscript]
* areAnyValuesPromises(values Array) -> boolean
* areAnyValuesPromises(values Object) -> boolean
* ```
*/
const areAnyValuesPromises = function (values) {
const length = values.length
let index = -1
while (++index < length) {
const value = values[index]
if (isArray(values)) {
const length = values.length
let index = -1
while (++index < length) {
const value = values[index]
if (isPromise(value)) {
return true
}
}
return false
}

for (const key in values) {
const value = values[key]
if (isPromise(value)) {
return true
}
Expand Down
3 changes: 2 additions & 1 deletion _internal/functionArrayAll.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ const functionArrayAll = function (funcs, args) {
result = Array(funcsLength)
let funcsIndex = -1, isAsync = false
while (++funcsIndex < funcsLength) {
const resultItem = funcs[funcsIndex](...args)
const f = funcs[funcsIndex]
const resultItem = typeof f == 'function' ? f(...args) : f
if (isPromise(resultItem)) {
isAsync = true
}
Expand Down
123 changes: 101 additions & 22 deletions all.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
const isPromise = require('./_internal/isPromise')
const areAnyValuesPromises = require('./_internal/areAnyValuesPromises')
const areAllValuesNonfunctions = require('./_internal/areAllValuesNonfunctions')
const promiseAll = require('./_internal/promiseAll')
const promiseObjectAll = require('./_internal/promiseObjectAll')
const isArray = require('./_internal/isArray')
const __ = require('./_internal/placeholder')
const curry2 = require('./_internal/curry2')
Expand All @@ -9,21 +12,54 @@ const functionArrayAllSeries = require('./_internal/functionArrayAllSeries')
const functionObjectAll = require('./_internal/functionObjectAll')

/**
* @name all
* @name _allValues
*
* @synopsis
* ```coffeescript [specscript]
* all(...args, funcsArray Array<function>) -> result Promise|Array
*
* all(funcsArray Array<function>)(...args) -> result Promise|Array
*
* all(...args, funcsObject Object<function>) -> result Promise|Object
* _allValues(values Array<Promise|any>) -> Promise<Array>
* _allValues(values Object<Promise|any>) -> Promise<Object>
* ```
*/
const _allValues = function (values) {
if (isArray(values)) {
return areAnyValuesPromises(values)
? promiseAll(values)
: values
}
return areAnyValuesPromises(values)
? promiseObjectAll(values)
: values
}

/**
* @name all
*
* all(funcsObject Object<function>)(...args) -> result Promise|Object
* @synopsis
* ```coffeescript [specscript]
* all(values Promise|Array<Promise|any>) -> result Promise|Array
* all(values Promise|Object<Promise|any>) -> result Promise|Object
*
* all(
* ...args,
* resolversOrValues Array<function|Promise|any>
* ) -> result Promise|Array
*
* all(
* resolversOrValues Array<function|Promise|any>
* )(...args) -> result Promise|Array
*
* all(
* ...args,
* resolversOrValues Object<function|Promise|any>
* ) -> result Promise|Object
*
* all(
* resolversOrValues Object<function|Promise|any>
* )(...args) -> result Promise|Object
* ```
*
* @description
* Function executor and composer. Accepts either an array of functions or an object of functions. Calls each function of the provided array or object in parallel with the provided arguments. Returns either an array or object of the execution results.
* Calls an array or object of resolver functions or values `resolversOrValues` with provided arguments.
*
* ```javascript [playground]
* const createArrayOfGreetingsFor = all([
Expand All @@ -32,24 +68,29 @@ const functionObjectAll = require('./_internal/functionObjectAll')
* name => `Hello ${name}`,
* ])
*
* const arrayOfGreetingsForFred = createArrayOfGreetingsFor('Fred')
*
* console.log(arrayOfGreetingsForFred)
* // ['Hi Fred', 'Hey Fred', 'Hello Fred']
* const arrayOfGreetingsFor1 = createArrayOfGreetingsFor('1')
*
* const createObjectOfGreetingsFor = all({
* hi: name => `Hi ${name}`,
* hey: name => `Hey ${name}`,
* hello: name => `Hello ${name}`,
* })
* console.log(arrayOfGreetingsFor1)
* // ['Hi 1', 'Hey 1', 'Hello 1']
* ```
*
* const objectOfGreetingsForJane = createObjectOfGreetingsFor('Jane')
* If provided only values for `resolversOrValues`, returns an array or object with the same shape as `resolversOrValues` with any Promises resolved.
*
* console.log(objectOfGreetingsForJane)
* // { hi: 'Hi Jane', hey: 'Hey Jane', hello: 'Hello Jane' }
* ```javascript [playground]
* all([
* Promise.resolve(1),
* Promise.resolve(2),
* 3,
* ]).then(console.log) // [1, 2, 3]
*
* all({
* a: Promise.resolve(1),
* b: Promise.resolve(2),
* c: 3,
* }).then(console.log) // { a: 1, b: 2, c: 3 }
* ```
*
* `all` can simultaneously compose objects and handle promises.
* `all` can be used in a pipeline to compose and manpulate data.
*
* ```javascript [playground]
* const identity = value => value
Expand All @@ -72,6 +113,14 @@ const functionObjectAll = require('./_internal/functionObjectAll')
* getAndLogUserById('1') // Got user {"_id":1,"name":"George"} by id 1
* ```
*
* Values passed in resolver position are set on the result object or array directly. If any of these values are promises, they are resolved for their values before being set on the result object or array.
*
* ```javascript [playground]
* all({}, [
* Promise.resolve(1),
* ])
* ```
*
* Any promises passed in argument position are resolved for their values before further execution. This only applies to the eager version of the API.
*
* ```javascript [playground]
Expand All @@ -84,8 +133,37 @@ const functionObjectAll = require('./_internal/functionObjectAll')
*
* @execution concurrent
*/

const all = function (...args) {
if (args.length == 1) {
const resolversOrValues = args[0]
if (isPromise(resolversOrValues)) {
return resolversOrValues.then(_allValues)
}
if (areAllValuesNonfunctions(resolversOrValues)) {
return _allValues(resolversOrValues)
}
return isArray(resolversOrValues)
? curryArgs2(functionArrayAll, resolversOrValues, __)
: curryArgs2(functionObjectAll, resolversOrValues, __)
}

const resolversOrValues = args[args.length - 1]
const argValues = args.slice(0, -1)

if (areAnyValuesPromises(argValues)) {
return isArray(resolversOrValues)
? promiseAll(argValues)
.then(curry2(functionArrayAll, resolversOrValues, __))
: promiseAll(argValues)
.then(curry2(functionObjectAll, resolversOrValues, __))
}

return isArray(resolversOrValues)
? functionArrayAll(resolversOrValues, argValues)
: functionObjectAll(resolversOrValues, argValues)

/*
////////////////////////////////////////////////////////////////
const funcs = args.pop()
if (args.length == 0) {
return isArray(funcs)
Expand All @@ -102,6 +180,7 @@ const all = function (...args) {
return isArray(funcs)
? functionArrayAll(funcs, args)
: functionObjectAll(funcs, args)
*/
}

/**
Expand Down
2 changes: 1 addition & 1 deletion assign.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const _assign = function (object, funcs) {
* // { numbers: [1, 2, 3, 4, 5], total: 15 }
* ```
*
* Values passed in resolver position are set on the result object directly. If any of these values are promises, they are resolved for their values before being assigned to the result object.
* Values passed in resolver position are set on the result object directly. If any of these values are promises, they are resolved for their values before being set on the result object.
*
* ```javascript [playground]
* assign({}, {
Expand Down
66 changes: 47 additions & 19 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,21 @@ describe('rubico', () => {
],
)

ade(
all([
Array.of,
Array.of,
Array.of,
4,
])(1, 2, 3),
[
[1, 2, 3],
[1, 2, 3],
[1, 2, 3],
4,
],
)

ade(
await all(Promise.resolve(1), 2, Promise.resolve(3), [
Array.of,
Expand Down Expand Up @@ -486,6 +501,21 @@ describe('rubico', () => {
c: [1, 2, 3],
},
)

ade(
all({
a: Array.of,
b: Array.of,
c: Array.of,
d: 4,
})(1, 2, 3),
{
a: [1, 2, 3],
b: [1, 2, 3],
c: [1, 2, 3],
d: 4,
},
)
})
it('maps input to array of sync functions', async () => {
ade(all([hi, hi, hi])('hi'), ['hihi', 'hihi', 'hihi'])
Expand Down Expand Up @@ -517,25 +547,23 @@ describe('rubico', () => {
['1hey', '1hey', '1hi'],
)
})
it('all([])() -> []', async () => {
ade(all([])(), [])
ade(all([])('hey'), [])
})
it('all({})() -> {}', async () => {
ade(all({})(), {})
ade(all({})('hey'), {})
})
it('TypeError for all([\'hey\'])()', async () => {
assert.throws(
() => all(['hey'])(),
new TypeError('funcs[funcsIndex] is not a function'),
)
})
it('{} for Set<[func]>; no functions exposed via in', async () => {
assert.deepEqual(all(new Set([() => 'yo']))('hey'), {})
})
it('{} for Map<[[1, func]]>', async () => {
assert.deepEqual(all(new Map([[1, () => 'yo']]))('hey'), {})
it('only nonfunctions', async () => {
ade(all([1, 2, 3]), [1, 2, 3])
ade(await all(Promise.resolve([Promise.resolve(1), 2, 3])), [1, 2, 3])
ade(await all([Promise.resolve(1), Promise.resolve(2), 3]), [1, 2, 3])
ade(all([]), [])
ade(all({ a: 1, b: 2, c: 3 }), { a: 1, b: 2, c: 3 })
ade(await all(Promise.resolve({
a: Promise.resolve(1),
b: 2,
c: 3,
})), { a: 1, b: 2, c: 3 })
ade(await all({
a: Promise.resolve(1),
b: Promise.resolve(2),
c: 3,
}), { a: 1, b: 2, c: 3 })
ade(all({}), {})
})
})

Expand Down

0 comments on commit e8c7db0

Please sign in to comment.