Skip to content

Instantly share code, notes, and snippets.

@staltz
Forked from axefrog/scope.js
Last active May 6, 2024 03:15
Show Gist options
  • Save staltz/f02ee6f2cf8eb134b887 to your computer and use it in GitHub Desktop.
Save staltz/f02ee6f2cf8eb134b887 to your computer and use it in GitHub Desktop.
An idea for scoping Cycle.js child components/dialogues in the context of their parent. Any driver can optionally supply a scope() function, which returns a scoped instance of itself. A driver can also supply an unscope() function which returns a transformed instance of its associated sink. Anything that is scope-unaware is preserved.
function runInScope(main, sources, context, ...args) {
if(!main) {
throw new Error('A "main" function must be supplied, which will be called in scope and from which a (sinks) object will be returned');
}
if(!sources) {
throw new Error('A source drivers object must be supplied, to which scoping can be applied');
}
if(!context) {
throw new Error('A scope context object must be supplied, either as a string, or as an object of key/value pairs');
}
// Prepare the scoped sources object, preserving all drivers that have no
// concept of scoping, and applying either a universal scope context to all
// drivers, or a scope context per driver, on an opt-in basis.
let scopedSources = {};
for(let key of sources) {
let source = sources[key];
if(context && typeof source.scope === 'function') {
let scopeContext = typeof context === 'string' ? context : context[key];
if(typeof scopeContext !== 'undefined' && scopeContext !== null) {
source = source.scope(scopeContext);
}
}
scopedSources[key] = source;
}
// The main function is now called in the context of the constrained scope
let sinks = main(scopedSources, ...args);
// Each driver can now optionally apply a transform to the returned sink with
// which it is associated. Note that the `unscope()` function is called against
// the scoped driver instance in case that driver instance retained scope state
// information internally that it may wish to use when unscoping the sink
// object. If not, it will still have context provided via the second argument
// passed to `unscope()`.
for(let key of sinks) {
let sink = sinks[key];
let source = scopedSources[key];
if(source && context && typeof source.unscope === 'function') {
let scopeContext = typeof context === 'string' ? context : context[key];
if(typeof scopeContext !== 'undefined' && scopeContext !== null) {
sinks[key] = source.unscope(sink, scopeContext);
}
}
}
return sinks;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment