astx-redux-util
Version:
Several redux reducer composition utilities.
647 lines (540 loc) • 21.9 kB
JavaScript
(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__) {
;
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__) {
;
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__) {
;
/**
* 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__) {
;
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__) {
;
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__) {
;
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__) {
;
/**
* 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__) {
;
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
};
/***/ })
/******/ ]);
});