immutable-validation
Version:
Data validation using immutable data structures
200 lines (148 loc) • 7.35 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _immutable = require('immutable');
var _immutable2 = _interopRequireDefault(_immutable);
var _curry = require('ramda/src/curry');
var _curry2 = _interopRequireDefault(_curry);
var _pipe = require('ramda/src/pipe');
var _pipe2 = _interopRequireDefault(_pipe);
var _both = require('ramda/src/both');
var _both2 = _interopRequireDefault(_both);
var _tap2 = require('ramda/src/tap');
var _tap3 = _interopRequireDefault(_tap2);
var _util = require('./util');
var _ValidatorWrapper = require('./ValidatorWrapper');
var _ValidatorWrapper2 = _interopRequireDefault(_ValidatorWrapper);
var _ValidationRecord = require('./ValidationRecord');
var _ValidationRecord2 = _interopRequireDefault(_ValidationRecord);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var MESSAGE = 'message';
var RULES = 'rules';
var IS_VALID = 'isValid';
function assertRuleResult(result) {
var interfaceErr = new Error('Rules must return a result with the\n properties `isValid` and `message`.\n Use ImmutableValidation.rule()');
(0, _util.assert)((0, _both2.default)((0, _util.maybeHas)(IS_VALID), (0, _util.maybeHas)(MESSAGE))(result), interfaceErr);
}
function assertIterable(propName) {
var iterableErr = new TypeError('`' + propName + '` is a non-iterable value.\n ruleForEach applies a validation rule to\n each item in an iterable');
return function (obj) {
return (0, _util.assert)((0, _util.isIterable)(obj), iterableErr);
};
}
var reduceRules = (0, _curry2.default)(function (data, messagesForProp, rule) {
var _tap = (0, _tap3.default)(assertRuleResult, rule(data));
var isValid = _tap.isValid;
var message = _tap.message;
return !isValid ? messagesForProp.add(message) : messagesForProp;
});
var createResult = _ValidationRecord2.default.create;
var evaluateRules = (0, _curry2.default)(function (rules, data) {
return rules.reduce(reduceRules(data), _immutable2.default.Set());
});
var evaluateValidator = (0, _curry2.default)(function (validator, data) {
return validator.validate(data);
});
var merge = (0, _curry2.default)(function (propName, vState, result) {
return result.mergeDeepWith(function (prev, next, key) {
return key === IS_VALID ? prev && next : next;
}, vState.getIn([RULES, propName]));
});
var setResult = (0, _curry2.default)(function (propName, vState, result) {
return vState.set(IS_VALID, vState.get('isValid') && result.get('isValid')).setIn([RULES, propName], result);
});
function buildRuleSet(propName, getData, rules) {
var evaluateData = evaluateRules(rules);
var mergeWithExisting = merge(propName);
var setForProp = setResult(propName);
return function (vState, data) {
return (0, _pipe2.default)(getData, evaluateData, createResult, mergeWithExisting(vState), setForProp(vState))(data);
};
}
function buildRuleSetForValidator(propName, getData, validator) {
var evaluateData = evaluateValidator(validator);
var mergeWithExisting = merge(propName);
var setForProp = setResult(propName);
return function (vState, data) {
return (0, _pipe2.default)(getData, evaluateData, mergeWithExisting(vState), setForProp(vState))(data);
};
}
function buildRuleSetForEachValidator(propName, getData, validator) {
return function (vState, data) {
var iter = (0, _tap3.default)(assertIterable(propName), getData(data));
var result = iter.reduce(function (record, item, i) {
return setResult(i, record, validator.validate(item));
}, createResult());
return setResult(propName, vState, result);
};
}
function buildRuleSetForEachItem(propName, getData, rules) {
var evaluateData = evaluateRules(rules);
return function (vState, data) {
var iter = (0, _tap3.default)(assertIterable(propName), getData(data));
var result = iter.reduce(function (record, item, i) {
return (0, _pipe2.default)(evaluateData, createResult, setResult(i, record))(item);
}, createResult());
return setResult(propName, vState, result);
};
}
function extendRuleSets(baseValidator, ruleSet) {
var extendedValidator = new Validator();
extendedValidator.ruleSets = baseValidator.ruleSets.concat(ruleSet);
return extendedValidator;
}
var Validator = function () {
function Validator() {
_classCallCheck(this, Validator);
this.ruleSets = [];
this.validationState = _immutable2.default.Map({
isValid: true
});
}
_createClass(Validator, [{
key: 'ruleFor',
value: function ruleFor(propName, accessor) {
for (var _len = arguments.length, rules = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
rules[_key - 2] = arguments[_key];
}
var ruleSet = Validator.isInstance(rules[0]) ? buildRuleSetForValidator(propName, accessor, rules[0]) : buildRuleSet(propName, accessor, rules);
return extendRuleSets(this, ruleSet);
}
}, {
key: 'ruleForEach',
value: function ruleForEach(propName, accessor) {
for (var _len2 = arguments.length, rules = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
rules[_key2 - 2] = arguments[_key2];
}
var ruleSet = Validator.isInstance(rules[0]) ? buildRuleSetForEachValidator(propName, accessor, rules[0]) : buildRuleSetForEachItem(propName, accessor, rules);
return extendRuleSets(this, ruleSet);
}
}, {
key: 'validate',
value: function validate(dataToValidate) {
this.validationState = this.ruleSets.reduce(function (messages, ruleSet) {
return ruleSet(messages, dataToValidate);
}, _ValidationRecord2.default.create());
return this.validationState;
}
}], [{
key: 'isInstance',
value: function isInstance(obj) {
if (!obj) return false;
return (0, _util.isFunction)(obj.validate);
}
}, {
key: 'which',
value: function which(chooseValidator) {
if (!(0, _util.isFunction)(chooseValidator)) {
throw new TypeError('Call Validator.which with a function returning a validator or null');
}
return new _ValidatorWrapper2.default(chooseValidator);
}
}]);
return Validator;
}();
exports.default = Validator;