Вложенные проверки с народной сказкой
Я использовал Folktale's Validation для нового проекта, и я нашел его действительно полезным, но я столкнулся с необходимостью последовательных проверок. У меня есть объект конфигурации, и мне нужно выполнить следующие проверки:
- это объект?
- действительны ли ключи объекта (они отображаются в белом списке)?
- допустимы ли значения ключей?
Каждая проверка зависит от предыдущей проверки - если элемент не является объектом, проверка его ключей бессмысленна (и приведет к ошибке), если объект не имеет ключей, проверка их значений не имеет смысла. По сути, я хочу короткого замыкания проверки, если проверка не удается.
Первоначально я думал использовать Result вместо Validatio., but mixing the two types feels confusing, and I already have
validateIsObject` определен и используется в другом месте.
Мое текущее (рабочее, но уродливое) решение здесь:
import { validation } from 'folktale';
import { validateIsObject } from 'folktale-validations';
import validateConfigKeys from './validateConfigKeys';
import validateConfigValues from './validateConfigValues';
const { Success, Failure } = validation;
export default config => {
const wasObject = validateIsObject(config);
let errorMessages;
if (Success.hasInstance(wasObject)) {
const hadValidKeys = validateConfigKeys(config);
if (Success.hasInstance(hadValidKeys)) {
const hasValidValues = validateConfigValues(config);
if (Success.hasInstance(hasValidValues)) {
return Success(config);
}
errorMessages = hasValidValues.value;
} else {
errorMessages = hadValidKeys.value;
}
} else {
errorMessages = wasObject.value;
}
return Failure(errorMessages);
};
Я изначально принял подход использования вложенных matchWith
с, но это было еще сложнее читать.
Как я могу улучшить это решение?
1 ответ
Вы можете написать помощника, который применяет правила проверки до Failure
возвращается Быстрый пример:
const validateUntilFailure = (rules) => (x) => rules.reduce(
(result, rule) => Success.hasInstance(result)
? result.concat(rule(x))
: result,
Success()
);
Мы используем concat
объединить два результата. Мы используем Success.hasInstance
чтобы проверить, нужно ли применять следующее правило. Ваш модуль теперь будет длиной в одну строку:
export default config => validateUntilFailure([
validateIsObject, validateConfigKeys, validateConfigValues
]);
Обратите внимание, что эта реализация не возвращается рано, как только она видит Failure
, Рекурсивная реализация может быть более функциональным подходом, но не всем понравится:
const validateUntilFailure = ([rule, ...rules], x, result = Success()) =>
Failure.hasInstance(result) || !rule
? result
: validateUntilFailure(rules, x, result.concat(rule(x)))
Посмотрите пример ниже для запуска кода. Там есть закомментированный раздел, который показывает, как запустить все правила, даже если есть сбои.
const { Success, Failure } = folktale.validation;
const validateIsObject = (x) =>
x !== null && x.constructor === Object
? Success(x)
: Failure(['Input is not an object']);
const validateHasRightKeys = (x) =>
["a", "b"].every(k => k in x)
? Success(x)
: Failure(['Item does not have a & b.']);
const validateHasRightValues = (x) =>
x.a < x.b
? Success(x)
: Failure(['b is larger or equal to a']);
// This doesn't work because it calls all validations on
// every item
/*
const validateItem = (x) =>
Success().concat(validateIsObject(x))
.concat(validateHasRightKeys(x))
.concat(validateHasRightValues(x))
.map(_ => x);
*/
// General validate until failure function:
const validateUntilFailure = (rules) => (x) => rules.reduce(
(result, rule) => Success.hasInstance(result)
? result.concat(rule(x))
: result,
Success()
);
// Let's try it out!
const testCases = [
null,
{ a: 1 },
{ b: 2 },
{ a: 1, b: 2 },
{ a: 2, b: 1 }
];
const fullValidation = validateUntilFailure([
validateIsObject,
validateHasRightKeys,
validateHasRightValues
]);
console.log(
testCases
.map(x => [x, fullValidation(x)])
.map(stringifyResult)
.join("\n")
);
function stringifyResult([input, output]) {
return `input: ${JSON.stringify(input)}, ${Success.hasInstance(output) ? "success:" : "error:"} ${JSON.stringify(output.value)}`;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/folktale/2.0.1/folktale.min.js"></script>