Home Manual Reference Source Repository

src/scopes/combiner.js

import Immutable from 'immutable';
import t from 'tcomb';
import flyd from 'flyd';

import zipObj from '@f/zip-obj';
import curry from './../utils/curry';

import Context from './../Context';
import JSONPointer from './../JSONPointer';

const slicePath     = patch => Object.assign({}, patch, { path: patch.path.slice(1) });
const stringifyPath = patch => Object.assign({}, patch, { path: patch.path.toRFC() });
const parsePath     = patch => Object.assign({}, patch, { path: JSONPointer.ofString(patch.path) });

const filterPatchSet = curry((name, patchSet) => patchSet
  .map(parsePath)
  .filter(patch => patch.path.first() === name)
  .map(slicePath)
  .map(stringifyPath));

function readCtx(names, stores) {
  const values = stores.map(store => store());
  const stateMap = zipObj(names, values.map(ctx => ctx.state));
  const typeMap = zipObj(names, values.map(ctx => ctx.type));

  return new Context({
    state: Immutable.Map(stateMap),
    type:  t.struct(typeMap)
  });
}

function createCombine(keys) {
  return function(...args) {
    return readCtx(keys, args.slice(0, 2));
  };
}


/**
 * combiner scope factory
 * represents map of (sub)scopes.
 * @param {Map<string, ScopeFactory'>} scopeMap - the routing map
 * @param {Stream} input - input stream
 * @return {Scope}
 */

export default function combiner(scopeMap, input) {
  const names = Object.keys(scopeMap);
  const factories = names.map(name => scopeMap[ name ]);
  const inputs = names.map(name => flyd.map(function(rawPatchSet) {
    const patchSet = filterPatchSet(name, rawPatchSet);

    return patchSet;
  }, input));
  const stores = factories.map((f, i) => f(inputs[ i ]));
  const combineStreams = createCombine(names);

  return flyd.combine(combineStreams, stores);
}