Home Manual Reference Source Repository

src/decorators/dispatchChangeSets.js

import t from 'tcomb';

import patchUtil from './../utils/patch';

import { specDecorator } from './utils';

const ChangeSet = t.irreducible('ChangeSet', function(x) {
  if(!t.Array.is(x) || x.length % 2 !== 0) return false;

  const pairs = x.reduce(function(current, e, i) {
    if(i % 2 === 0) current.push([]);
    current[ current.length - 1 ].push(e);

    return current;
  }, []);

  return t.list(t.tuple([t.String, t.Any])).is(pairs);
});

function toPairs(changeSet) {
  return changeSet.reduce(function(current, e, i) {
    if(i % 2 === 0) current.push([]);
    current[ current.length - 1 ].push(e);

    return current;
  }, []);
}
const dispatch = model => payload => {
  model.dispatch(t.match(payload,
    ChangeSet, changeSet => patchUtil(model.context, toPairs(changeSet)),
    t.Any, x => x
  ));
};

const spec = {
  deep:  false,
  cache: null,
  name:  'dispatchChangeSets',
  model: (component, model) => {
    const decorated = Object.assign({}, model, {
      dispatch: pay => {
        dispatch(model)(pay);
      }
    });

    return decorated;
  }
};

const decorator = specDecorator(spec);

/**
 * dispatch change sets
 *
 * Usage:
 * ```javascript
 * dispatchChangeSets()(function({ dispatch }) {
 * 		// some code...
 * 		dispatch(['/count', v => v + 1])
 * })
 * ```
 *
 * A change set is an array of strings on even positions and some values on odd positions.
 * The strings acts as path selector on the context object and the next element as value for the previous path.
 * If the value element is a function, this function will be called with the value in the given path as argument.
 * If the path string ends with '-' the operator in the resulting patch will be 'add' ([info](http://jsonpatch.com/#json-pointer)).
 *
 * Example:
 * ```javascript
 * const changeSet = [
 * 	'/foo/bar' : 42,
 * 	'/someNumber' : n => n + 3,
 * 	'/someList/-' : {name: 'baz'}
 * ];
 * ```
 * The function will be called with `n = model.context.get('/someNumber')`.
 * The resulting patch set with `n = 4`:
 * ```javascript
 * const patchSet = [
 * 	{
 * 		op: 'replace',
 * 		path: '/foo/bar',
 * 		value: 42
 * 	},
 * 	{
 * 		op: 'replace',
 * 		path: '/someNumber',
 * 		value: 7
 * 	},
 * 	{
 * 		op: 'add',
 * 		path: '/someList/-',
 * 		value: { name: 'baz' }
 * 	}
 * ];
 * ```
 *
 * @return {HOC}
 */
export default function dispatchChangeSets() {
  return decorator;
}