Skip to content

Commit

Permalink
Allow top level generator types for actions
Browse files Browse the repository at this point in the history
This commit adds a short-hand for generator actions. For example:

```javascript
let count = n => n

function * range (repo, start, end) {
  while (start <= end)
    yield repo.push(count, start++)
  }
}

repo.push(range, 1, 10) // 1,2,3,4,5,6,7,8,9,10
```
  • Loading branch information
nhunzaker committed Jul 6, 2017
1 parent 5215011 commit 50ce69e
Show file tree
Hide file tree
Showing 13 changed files with 93 additions and 116 deletions.
2 changes: 0 additions & 2 deletions examples/react-router/.flowconfig

This file was deleted.

12 changes: 4 additions & 8 deletions examples/react-router/app/actions/items.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import Items from '../domains/items'

export function addItem(params) {
return function*(repo) {
yield repo.push(Items.create, params)
}
export function* addItem(repo, params) {
yield repo.push(Items.create, params)
}

export function removeItem(id) {
return function*(repo) {
yield repo.push(Items.destroy, id)
}
export function* removeItem(repo, id) {
yield repo.push(Items.destroy, id)
}
32 changes: 13 additions & 19 deletions examples/react-router/app/boot.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,21 @@ import React from 'react'
import DOM from 'react-dom'
import { Router } from 'react-router'
import createBrowserHistory from 'history/createBrowserHistory'
import { AppContainer } from 'react-hot-loader'
import Repo from './repo'
import Layout from './views/layout'
import Application from './views/application'

const el = document.getElementById('app')
// We're creating a browser history first, so that we can pass it
// into Microcosm. This lets us take advantage of the router when
// dispatching actions.
//
// See ./effects/routing.js
const browserHistory = createBrowserHistory()
const repo = new Repo({ maxHistory: Infinity, browserHistory })

function render() {
DOM.render(
<Router history={browserHistory}>
<AppContainer>
<Layout repo={repo} />
</AppContainer>
</Router>,
el
)
}
const repo = new Repo({ browserHistory })

render()

if (module.hot) {
module.hot.accept('./views/layout', render)
}
DOM.render(
<Router history={browserHistory}>
<Application repo={repo} />
</Router>,
document.getElementById('app')
)
17 changes: 5 additions & 12 deletions examples/react-router/app/domains/domain.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@
/**
* @flow
*/

type Collection = Array<Object>
type Record = Object

class Domain {
getInitialState(): Collection {
getInitialState() {
return []
}

add(items: Collection, params: Record) {
add(items, params) {
return items.concat(params)
}

remove(items: Collection, unwanted: string) {
remove(items, unwanted) {
return items.filter(i => i.id !== unwanted)
}

removeBy(key: string) {
return (items: Collection, value: *) => {
removeBy(key) {
return (items, value) => {
return items.filter(item => item[key] !== value)
}
}
Expand Down
14 changes: 2 additions & 12 deletions examples/react-router/app/domains/items.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,13 @@
/**
* @flow
*/

import uid from 'uid'
import Lists from './lists'
import Domain from './domain'

export type Item = {
id?: string,
name: string,
list: string
}

class Items extends Domain {
static create(params): Item {
static create(params) {
return { id: uid(), ...params }
}

static destroy(id: string) {
static destroy(id) {
return id
}

Expand Down
13 changes: 2 additions & 11 deletions examples/react-router/app/domains/lists.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,12 @@
/**
* @flow
*/

import uid from 'uid'
import Domain from './domain'

export type List = {
id?: string,
name: string
}

class Lists extends Domain {
static create(params): List {
static create(params) {
return { id: uid(), ...params }
}

static destroy(id: string) {
static destroy(id) {
return id
}

Expand Down
33 changes: 9 additions & 24 deletions examples/react-router/app/models/lists.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,7 @@
/**
* @flow
*/
import { set } from 'microcosm'

import type Presenter from 'microcosm/addons/presenter'
import { set } from 'microcosm' // eslint-ignore-line

type Snapshot = { [string]: * }

interface Model {
call(presenter: Presenter, state: Snapshot, repo: Microcosm): *
}

export class ListsWithCounts implements Model {
call(_presenter: Presenter, state: Snapshot) {
export class ListsWithCounts {
call(_presenter, state) {
const { lists, items } = state

return lists.map(function(list) {
Expand All @@ -23,28 +12,24 @@ export class ListsWithCounts implements Model {
}
}

export class List implements Model {
id: string

constructor(id: string) {
export class List {
constructor(id) {
this.id = id
}

call(_presenter: Presenter, state: Snapshot) {
call(_presenter, state) {
const { lists } = state

return lists.find(list => list.id === this.id)
}
}

export class ListItems implements Model {
id: string

constructor(id: string) {
export class ListItems {
constructor(id) {
this.id = id
}

call(_presenter: Presenter, state: Snapshot) {
call(_presenter, state) {
const { items } = state

return items.filter(item => item.list === this.id)
Expand Down
1 change: 0 additions & 1 deletion examples/react-router/app/views/lists/parts/item-form.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from 'react'
import ActionForm from 'microcosm/addons/action-form'

import { addItem } from '../../../actions/items'

class ItemForm extends React.PureComponent {
Expand Down
1 change: 0 additions & 1 deletion examples/react-router/app/views/lists/parts/item-list.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from 'react'
import ActionButton from 'microcosm/addons/action-button'

import { removeItem } from '../../../actions/items'

function Item({ id, name }) {
Expand Down
12 changes: 0 additions & 12 deletions examples/react-router/flow-typed/model.js

This file was deleted.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@
]
},
"dependencies": {
"form-serialize": "^0.7.2",
"react-router": "^4.1.1"
"form-serialize": "^0.7.2"
},
"devDependencies": {
"babel-core": "^6.25.0",
Expand Down Expand Up @@ -84,6 +83,7 @@
"react": "^15.6.1",
"react-dom": "^15.6.1",
"react-hot-loader": "next",
"react-router": "^4.1.1",
"react-router-dom": "^4.1.1",
"react-test-renderer": "^15.6.1",
"rollup": "^0.43.0",
Expand Down
31 changes: 19 additions & 12 deletions src/coroutine.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ import { isFunction, isPromise, isGeneratorFn } from './utils'
function processGenerator(
action: Action,
body: (repo: Microcosm) => Generator<Action, void, *>,
repo: *
repo: *,
params: *[]
) {
action.open()
action.open(...params)

let iterator = body(repo)
let iterator = body(repo, ...params)

function step(payload: mixed) {
let next = iterator.next(payload)
Expand All @@ -29,21 +30,23 @@ function processGenerator(
}
}

function progress(subAction: Action | Action[]): Action {
function progress(subAction): Action {
let subject = subAction

if (Array.isArray(subAction)) {
return progress(repo.parallel(subAction))
subject = repo.parallel(subAction)
}

console.assert(
subAction instanceof Action,
`Iteration of generator expected an Action. Instead got ${typeof subAction}`
subject instanceof Action,
`Iteration of generator expected an Action. Instead got ${typeof subject}`
)

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

return subAction
return subject
}

step()
Expand All @@ -65,6 +68,10 @@ export default function coroutine(
return action.resolve(...params)
}

if (isGeneratorFn(command)) {
return processGenerator(action, command, repo, params)
}

let body = command.apply(null, params)

/**
Expand Down Expand Up @@ -92,7 +99,7 @@ export default function coroutine(
* in order
*/
if (isGeneratorFn(body)) {
return processGenerator(action, body, repo)
return processGenerator(action, body, repo, params)
}

/**
Expand Down
37 changes: 37 additions & 0 deletions test/unit/middleware/generator-middleware.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
import Microcosm from '../../../src/microcosm'

describe('Generator Middleware', function() {
it('opens with the parameters', function() {
let repo = new Microcosm()

function* stall(repo, n) {
yield repo.push(() => new Promise(() => {}))
}

let action = repo.push(stall, 2)

expect(action).toHaveStatus('open')
expect(action.payload).toEqual(2)
})

it('processes actions sequentially', function() {
expect.assertions(1)

Expand Down Expand Up @@ -235,4 +248,28 @@ describe('Generator Middleware', function() {
})
})
})

it('allows the top level action description to be a generator', function() {
let count = n => n

function* sequence(repo, start) {
yield repo.push(count, 1)
yield repo.push(count, 2)
yield repo.push(count, 3)
}

class Repo extends Microcosm {
register() {
return {
[count]: (_last, next) => next
}
}
}

let repo = new Repo()

repo.push(sequence, 1)

expect(repo.state).toEqual(3)
})
})

0 comments on commit 50ce69e

Please sign in to comment.