Last active
February 8, 2017 00:43
-
-
Save milankinen/f87821926b18963b2abb to your computer and use it in GitHub Desktop.
Bacon flatUpdate
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const Bacon = require("baconjs") | |
/** | |
* flatUpdate :: (state, [Observable+, [(state,...events)=>Observable<A>, (state, A)=>newState]]+) => Observable<state> | |
* | |
* const stateP = flatUpdate(initialState, | |
* [event1S], (state, newState) => newState, // supports normal Bacon.update | |
* [event2S], (state, newState) => Bacon.later(100, newState) // supports delayed state updating | |
* [event3S], [submitForm, handleSubmitResult] // supports 2-stage state updating | |
* ) | |
* | |
* function submitForm(state, event) { | |
* return Bacon.later(1000, doSomethingWith(state, event)) // simulate "server" | |
* } | |
* function handleSubmitResult(state, resultFromServer) { | |
* return {...state, ...resultFromServer} // resultFromServer === doSomething(state, event) | |
* } | |
*/ | |
function flatUpdate(initial, ...patterns) { | |
const updateBus = new Bacon.Bus(), | |
lazyUpdate = [updateBus, (state, update) => update(state)] | |
const streams = patterns.filter((_, i) => i % 2 === 0), | |
callbacks = patterns.filter((_, i) => i % 2 !== 0).map(toUpdateHandler), | |
args = streams.map((_, i) => [streams[i], callbacks[i]]).reduce((memo, pair) => [...memo, ...pair], lazyUpdate) | |
return Bacon.update(...[initial, ...args]) | |
function toUpdateHandler(cb) { | |
if (cb instanceof Array) { | |
if (cb.length !== 2) { | |
throw new Error("Update pattern must be [Function<(state, ...args), Observable<A>>, Function<(state, A), state>") | |
} | |
return (state, ...args) => { | |
const [fetch, resolve] = cb | |
const result = fetch(state, ...args) | |
if (result instanceof Bacon.Observable) { | |
updateBus.plug(result.map(val => state => resolve(state, val))) | |
return state | |
} else { | |
return result | |
} | |
} | |
} else { | |
return (state, ...args) => { | |
const result = cb.apply(null, [state, ...args]) | |
if (result instanceof Bacon.Observable) { | |
updateBus.plug(result.map(newState => () => newState)) | |
return state | |
} else { | |
return result | |
} | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const Bacon = require("baconjs"), | |
{expect} = require("chai") | |
describe("flatUpdate", () => { | |
it("makes server integration easier", done => { | |
const userS = Bacon.once({name: "mla", status: "guest"}), | |
submitS = Bacon.once({form: "login"}) | |
const stateP = flatUpdate({}, | |
[userS], (state, user) => ({...state, user}), | |
[submitS], [loginToServer, handleLoginResponse] | |
) | |
stateP | |
.changes() | |
.bufferWithCount(3) | |
.take(1) | |
.onValue(states => { | |
expect(states).to.deep.equal([ | |
{ user: { name: "mla", status: "guest" } }, // after userS | |
{ user: { name: "mla", status: "guest" } }, // after loginToServer | |
{ user: { name: "mla", status: "member" } } // after handleLoginResponse | |
]) | |
done() | |
}) | |
function loginToServer(state) { | |
//console.log("logging in user", state.user.name) | |
return Bacon.later(100, {status: "ok"}) | |
} | |
function handleLoginResponse(state, {status}) { | |
const {user} = state | |
return status === "ok" ? {...state, user: {...user, status: "member"}} : state | |
} | |
}) | |
it("just works", done => { | |
const ready = Bacon.later(300, 2), | |
set = Bacon.later(100, 1), | |
go = Bacon.later(200, 4) | |
const stateP = flatUpdate(10, | |
[ready, set, go], [(_, r, s, g) => Bacon.later(100, r + s + g), (state, sum) => state + sum], | |
[go.map(g => g + 1)], (state, g) => state - g | |
) | |
stateP | |
.changes() | |
.skipDuplicates() | |
.bufferWithCount(2) | |
.take(1) | |
.onValue(([afterGo, afterAll]) => { | |
expect(afterGo).to.equal(10 - (4 + 1)) | |
expect(afterAll).to.equal(10 - (4 + 1) + 2 + 1 + 4) | |
done() | |
}) | |
}) | |
it("supports fancy things", done => { | |
const times = Bacon.once(3) | |
const stateP = flatUpdate([], | |
[times], [(_, t) => Bacon.interval(10, "tsers").take(t), (state, s) => [...state, s]] | |
) | |
stateP | |
.changes() | |
.bufferWithCount(4) | |
.take(1) | |
.onValue(words => { | |
expect(words).to.deep.equal([[], ["tsers"], ["tsers", "tsers"], ["tsers", "tsers", "tsers"]]) | |
done() | |
}) | |
}) | |
}) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment