UNPKG

astx-redux-util

Version:

Several redux reducer composition utilities.

647 lines (540 loc) 21.9 kB
(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define("AstxReduxUtil", [], factory); else if(typeof exports === 'object') exports["AstxReduxUtil"] = factory(); else root["AstxReduxUtil"] = factory(); })(this, function() { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) /******/ return installedModules[moduleId].exports; /******/ /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // identity function for calling harmony imports with the correct context /******/ __webpack_require__.i = function(value) { return value; }; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { /******/ configurable: false, /******/ enumerable: true, /******/ get: getter /******/ }); /******/ } /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 7); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; /** * lodash 3.0.8 (Custom Build) <https://lodash.com/> * Build: `lodash modularize exports="npm" -o ./` * Copyright 2012-2016 The Dojo Foundation <http://dojofoundation.org/> * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE> * Copyright 2009-2016 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors * Available under MIT license <https://lodash.com/license> */ /** `Object#toString` result references. */ var funcTag = '[object Function]', genTag = '[object GeneratorFunction]'; /** Used for built-in method references. */ var objectProto = Object.prototype; /** * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) * of values. */ var objectToString = objectProto.toString; /** * Checks if `value` is classified as a `Function` object. * * @static * @memberOf _ * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. * @example * * _.isFunction(_); * // => true * * _.isFunction(/abc/); * // => false */ function isFunction(value) { // The use of `Object#toString` avoids issues with the `typeof` operator // in Safari 8 which returns 'object' for typed array constructors, and // PhantomJS 1.9 which returns 'function' for `NodeList` instances. var tag = isObject(value) ? objectToString.call(value) : ''; return tag == funcTag || tag == genTag; } /** * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`. * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) * * @static * @memberOf _ * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an object, else `false`. * @example * * _.isObject({}); * // => true * * _.isObject([1, 2, 3]); * // => true * * _.isObject(_.noop); * // => true * * _.isObject(null); * // => false */ function isObject(value) { var type = typeof value === 'undefined' ? 'undefined' : _typeof(value); return !!value && (type == 'object' || type == 'function'); } module.exports = isFunction; /***/ }), /* 1 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; exports.__esModule = true; exports.default = verify; /** * A convenience assertion utility, typically used to validate * pre-conditions of a routine. * * **Advanced**: verify.prefix(msgPrefix) returns a higher-order * verify() function where all messaged are prefixed. * * @param {truthy} condition - a "truthy" condition which * must be satisfied. * * @param {string} msg - a message clarifying the condition being * checked. * * @throws {Error} an Error is thrown when the supplied condition is * NOT met. * * @private */ function verify(condition, msg) { if (!condition) { throw new Error(msg); } } verify.prefix = function (msgPrefix) { return function (condition, msg) { return verify(condition, msgPrefix + msg); }; }; /***/ }), /* 2 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /** * lodash 3.0.0 (Custom Build) <https://lodash.com/> * Build: `lodash modern modularize exports="npm" -o ./` * Copyright 2012-2015 The Dojo Foundation <http://dojofoundation.org/> * Based on Underscore.js 1.7.0 <http://underscorejs.org/LICENSE> * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors * Available under MIT license <https://lodash.com/license> */ /** * This method returns the first argument provided to it. * * @static * @memberOf _ * @category Utility * @param {*} value Any value. * @returns {*} Returns `value`. * @example * * var object = { 'user': 'fred' }; * _.identity(object) === object; * // => true */ function identity(value) { return value; } module.exports = identity; /***/ }), /* 3 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; exports.__esModule = true; exports.default = conditionalReducer; var _lodash = __webpack_require__(2); var _lodash2 = _interopRequireDefault(_lodash); var _lodash3 = __webpack_require__(0); var _lodash4 = _interopRequireDefault(_lodash3); var _verify = __webpack_require__(1); var _verify2 = _interopRequireDefault(_verify); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * Create a higher-order reducer that conditionally executes one of * the supplied reducerFns, based on the conditionalFn() return * directive. * * The {{book.guide.devGuide}} discusses conditionalReducer() in more detail * (see {{book.guide.conceptConditional}}), and additional examples can * be found in {{book.guide.conceptJoin}} and {{book.guide.fullExample}}. * * @param {conditionalReducerCB} conditionalFn - a callback function * whose return value determines which reducerFn is executed * ... truthy: thenReducerFn(), falsy: elseReducerFn(). * * @param {reducerFn} thenReducerFn - the "wrapped" reducer invoked * when conditionalFn returns truthy. * * @param {reducerFn} [elseReducerFn=identity] - the * optional "wrapped" reducer invoked when conditionalFn returns * falsy. DEFAULT: [identity function](https://lodash.com/docs#identity) * * @param {InitialState} [initialState] - the optional fall-back state * value used during the state initialization boot-strap process. * * @returns {reducerFn} a newly created reducer function (described above). */ function conditionalReducer(conditionalFn, thenReducerFn) { var elseReducerFn = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _lodash2.default; var initialState = arguments[3]; // validate params var check = _verify2.default.prefix('AstxReduxUtil.conditionalReducer() parameter violation: '); check(conditionalFn, 'conditionalFn argument is required'); check((0, _lodash4.default)(conditionalFn), 'conditionalFn argument is NOT a function'); check(thenReducerFn, 'thenReducerFn argument is required'); check((0, _lodash4.default)(thenReducerFn), 'thenReducerFn argument is NOT a function'); check((0, _lodash4.default)(elseReducerFn), 'elseReducerFn argument is NOT a function'); // expose our new higher-order reducer // NOTE: For more info on he originalReducerState parameter, refer to the Dev Guide {{book.guide.originalReducerState}} return function () { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialState; var action = arguments[1]; var originalReducerState = arguments[2]; // maintain the originalReducerState as the immutable state // at the time of the start of the reduction process // ... in support of joinReducers() // ... for more info, refer to the Dev Guide {{book.guide.originalReducerState}} if (originalReducerState === undefined) { originalReducerState = state; } // execute either thenReducerFn or elseReducerFn, based on conditionalFn return conditionalFn(state, action, originalReducerState) ? thenReducerFn(state, action, originalReducerState) : elseReducerFn(state, action, originalReducerState); }; } //*** //*** Specification: conditionalReducerCB //*** /** * A callback function (used in {{book.api.conditionalReducer}}) whose * return value determines which reducerFn is executed. * * @callback conditionalReducerCB * * @param {*} state - The current immutable state that is the * reduction target. * * @param {Action} action - The standard redux Action object that * drives the reduction process. * * @param {*} originalReducerState - The immutable state at the time * of the start of the reduction process. * * This is useful in determining whether state has changed within a * series of reductions {{book.api.joinReducers}} ... because each * individual reducer only has visibility of the state within it's own * reduction process. * * Further information can be found in the * {{book.guide.originalReducerState}} discussion of the {{book.guide.devGuide}}. * * @returns {truthy} A truthy value indicating which reducerFn is * executed ... truthy: thenReducerFn(), falsy: elseReducerFn(). */ /***/ }), /* 4 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; exports.__esModule = true; exports.default = joinReducers; var _lodash = __webpack_require__(0); var _lodash2 = _interopRequireDefault(_lodash); var _lodash3 = __webpack_require__(6); var _lodash4 = _interopRequireDefault(_lodash3); var _verify = __webpack_require__(1); var _verify2 = _interopRequireDefault(_verify); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * Create a higher-order reducer by combining two or more reducers, * logically executing each in sequence (in essence combining their * functionality into one). This is useful when combining various * reducer types into one logical construct. * * **Please Note:** Because each reducer is able to build on what has * been accomplished by a prior reducer, joinReducers cumulatively * passes the state parameter that was returned from any prior reducer * (in the chain of reducers to execute). In essence this is an * accumulative process. While this does NOT relax the immutable * constraint of the reducer's state parameter, it is possible for a * down-stream reducer to receive a state parameter that is a * different instance from the start of the reduction process (because * an up-stream reducer needed to alter it in some way). * * The {{book.guide.devGuide}} discusses joinReducers() in more detail * (see {{book.guide.conceptJoin}}), and additional examples can * be found in {{book.guide.fullExample}}. * * @param {...reducerFn} reducerFns two or more reducer functions to join * together. * * @param {InitialState} [initialState] - the optional fall-back state * value used during the state initialization boot-strap process. * * @returns {reducerFn} a newly created reducer function (described above). */ function joinReducers() { for (var _len = arguments.length, reducerFns = Array(_len), _key = 0; _key < _len; _key++) { reducerFns[_key] = arguments[_key]; } // define our initialState parameter (optionally, the last parameter) // NOTE: We have to do this programatically because our function // accepts variable number of arguments. var initialState = (0, _lodash2.default)((0, _lodash4.default)(reducerFns)) ? undefined : reducerFns.pop(); // validate params var check = _verify2.default.prefix('AstxReduxUtil.joinReducers() parameter violation: '); check(reducerFns && reducerFns.length >= 2, 'two or more reducerFn arguments are required'); // ... each arg MUST be a function (reducerFn) var badArgNum = reducerFns.reduce(function (firstBadArgNum, reducerFn, indx) { return firstBadArgNum || ((0, _lodash2.default)(reducerFn) ? 0 : indx + 1); }, 0); check(!badArgNum, 'argument position number ' + badArgNum + ' is NOT a function ... expecting two or more reducerFns to join together'); // expose our new higher-order reducer return function () { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialState; var action = arguments[1]; var originalReducerState = arguments[2]; // maintain the originalReducerState as the immutable state // at the time of the start of the reduction process // ... in support of joinReducers() // ... for more info, refer to the Dev Guide {{book.guide.originalReducerState}} if (originalReducerState === undefined) { originalReducerState = state; } // execute each reducerFn in sequence return reducerFns.reduce(function (nextState, reducerFn) { return reducerFn(nextState, action, originalReducerState); }, state); }; } /***/ }), /* 5 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; exports.__esModule = true; exports.default = reducerHash; var _lodash = __webpack_require__(2); var _lodash2 = _interopRequireDefault(_lodash); var _lodash3 = __webpack_require__(0); var _lodash4 = _interopRequireDefault(_lodash3); var _verify = __webpack_require__(1); var _verify2 = _interopRequireDefault(_verify); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * Create a higher-order reducer by combining a set of sub-reducer * functions that are indexed by the standard action.type. When no * action.type is acted on, the original state is merely * passed-through (using the [identity * function](https://lodash.com/docs#identity)). * * This is one of the more prevalent composition reducers, and * provides an alternative to the switch statement (commonly used to * provide this control mechanism). * * The {{book.guide.devGuide}} discusses reducerHash() in more detail (see * {{book.guide.conceptHash}}), and additional examples can be found in * {{book.guide.conceptJoin}} and {{book.guide.fullExample}}. * * **SideBar**: Because reducerHash is so central to the rudimentary * aspect of reduction, it is a common practice to extend it, * promoting a * [`centralized reducer-based logging capability`](/extending/logExt.md), * with an ability to correlate logging levels to state changes * *(providing a means to filter logs at a high level with minimal * output)*. * * @param {ActionReducerHash} actionHandlers - a hash of reducer functions, * indexed by the standard redux action.type. * * @param {InitialState} [initialState] - the optional fall-back state * value used during the state initialization boot-strap process. * * @returns {reducerFn} a newly created reducer function (described above). */ function reducerHash(actionHandlers, initialState) { // validate params var check = _verify2.default.prefix('AstxReduxUtil.reducerHash() parameter violation: '); check(actionHandlers, 'actionHandlers is required'); // ... AI: this check may be too intrusive if the client's actionHandlers object is used for OTHER things? var invalidHashEntry = Object.getOwnPropertyNames(actionHandlers).reduce(function (firstBadEntry, type) { return firstBadEntry || (0, _lodash4.default)(actionHandlers[type]) ? null : type; }, null); check(!invalidHashEntry, 'actionHandlers[\'' + invalidHashEntry + '\'] is NOT a function ... expecting reducer function indexed by action type'); check(!actionHandlers['undefined'], "actionHandlers contains an 'undefined' entry ... suspect a misspelled constant"); // internal function: locate handler from actionHandlers action.type hash lookup // ... default: identity pass-through var locateHandler = function locateHandler(action) { return actionHandlers[action.type] || _lodash2.default; }; // expose the new reducer fn, which resolves according the the supplied actionHandlers return function () { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialState; var action = arguments[1]; var originalReducerState = arguments[2]; // maintain the originalReducerState as the immutable state // at the time of the start of the reduction process // ... in support of joinReducers() // ... for more info, refer to the Dev Guide {{book.guide.originalReducerState}} if (originalReducerState === undefined) { originalReducerState = state; } // execute the handler indexed by the action.type (or the identity pass-through) return locateHandler(action)(state, action, originalReducerState); }; } //*** //*** Specification: ActionReducerHash //*** /** * @typedef {Object} ActionReducerHash * * A hash of reducer functions, indexed by the standard redux * action.type. * * @property {reducerFn} actionType1 - The reducer function servicing: 'actionType1'. * @property {reducerFn} actionType2 - The reducer function servicing: 'actionType2'. * @property {reducerFn} ...more - ...etc. */ /***/ }), /* 6 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /** * lodash 3.0.0 (Custom Build) <https://lodash.com/> * Build: `lodash modern modularize exports="npm" -o ./` * Copyright 2012-2015 The Dojo Foundation <http://dojofoundation.org/> * Based on Underscore.js 1.7.0 <http://underscorejs.org/LICENSE> * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors * Available under MIT license <https://lodash.com/license> */ /** * Gets the last element of `array`. * * @static * @memberOf _ * @category Array * @param {Array} array The array to query. * @returns {*} Returns the last element of `array`. * @example * * _.last([1, 2, 3]); * // => 3 */ function last(array) { var length = array ? array.length : 0; return length ? array[length - 1] : undefined; } module.exports = last; /***/ }), /* 7 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; exports.__esModule = true; exports.reducerHash = exports.joinReducers = exports.conditionalReducer = undefined; var _conditionalReducer = __webpack_require__(3); var _conditionalReducer2 = _interopRequireDefault(_conditionalReducer); var _joinReducers = __webpack_require__(4); var _joinReducers2 = _interopRequireDefault(_joinReducers); var _reducerHash = __webpack_require__(5); var _reducerHash2 = _interopRequireDefault(_reducerHash); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } //*** //*** Promote all library utilities through a single module. //*** // NOTE: This non-default export supports ES6 imports. // Example: // import { reducerHash } from 'astx-redux-util'; // -or- // import * as AstxReduxUtil from 'astx-redux-util'; exports.conditionalReducer = _conditionalReducer2.default; exports.joinReducers = _joinReducers2.default; exports.reducerHash = _reducerHash2.default; // NOTE: This default export supports CommonJS modules (otherwise Babel does NOT promote them). // Example: // const { reducerHash } = require('astx-redux-util'); // -or- // const AstxReduxUtil = require('astx-redux-util'); exports.default = { conditionalReducer: _conditionalReducer2.default, joinReducers: _joinReducers2.default, reducerHash: _reducerHash2.default }; /***/ }) /******/ ]); });