Skip to content

Commit

Permalink
Repo.parallel can accept objects (#452)
Browse files Browse the repository at this point in the history
This commit updates repo.parallel such that it can accept plain
JavaScript objects. In this case, the return payload is an object
who's keys match the shape provided.

This also allows generator actions to yield objects
  • Loading branch information
nhunzaker authored Nov 13, 2017
1 parent 68435ea commit c418759
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 18 deletions.
32 changes: 30 additions & 2 deletions packages/microcosm/docs/api/actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,10 +210,38 @@ function getUsers (ids) {
yield ids.map(id => repo.push(getUser, id))
}
}

repo.push(getUsers, [ 1, 2 ])
```

Alternatively, you may also yield an object, this is useful for stitching together records that may have data at different locations:

```javascript
function getPost (id) {
return fetch(`/posts/${id}`)
}

function getComments (id) {
return fetch(`/posts/${id}/comments`)
}

function getPostWithComments (id) {
return function * (repo) {
let { post, comments } = yield {
post: repo.push(getPost, id),
comments: repo.push(getComments, id)
}

post.comments = comments

return post
}
}

repo.push(getBlogPost, 1)

This comment has been minimized.

Copy link
@tommymarshall

tommymarshall Dec 4, 2017

repo.push(getPostWithComments, 1)

This comment has been minimized.

Copy link
@nhunzaker

nhunzaker Dec 4, 2017

Author Contributor

Thank you, 👍

This comment has been minimized.

Copy link
@nhunzaker

nhunzaker Dec 4, 2017

Author Contributor

Fixed in a7dbff6

```

If all actions resolve or cancel, the generator sequence
continues.
If all actions resolve or cancel, the generator sequence continues.

### Action status methods are auto-bound

Expand Down
23 changes: 14 additions & 9 deletions packages/microcosm/src/action.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,27 +187,32 @@ class Action extends Emitter {
* Set up an action such that it depends on the result of another
* series of actions.
*/
link(actions: Action[]): this {
let outstanding = actions.length
let answers = []
link(actions: *): this {
let keys = Object.keys(Object(actions))
let outstanding = keys.length
let answers = Array.isArray(actions) ? [] : {}

if (actions.length === 0) {
if (keys.length <= 0) {
return this.resolve(answers)
}

actions.forEach(action => {
keys.forEach(key => {
let action = actions[key]

let onResolve = answer => {
answers[actions.indexOf(action)] = answer
answers[key] = answer
outstanding -= 1

if (outstanding <= 0) {
this.resolve(answers)
}
}

action.onDone(onResolve)
action.onCancel(onResolve)
action.onError(this.reject)
action.subscribe({
onDone: onResolve,
onCancel: onResolve,
onError: this.reject
})
})

return this
Expand Down
17 changes: 10 additions & 7 deletions packages/microcosm/src/coroutine.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/

import Action from './action'
import { isFunction, isPromise, isGeneratorFn } from './utils'
import { isFunction, isPromise, isGeneratorFn, isPlainObject } from './utils'

/**
* Provide support for generators, performing a sequence of actions in
Expand All @@ -28,7 +28,7 @@ function processGenerator(action: Action, body: GeneratorAction, repo: *) {
}

function progress(subAction: Action | Action[]): Action {
if (Array.isArray(subAction)) {
if (Array.isArray(subAction) || isPlainObject(subAction)) {
return progress(repo.parallel(subAction))
}

Expand All @@ -37,11 +37,14 @@ function processGenerator(action: Action, body: GeneratorAction, repo: *) {
`Iteration of generator expected an Action. Instead got ${typeof subAction}`
)

subAction.onDone(step)
subAction.onCancel(action.cancel, action)
subAction.onError(action.reject, action)

return subAction
return subAction.subscribe(
{
onDone: step,
onCancel: action.cancel,
onError: action.reject
},
action
)
}

step()
Expand Down
7 changes: 7 additions & 0 deletions packages/microcosm/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,13 @@ export function isObject(target: *): boolean {
return !(!target || typeof target !== 'object')
}

/**
* Is a value a POJO?
*/
export function isPlainObject(target: *) {
return isObject(target) && target.constructor == Object
}

/**
* Is a value a function?
*/
Expand Down
16 changes: 16 additions & 0 deletions packages/microcosm/test/unit/action/link.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,20 @@ describe('Action link', function() {

expect(action.status).toEqual('reject')
})

describe('when given an object', function() {
it('returns an object', async function() {
let repo = new Microcosm()

let action = repo.parallel({
one: repo.push(() => delay(1, 20)),
two: repo.push(() => delay(2, 10)),
three: repo.push(() => delay(3, 15))
})

let payload = await action

expect(payload).toEqual({ one: 1, two: 2, three: 3 })
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -235,4 +235,36 @@ describe('Generator Middleware', function() {
})
})
})

describe('when yielding an object', function() {
it('waits for all items to complete', function() {
expect.assertions(2)

let add = n => n
let repo = new Microcosm()

repo.addDomain('count', {
getInitialState() {
return 0
},
register() {
return {
[add]: (a, b) => a + b
}
}
})

function testReturnValue() {
return function*(repo) {
yield { one: repo.push(add, 1), two: repo.push(add, 2) }
}
}

repo.push(testReturnValue).onDone(payload => {
expect(repo).toHaveState('count', 3)

expect(payload).toEqual({ one: 1, two: 2 })
})
})
})
})

0 comments on commit c418759

Please sign in to comment.