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
236 lines (182 loc) • 10.6 kB
JavaScript
;
exports.__esModule = true;
exports.makeEffectHandler = exports.makeResponseHandler = exports.makePredicate = exports.defaultErrorHandler = exports.defaultSuccessHandler = undefined;
var _ifElse = require('ramda/src/ifElse');
var _ifElse2 = _interopRequireDefault(_ifElse);
var _tryCatch = require('ramda/src/tryCatch');
var _tryCatch2 = _interopRequireDefault(_tryCatch);
var _adjust = require('ramda/src/adjust');
var _adjust2 = _interopRequireDefault(_adjust);
var _pipe = require('ramda/src/pipe');
var _pipe2 = _interopRequireDefault(_pipe);
var _identity = require('ramda/src/identity');
var _identity2 = _interopRequireDefault(_identity);
var _F = require('ramda/src/F');
var _F2 = _interopRequireDefault(_F);
var _always = require('ramda/src/always');
var _always2 = _interopRequireDefault(_always);
var _T = require('ramda/src/T');
var _T2 = _interopRequireDefault(_T);
var _test = require('ramda/src/test');
var _test2 = _interopRequireDefault(_test);
var _pathEq = require('ramda/src/pathEq');
var _pathEq2 = _interopRequireDefault(_pathEq);
var _cond = require('ramda/src/cond');
var _cond2 = _interopRequireDefault(_cond);
var _pick = require('ramda/src/pick');
var _pick2 = _interopRequireDefault(_pick);
var _evolve = require('ramda/src/evolve');
var _evolve2 = _interopRequireDefault(_evolve);
var _is = require('ramda/src/is');
var _is2 = _interopRequireDefault(_is);
var _pathSatisfies = require('ramda/src/pathSatisfies');
var _pathSatisfies2 = _interopRequireDefault(_pathSatisfies);
var _when = require('ramda/src/when');
var _when2 = _interopRequireDefault(_when);
var _objOf = require('ramda/src/objOf');
var _objOf2 = _interopRequireDefault(_objOf);
var _unless = require('ramda/src/unless');
var _unless2 = _interopRequireDefault(_unless);
var _ = require('ramda/src/__');
var _2 = _interopRequireDefault(_);
var _merge = require('ramda/src/merge');
var _merge2 = _interopRequireDefault(_merge);
var _compose = require('ramda/src/compose');
var _compose2 = _interopRequireDefault(_compose);
var _curry = require('ramda/src/curry');
var _curry2 = _interopRequireDefault(_curry);
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 _shapey = require('shapey');
var _is3 = require('../util/is');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* A slightly opinionated way to handle successful effects and creating a new
* Action to be dispatched. This default success handler assumes you're alright
* with appending _SUCCESS to the original action type, and merging the return object
* (from your effect function) into the new Action. However, if your effect does NOT
* return an object, that return value will be merged onto the new Action onto a prop
* called "payload". The other assumption it makes is that if you have suffixed your
* original Action with _REQUEST or _EFFECT, that will be removed. A (somewhat) standard
* pattern in the Redux community is to suffix _SUCCESS for effects that succeed
* and either _ERROR or _FAIL for those that failed. You can always override
* with your own success handler by providing it when you create an effect in
* the ducks.
*
* @func
* @sig String|{k: v} -> {k: v} -> {k: v}
* @param {String|Object} result The result returned from your effect function
* @param {Object} action The original Redux action that triggered the effect
* @returns {Object} A new Action object that contains the "payload" that was
* caught when the effect was created (unless your effect function returned an
* object, in which case it will be merged onto this)
*/
var defaultSuccessHandler = exports.defaultSuccessHandler = (0, _curry2.default)(function (result, action) {
return (0, _compose2.default)((0, _merge2.default)(_2.default, _extends({}, (0, _unless2.default)(_is3.isPlainObj, (0, _objOf2.default)('payload'))(result))), (0, _when2.default)((0, _pathSatisfies2.default)((0, _is2.default)(String), ['type']), (0, _compose2.default)((0, _evolve2.default)({ type: function type(t) {
return t.replace(/_REQUEST$/i, '').replace(/_EFFECT$/i, '') + '_SUCCESS';
} }), (0, _pick2.default)(['type']))))(action);
});
/**
* A slightly opinionated way to handle catching an exception and creating a new
* Action to be dispatched. This default error handler assumes you're alright
* with appending _ERROR to the original action type, and pruning out all of the
* other fields from the original action and setting only an "error" prop on the
* new Action. The other assumption it makes is that if you have suffixed your
* original Action with _REQUEST or _EFFECT, that will be removed. A (somewhat) standard
* pattern in the Redux community is to suffix _SUCCESS for effects that succeed
* and either _ERROR or _FAIL for those that failed. You can always override
* with your own error handler by providing it when you create an effect in
* the ducks.
*
* @func
* @sig String|{k: v} -> {k: v} -> {k: v}
* @param {String|Object} error An error that was caught when the effect was
* triggered
* @param {Object} action The original Redux action that triggered the effect
* @returns {Object} A new Action object that contains the "error" that was
* caught when the effect was created
*/
var defaultErrorHandler = exports.defaultErrorHandler = (0, _curry2.default)(function (error, action) {
return (0, _compose2.default)((0, _merge2.default)({ error: (0, _unless2.default)(_is3.isPlainObj, String)(error) }), (0, _when2.default)((0, _pathSatisfies2.default)((0, _is2.default)(String), ['type']), (0, _compose2.default)((0, _evolve2.default)({ type: function type(t) {
return t.replace(/_REQUEST$/i, '').replace(/_EFFECT$/i, '') + '_ERROR';
} }), (0, _pick2.default)(['type']))))(action);
});
/**
* Makes a predicate function out of either a String, RegExp, or a Function,
* which can then be applied to an Object that contains a "type" property.
* This is meant to be used to evaluate if a Redux action matches the predicate.
* If neither a String, RegExp, nor Function is supplied here, then the result
* will be to give back a function that always returns false.
*
* @func
* @sig String|RegExp|({k: v} -> Boolean) -> ({k: v} -> Boolean)
* @param {String|RegExp|Function} pattern A String to be exactly matched to an
* Object's "type" property, OR a Regular expression to be matched against it,
* OR a function to be used to match against any custom criteria on that Object
* @returns {Function} A predicate function to be applied later to an Object
* (ideally, one containing a "type" property)
*/
var makePredicate = exports.makePredicate = (0, _cond2.default)([[(0, _is2.default)(String), (0, _pathEq2.default)(['type'])], [(0, _is2.default)(RegExp), (0, _compose2.default)((0, _pathSatisfies2.default)(_2.default, ['type']), _test2.default)], [(0, _is2.default)(Function), function (fn) {
return (0, _compose2.default)(Boolean, fn);
}], [_T2.default, (0, _always2.default)(_F2.default)]]);
/**
* Creates a function that handles the effect result (either success or failure)
*
* @func
* @sig (a -> b) -> (a -> b) -> (a -> b)
* @param {Function} defaultHandler The fall through handler function to be used
* in case the handler passed into this function is null/undefined
* @param {String|Object|Function} handler The handler function or the
* String/Object to be turned into a handler function (if String/Object, it will
* be turned into a Shapey spec-mapping function)
* @returns {Function} A success OR error handler function to be applied after
* the effect is finished
*/
var makeResponseHandler = exports.makeResponseHandler = (0, _curry2.default)(function (defaultHandler, handler) {
return (0, _cond2.default)([[(0, _is2.default)(String), (0, _compose2.default)(_shapey.makeShaper, (0, _objOf2.default)('type'))], [_is3.isPlainObj, _shapey.makeShaper], [(0, _is2.default)(Function), _identity2.default], [_T2.default, (0, _always2.default)(defaultHandler)]])(handler);
});
/**
* Creates a robust effect handler from a predicate, an effect creating
* function, as well as a success and error handler. This curried function can
* then be applied safely to any Action that matches the predicate (if the
* Action does NOT match, this is simply an identity function - which returns
* the Action as-is).
*
* @func
* @param {String|RegExp|Function} pattern A String to be exactly matched to an
* Object's "type" property, OR a Regular expression to be matched against it,
* OR a function to be used to match against any custom criteria on that Object
* @param {Function} effectHandler A custom function that creates some kind of
* normal (but "impure") effect which may succeed or it may fail
* @param {Function} successHandler A function that receives the input of the
* effect creating function and creates a new Action containing its result
* @param {Function} errorHandler A function that receives the caught exception
* from the effect creating function and creates a new Action from it
* @returns {Object} Either the original action (if it didn't match the
* predicate) or a new Action that represents the succes of the effect or
* alternatively it's failure
*/
var makeEffectHandler = exports.makeEffectHandler = (0, _curry2.default)(function (effect, action) {
return (0, _pipe2.default)((0, _adjust2.default)(makePredicate, 0), function (_ref) {
var pattern = _ref[0],
effectHandler = _ref[1],
successHandler = _ref[2],
errorHandler = _ref[3];
return [makePredicate(pattern), effectHandler, makeResponseHandler(defaultSuccessHandler, successHandler), makeResponseHandler(defaultErrorHandler, errorHandler)];
}, function (_ref2) {
var predicate = _ref2[0],
effectHandler = _ref2[1],
successHandler = _ref2[2],
errorHandler = _ref2[3];
return (0, _when2.default)(predicate, (0, _tryCatch2.default)((0, _pipe2.default)(effectHandler, (0, _ifElse2.default)(_is3.isPromise, function (promise) {
return promise.then(function (res) {
return successHandler(res, action);
}).catch(function (err) {
return errorHandler(err, action);
});
}, function (res) {
return successHandler(res, action);
})), function (ex) {
return errorHandler(ex, action);
}))(action);
})(effect);
});