ruddy
Version:
Modularized state-management tools for modern front-end applications. Manage dispatched messages in a clean and predictable way for either small or large scale projects
460 lines (355 loc) • 21.1 kB
JavaScript
'use strict';
exports.__esModule = true;
exports.isActionTypeInCurrentState = exports.getNextState = exports.getCurrentState = exports.getDefaultStateForMachines = exports.getStateMachinesPropPath = exports.createMachines = exports.createMachineStates = exports.isTransitionPossible = exports.isEachTransitionAmongMachineStates = exports.areStateMachineInputsActionTypes = exports.areInputsAndTransitionsStrings = exports.areStateNamesStrings = exports.invalidStateMachineInputs = exports.getStateInputsForAllMachines = exports.getStateInputsForMachine = exports.getTransitionsForMachine = exports.addTransitionsToState = exports.noMachines = undefined;
var _any = require('ramda/src/any');
var _any2 = _interopRequireDefault(_any);
var _pair = require('ramda/src/pair');
var _pair2 = _interopRequireDefault(_pair);
var _mergeDeepRight = require('ramda/src/mergeDeepRight');
var _mergeDeepRight2 = _interopRequireDefault(_mergeDeepRight);
var _always = require('ramda/src/always');
var _always2 = _interopRequireDefault(_always);
var _when = require('ramda/src/when');
var _when2 = _interopRequireDefault(_when);
var _split = require('ramda/src/split');
var _split2 = _interopRequireDefault(_split);
var _ifElse = require('ramda/src/ifElse');
var _ifElse2 = _interopRequireDefault(_ifElse);
var _both = require('ramda/src/both');
var _both2 = _interopRequireDefault(_both);
var _last = require('ramda/src/last');
var _last2 = _interopRequireDefault(_last);
var _ = require('ramda/src/__');
var _2 = _interopRequireDefault(_);
var _contains = require('ramda/src/contains');
var _contains2 = _interopRequireDefault(_contains);
var _head = require('ramda/src/head');
var _head2 = _interopRequireDefault(_head);
var _pick = require('ramda/src/pick');
var _pick2 = _interopRequireDefault(_pick);
var _reduce = require('ramda/src/reduce');
var _reduce2 = _interopRequireDefault(_reduce);
var _path = require('ramda/src/path');
var _path2 = _interopRequireDefault(_path);
var _has = require('ramda/src/has');
var _has2 = _interopRequireDefault(_has);
var _useWith = require('ramda/src/useWith');
var _useWith2 = _interopRequireDefault(_useWith);
var _toPairs = require('ramda/src/toPairs');
var _toPairs2 = _interopRequireDefault(_toPairs);
var _either = require('ramda/src/either');
var _either2 = _interopRequireDefault(_either);
var _allPass = require('ramda/src/allPass');
var _allPass2 = _interopRequireDefault(_allPass);
var _is = require('ramda/src/is');
var _is2 = _interopRequireDefault(_is);
var _all = require('ramda/src/all');
var _all2 = _interopRequireDefault(_all);
var _difference = require('ramda/src/difference');
var _difference2 = _interopRequireDefault(_difference);
var _converge = require('ramda/src/converge');
var _converge2 = _interopRequireDefault(_converge);
var _prop = require('ramda/src/prop');
var _prop2 = _interopRequireDefault(_prop);
var _keys = require('ramda/src/keys');
var _keys2 = _interopRequireDefault(_keys);
var _map = require('ramda/src/map');
var _map2 = _interopRequireDefault(_map);
var _filter = require('ramda/src/filter');
var _filter2 = _interopRequireDefault(_filter);
var _values = require('ramda/src/values');
var _values2 = _interopRequireDefault(_values);
var _flatten = require('ramda/src/flatten');
var _flatten2 = _interopRequireDefault(_flatten);
var _uniq = require('ramda/src/uniq');
var _uniq2 = _interopRequireDefault(_uniq);
var _defaultTo = require('ramda/src/defaultTo');
var _defaultTo2 = _interopRequireDefault(_defaultTo);
var _assocPath = require('ramda/src/assocPath');
var _assocPath2 = _interopRequireDefault(_assocPath);
var _unless = require('ramda/src/unless');
var _unless2 = _interopRequireDefault(_unless);
var _compose = require('ramda/src/compose');
var _compose2 = _interopRequireDefault(_compose);
var _curry = require('ramda/src/curry');
var _curry2 = _interopRequireDefault(_curry);
var _isEmpty = require('ramda/src/isEmpty');
var _isEmpty2 = _interopRequireDefault(_isEmpty);
var _isNil = require('ramda/src/isNil');
var _isNil2 = _interopRequireDefault(_isNil);
var _anyPass = require('ramda/src/anyPass');
var _anyPass2 = _interopRequireDefault(_anyPass);
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _util = require('../util');
var _types = require('../types');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Simple check to see whether a given duck's machines is empty.
*
* @func
* @sig {k: v} -> Boolean
* @param {Object} machines A duck's collection of state machines
* @returns {Boolean} whether or not the machines are empty
*/
var noMachines = exports.noMachines = (0, _anyPass2.default)([_isNil2.default, _isEmpty2.default]);
/**
* Adds an object containing the current state of all the state machines onto
* a given section of the redux store
*
* @func
* @sig {k: v} -> {k: v} -> {k: v}
* @param {Object} duck A duck instance, containing the state machines and the
* prop name/path to the section of the store where their state is tracked
* @param {*} state The current state of this portion of the store
* @returns {Object} original state (or initialState, if state is nil) plus
* current state of all the state machines
*/
var addTransitionsToState = exports.addTransitionsToState = (0, _curry2.default)(function (state, _ref) {
var _ref$initialState = _ref.initialState,
initialState = _ref$initialState === undefined ? {} : _ref$initialState,
_ref$stateMachinesPro = _ref.stateMachinesPropName,
stateMachinesPropName = _ref$stateMachinesPro === undefined ? ['states'] : _ref$stateMachinesPro;
return (0, _compose2.default)((0, _unless2.default)((0, _util.hasNestedProp)(stateMachinesPropName), (0, _assocPath2.default)(stateMachinesPropName, {})), (0, _defaultTo2.default)(initialState))(state);
});
/**
* Retrieves a unique list all the state transitions possible for a given machine.
* State machines are objects whose keys are states and whose values are objects
* inside of which the transition-to state is the value of each of its key/value pairs.
*
* @func
* @sig {k: v} -> [String]
* @param {Object} machine A single state machine
* @returns {String[]} A list of unique state transitions for the provided machine
*/
var getTransitionsForMachine = exports.getTransitionsForMachine = (0, _compose2.default)(_uniq2.default, (0, _filter2.default)(_util.isStringieThingie), _flatten2.default, (0, _map2.default)(_values2.default), _values2.default);
/**
* Retrieves a unique list all the inputs for a given state machine (which correspond to Action Types).
* State machines are objects whose keys are states and whose values are objects
* inside of which the input value is the key of each of its key/value pairs.
*
* @func
* @sig {k: v} -> [String]
* @param {Object} machine A single state machine
* @returns {String[]} A list of unique inputs to the provided state machine
*/
var getStateInputsForMachine = exports.getStateInputsForMachine = (0, _compose2.default)(_uniq2.default, (0, _filter2.default)(_util.isStringieThingie), _flatten2.default, (0, _map2.default)(_keys2.default), _values2.default);
/**
* Retrieves a unique list all the inputs for all of a given duck's state machines.
* State machines are objects whose keys are states and whose values are objects
* inside of which the input value is the key of each of its key/value pairs.
*
* @func
* @sig {k: v} -> [String]
* @param {Object} duck A duck containing one or more state machines
* @returns {String[]} A list of unique inputs to all the state machines for the provided duck
*/
var getStateInputsForAllMachines = exports.getStateInputsForAllMachines = (0, _compose2.default)(_uniq2.default, _flatten2.default, (0, _map2.default)(getStateInputsForMachine), _values2.default, (0, _prop2.default)('machines'));
/**
* Retrieves any invalid inputs from a given duck's state machines.
* State machines are objects whose keys are states and whose values are objects
* inside of which the input value is the key of each of its key/value pairs.
*
* @func
* @sig {k: v} -> [String]
* @param {Object} duck A duck containing one or more state machines and action types
* @returns {String[]} A list of any invalid inputs for the state machines of the provided duck
*/
var invalidStateMachineInputs = exports.invalidStateMachineInputs = (0, _converge2.default)(_difference2.default, [getStateInputsForAllMachines, _types.getTypes]);
/**
* Checks whether all a given state machine's states are string values.
* State machines are objects whose keys are states and whose values are objects
* inside of which the input value is the key of each of its key/value pairs.
*
* @func
* @sig {k: v} -> Boolean
* @param {Object} machine A single state machine
* @returns {Boolean} whether or not the states for the state machine are strings
*/
var areStateNamesStrings = exports.areStateNamesStrings = (0, _compose2.default)((0, _all2.default)((0, _is2.default)(String)), _keys2.default);
/**
* Checks whether all a given state machine's inputs and transition names are string values.
* State machines are objects whose keys are states and whose values are objects
* inside of which the input value is the key and transition is the value of each of its key/value pairs.
*
* @func
* @sig {k: v} -> Boolean
* @param {Object} machine A single state machine
* @returns {Boolean} whether or not the inputs and transitions for the state machine are strings
*/
var areInputsAndTransitionsStrings = exports.areInputsAndTransitionsStrings = (0, _compose2.default)((0, _all2.default)((0, _allPass2.default)([_util.isPlainObj, (0, _either2.default)(_isEmpty2.default, (0, _compose2.default)((0, _all2.default)((0, _all2.default)((0, _is2.default)(String))), _toPairs2.default))])), _values2.default);
/**
* Checks whether all the state machine inputs for a given duck correspond to its action types.
* State machines are objects whose keys are states and whose values are objects
* inside of which the input value is the key of each of its key/value pairs.
*
* @func
* @sig {k: v} -> Boolean
* @param {Object} duck A duck containing one or more state machines and action types
* @returns {Boolean} whether or not all the inputs for the state machines are among the duck's action types
*/
var areStateMachineInputsActionTypes = exports.areStateMachineInputsActionTypes = (0, _compose2.default)((0, _all2.default)(_isEmpty2.default), invalidStateMachineInputs);
/**
* Checks if all a state machine's transitions are states in that machine.
* State machines are objects whose keys are states and whose values are objects
* inside of which the transition-to state is the value of each of its key/value pairs.
*
* @func
* @sig {k: v} -> Boolean
* @param {Object} machine A single state machine
* @returns {Boolean} whether or not all the transition names are also states for a given state machine
*/
var isEachTransitionAmongMachineStates = exports.isEachTransitionAmongMachineStates = (0, _useWith2.default)(_difference2.default, [getTransitionsForMachine, _keys2.default]);
/**
* Checks to see whether a given machine allows for a given transition, provided its current state
*
* @func
* @sig String -> String -> {k: v} -> Boolean
* @param {String} transitionName A string value representing a potential state transition
* @param {String} currentState A string value representing the current state
* @param {Object} machine A state machine which contains the possible states and their associated transitions
* @returns {Boolean} whether or not the transition is possible from the current state
*/
var isTransitionPossible = exports.isTransitionPossible = (0, _curry2.default)(function (transitionName, currentState, machine) {
return (0, _compose2.default)((0, _has2.default)((0, _util.coerceToString)(transitionName)), (0, _defaultTo2.default)({}), (0, _path2.default)([(0, _util.coerceToString)(currentState)]), (0, _defaultTo2.default)({}))(machine);
});
/**
* Creates a "machine state" object, which can be combined with others to create a single state machine.
* The keys for a machine state object (called inputs or input values) correspond to the names of redux action types.
* The values for a machine state object (called transitions) are merely
* the names of fellow machine state objects (also created by this function).
*
* @func
* @sig {k: v} -> {k: v} -> {k: v}
* @param {Object} machine A single state machine
* @param {Object} types An object whose keys/values correspond
* to redux actions and will be used to set up inputs for the state machine
* @returns {Object} a validated, immutable state machine
*/
var createMachineStates = exports.createMachineStates = (0, _curry2.default)(function () {
var machine = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var types = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
return (0, _compose2.default)(Object.freeze, (0, _reduce2.default)(_util.listOfPairsToOneObject, {}), (0, _map2.default)(function (_ref2) {
var state = _ref2[0],
transitions = _ref2[1];
return [state, (0, _pick2.default)((0, _compose2.default)((0, _map2.default)(_head2.default), (0, _filter2.default)((0, _compose2.default)((0, _contains2.default)(_2.default, (0, _values2.default)(types)), _head2.default)), (0, _filter2.default)((0, _compose2.default)((0, _has2.default)(_2.default, machine), _last2.default)), (0, _filter2.default)((0, _all2.default)((0, _is2.default)(String))), _toPairs2.default)(transitions), transitions)];
}), (0, _filter2.default)((0, _both2.default)((0, _compose2.default)((0, _is2.default)(String), _head2.default), (0, _compose2.default)(_util.isPlainObj, _last2.default))), _toPairs2.default)(machine);
});
/**
* Creates a frozen (immutable) object whose keys are all the possible "current state"
* values for the machine and whose values are objects containing each action type that
* may produce a transition to another state.
*
* @func
* @sig {k: v} -> {k: v} -> {k: v}
* @param {Object} machines An object full of one or more state machines
* @param {Object} duck A duck instance
* @returns {Object} an object of validated, immutable state machines
*/
var createMachines = exports.createMachines = (0, _curry2.default)(function (machines, duck) {
return (0, _compose2.default)(Object.freeze, (0, _reduce2.default)(_util.listOfPairsToOneObject, {}), (0, _map2.default)(function (_ref3) {
var name = _ref3[0],
machine = _ref3[1];
return [name, createMachineStates(machine, duck.types)];
}), _toPairs2.default)(machines);
});
/**
* Creates an string path (array) that the state machine functions will use to
* lookup and alter the current state of one or more of the state machines.
* The path supplied to this function can be a single prop name (String) or
* an Array of prop names that represent a nested prop's path.
* And if a String is passed in, a dot-separated format is also supported.
*
* @func
* @sig String -> [String]
* @param {String|String[]} path The prop name or path to the section of the
* store where the state machines current state is tracked.
* @returns {String[]} an array of strings representing the nested path to the
* prop where state machines current state is tracked in the redux store
*/
var getStateMachinesPropPath = exports.getStateMachinesPropPath = (0, _ifElse2.default)((0, _allPass2.default)([(0, _is2.default)(String), (0, _contains2.default)('.'), _util.isValidPropName]), (0, _split2.default)('.'), (0, _compose2.default)((0, _when2.default)(_isEmpty2.default, (0, _always2.default)(['states'])), (0, _filter2.default)(_util.isNotBlankString), (0, _when2.default)((0, _is2.default)(String), Array)));
/**
* Builds a flattened object where the keys are the names of each of the duck's state machines,
* and the values are each set to 'initial'.
*
* @func
* @sig {k: v} -> {k: v}
* @param {Object} machines An object containing the definitions for one or more state machines
* @returns {Object} a flattened object where the current state of each state machine will be tracked
*/
var getDefaultStateForMachines = exports.getDefaultStateForMachines = function getDefaultStateForMachines() {
var machines = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
return (0, _reduce2.default)(function (obj, key) {
var _extends2;
return _extends({}, obj, (_extends2 = {}, _extends2[key] = 'initial', _extends2));
}, {}, (0, _keys2.default)(machines || {}));
};
/**
* Retrieves the section of the redux store where the current state is being
* tracked for one or more state machines. The shape of this section of the store
* is usually just a flattened object with a single value for each named state
* machine, representing its current state. Each possible state for any of those
* is defined in the Ducks.
*
* @func
* @sig {k: v} -> {k: v} -> {k: v}
* @param {Object} duck A duck instance, containing the state machines and the
* prop name/path to the section of the store where their state is tracked
* @param {*} state The current state of this portion of the store
* @returns {Object} A flattened Object containing the names of each state
* machine (String) and its current state (String) as key/value pairs
*/
var getCurrentState = exports.getCurrentState = function getCurrentState() {
var _ref4 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
_ref4$machines = _ref4.machines,
machines = _ref4$machines === undefined ? {} : _ref4$machines,
stateMachinesPropName = _ref4.stateMachinesPropName;
return (0, _compose2.default)((0, _mergeDeepRight2.default)((0, _map2.default)((0, _always2.default)('initial'), machines)), (0, _pick2.default)((0, _keys2.default)(machines)), (0, _defaultTo2.default)({}), (0, _path2.default)((0, _util.coerceToArray)(stateMachinesPropName)));
};
/**
* Based on the current state of the machines (managed in the store itself) and
* the rules defined for each state machine (in the ducks), an action is processed
* and may cause one or more ore the state machines to transition.
*
* @func
* @sig {k: v} -> {k: v} -> {k: v}
* @param {Object|*} state The current state of this portion of the store
* @param {Object} action A dispatched redux action
* @param {Object} duck A duck instance, containing the state machines and the
* prop name/path to the section of the store where their state is tracked
* @returns {Object} A flattened Object containing the names of each state
* machine (String) and its current state (String) as key/value pairs
*/
var getNextState = exports.getNextState = (0, _curry2.default)(function (state, action, _ref5) {
var _ref5$machines = _ref5.machines,
machines = _ref5$machines === undefined ? {} : _ref5$machines,
stateMachinesPropName = _ref5.stateMachinesPropName;
var currentState = getCurrentState({ machines: machines, stateMachinesPropName: stateMachinesPropName })(state);
return (0, _compose2.default)((0, _reduce2.default)(_util.listOfPairsToOneObject, {}), (0, _map2.default)(function (_ref6) {
var name = _ref6[0],
machine = _ref6[1];
return (0, _compose2.default)((0, _pair2.default)(name), (0, _unless2.default)(_util.isNotNil, (0, _always2.default)(currentState[name])), (0, _prop2.default)(action.type), (0, _defaultTo2.default)({}), (0, _prop2.default)(currentState[name]))(machine);
}), _toPairs2.default)(machines);
});
/**
* Checks whether a dispatched action's type is listed among the
* current state's input values for one or more of the state machines.
*
* @func
* @sig {k: v} -> {k: v} -> {k: v} -> Boolean
* @param {Object|*} state The current state of this portion of the store
* @param {Object} action A dispatch redux action
* @param {Object} duck A duck, which contains both state machines and the
* corresponding prop name/path in the redux store where their current state is tracked
* @returns {Boolean} whether or not the dispatched action is listed as an input
* value for the state machine(s) current state
*/
var isActionTypeInCurrentState = exports.isActionTypeInCurrentState = (0, _curry2.default)(function (state, action, _ref7) {
var machines = _ref7.machines,
stateMachinesPropName = _ref7.stateMachinesPropName;
var currentState = getCurrentState({ machines: machines, stateMachinesPropName: stateMachinesPropName })(state);
return (0, _compose2.default)((0, _any2.default)(function (_ref8) {
var name = _ref8[0],
machine = _ref8[1];
return (0, _compose2.default)(_util.isNotNil, (0, _prop2.default)(action.type), (0, _defaultTo2.default)({}), (0, _prop2.default)(_2.default, machine), (0, _prop2.default)(name))(currentState);
}), _toPairs2.default)(machines);
});