Last active
November 9, 2017 02:22
-
-
Save mikesol/33630d118bf2ede30dd0d6642a1ee0f5 to your computer and use it in GitHub Desktop.
Telling a saga
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
import _ from 'lodash'; | |
const ____kludge = (function*() {}).constructor; | |
const isGeneratorFunction = ugh => ugh instanceof ____kludge; | |
const recurse = rules => fn => isGeneratorFunction(fn) ? sagaTeller(fn, rules) : fn; | |
const genWrapper = (value, rules) => ({ | |
...value, | |
fn: recurse(rules)(value.fn), | |
args: value.args.map(recurse(rules)), | |
}); | |
const forkWrapper = (value, rules) => ({ | |
...value, | |
FORK: genWrapper(value.FORK, rules), | |
}); | |
const callWrapper = (value, rules) => ({ | |
...value, | |
CALL: genWrapper(value.CALL, rules), | |
}); | |
const wrapGeneratorsInTeller = (value, rules) => value.FORK ? forkWrapper(value, rules) : value.CALL ? callWrapper(value, rules) : value; | |
export default function sagaTeller(saga, rules = {}) { | |
return function*(...args) { | |
const it = saga(...args); | |
// yields the first rule | |
if (rules.first) { | |
yield rules.first; | |
} | |
// will hold the result of our yield | |
let result = null; | |
// will hold an error if there is one | |
let error = null; | |
// our main loop | |
while (true) { | |
// if there is an error, we throw the iterator, otherwise next | |
const { | |
value, | |
done | |
} = error ? it.throw(error) : it.next(result); | |
// clear our error so we don't keep throwing | |
error = null; | |
if (done) { | |
// break if the generator is done done | |
break; | |
} | |
// filter rules that match this value | |
const subrules = rules.matches ? rules.matches.filter(match => (match.call && value.CALL && value.CALL.fn === match.call) || (match.put && value.PUT && value.PUT.action.type === match.put)) : []; | |
const beforeRules = subrules.filter(match => match.when === 'before'); | |
const afterRules = subrules.filter(match => match.when === 'after'); | |
let i = 0; | |
// run the before rules | |
for (; i < beforeRules.length; i++) { | |
yield beforeRules[i].effect; | |
} | |
// get a new value and either assign the result or the error | |
try { | |
result = yield wrapGeneratorsInTeller(value, rules); | |
} catch (e) { | |
error = e; | |
continue; | |
} | |
i = 0; | |
// run the after rules | |
for (; i < afterRules.length; i++) { | |
yield afterRules[i].effect; | |
} | |
} | |
// yields the last rule | |
if (rules.last) { | |
yield rules.last; | |
} | |
} | |
} | |
class _tell { | |
constructor(s) { | |
this.saga = s; | |
} | |
by(...args) { | |
return [this.saga, ...args].reduce((saga, teller) => teller(saga)); | |
} | |
} | |
export const tell = saga => new _tell(saga); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment