Repository: vigetlabs/microcosm
Pull request #299 (2017-04-14 12:20:32)

Pass first argument of action parameters as open state of promises
Name says it all

```javascript
function getPlanet (id) {
  return fetch(`/planets/${id}`).then(res => res.json())
}

let action = repo.push(getPlanet, 'mars')

action.onOpen(data => console.log(data))
// 'mars'

action.onDone(data => console.log(data))
// { id: 'mars', name: 'Mars' }
```

Related issues:
https://github.com/vigetlabs/microcosm/issues/296


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6893cc53..a68f848e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,8 @@
   Microcosm classes.
 - Fixed a bug where the first action dispatched would always fire a
   change event, even if state didn't change.
+- The first argument of `repo.push` is passed into the `open` state of
+  actions that return promises.
 
 ## 12.6.1
 
diff --git a/docs/api/actions.md b/docs/api/actions.md
index 1edaa729..03843d6b 100644
--- a/docs/api/actions.md
+++ b/docs/api/actions.md
@@ -65,14 +65,23 @@ repo.push(addPlanet, { name: 'Saturn' })
 ### Return a promise
 
 ```javascript
-function readPlanets () {
+function getPlanet (id) {
   // Using your favorite promise-based ajax library (maybe axios or fetch?)
-  return ajax.get('/planets')
+  return ajax.get(`/planets/${id}`)
 }
 
-repo.push(readPlanets)
+repo.push(getPlanet, 'mars')
 ```
 
+When returning a Promise:
+
+1. The action is opened with the first argument of the parameters
+   passed to `repo.push`. In this case: `'mars'`
+2. When the request finishes, the action is resolved with the payload
+   passed into the Promise's resolve callback
+3. If the request fails, the action is rejected with whatever error
+   was passed with the Promise's reject callback
+
 ### Return a function
 
 Action creators that return functions grant full access to the action
diff --git a/docs/recipes/ajax.md b/docs/recipes/ajax.md
index 7e1ac573..d6a2dc33 100644
--- a/docs/recipes/ajax.md
+++ b/docs/recipes/ajax.md
@@ -47,7 +47,8 @@ When Microcosm detects a Promise returned from an action
 creator, it handles it in the following way:
 
 1. Mark the action as `open`. This gives domain handlers a way to
-   subscribe to a loading state.
+   subscribe to a loading state. The payload of the action will be
+   the first argument of the parameters passed into `repo.push`.
 2. On resolution, mark the action as `done` and update its payload to
    that of the resolved Promise.
 3. On failure, mark the action as `error` and update its payload to
diff --git a/src/coroutine.js b/src/coroutine.js
index 5c585f3a..d12fe734 100644
--- a/src/coroutine.js
+++ b/src/coroutine.js
@@ -38,7 +38,9 @@ function processGenerator (action, body, repo) {
  * Coroutine is used by an action to determine how it should resolve
  * the body of their associated command.
  */
-export default function coroutine (action, body, repo) {
+export default function coroutine (action, command, params, repo) {
+  let body = command.apply(null, params)
+
   /**
    * Provide support for Promises:
    *
@@ -49,7 +51,7 @@ export default function coroutine (action, body, repo) {
    * 4. Otherwise resolve the action with the returned body
    */
   if (isPromise(body)) {
-    action.open()
+    action.open(...params)
 
     body.then(
       result => global.setTimeout(() => action.resolve(result), 0),
diff --git a/src/microcosm.js b/src/microcosm.js
index 4218ce60..fd6fcc3f 100644
--- a/src/microcosm.js
+++ b/src/microcosm.js
@@ -183,7 +183,7 @@ inherit(Microcosm, Emitter, {
   push (command, ...params) {
     let action = this.append(command)
 
-    coroutine(action, action.command.apply(null, params), this)
+    coroutine(action, action.command, params, this)
 
     return action
   },


Test patch:
diff --git a/test/unit/microcosm/push.test.js b/test/unit/microcosm/push.test.js
index d6e2a021..f73a7cd7 100644
--- a/test/unit/microcosm/push.test.js
+++ b/test/unit/microcosm/push.test.js
@@ -2,6 +2,15 @@ import Microcosm from '../../../src/microcosm'
 
 describe('Microcosm::push', function () {
 
+  it('the pushed function has no scope', function (done) {
+    let repo = new Microcosm()
+
+    repo.push(function () {
+      expect(this).toBe(null)
+      done()
+    })
+  })
+
   it('can push an action, resolving it into state', function () {
     let repo = new Microcosm()
     let step = n => n
diff --git a/test/unit/middleware/promise-middleware.test.js b/test/unit/middleware/promise-middleware.test.js
index 50864468..872aff88 100644
--- a/test/unit/middleware/promise-middleware.test.js
+++ b/test/unit/middleware/promise-middleware.test.js
@@ -2,6 +2,14 @@ import Microcosm from '../../../src/microcosm'
 
 describe('Promise middleware', function () {
 
+  it('opens with the first argument of params', function () {
+    const repo = new Microcosm()
+    const action = repo.push(n => Promise.resolve(n), true)
+
+    expect(action).toHaveStatus('open')
+    expect(action.payload).toEqual(true)
+  })
+
   it('completes when a promise resolves', function (done) {
     const repo = new Microcosm()
     const action = repo.push(n => Promise.resolve(n))