Если вспомнить все ТЗ с описаниями валидации полей — они всегда выглядили примерно так:
- не должно быть короче 6 символов
- не должно превышать 12 символов
- должно включать только латинские символы, цифры и знак подчёркивания
Требования часто приходят набором простых однозначных фраз. А мы, программисты, переводим эти требования в код.
Можно превращать их в одно ультимативное регулярное выражение, вроде
const validateLogin = login => /^[a-zA-z_d]{6,12}$/.test(login);
Но лучше писать более простые функций которые легче читать и связывать с непосредственным ТЗ:
const charMatch = new RegExp('^[a-zA-Z_0-9]*$');
const validateLogin = login => {
if (login.length < 6) return false;
if (login.length > 12) return false;
if (!charMatch.test(login)) return false;
return true;
};
А что если ещё сильнее упростить этот код до чего-то вроде:
const validateLogin = login =>
validate(login)
.notLessThan(6)
.notLongerThan(12)
.hasOnly(['a-z','A-Z','0-9','_']);
Основная идея заключается в том, что требования к валидации зачастую можно разложить на список независимых типичных утверждений. А эти утвержджени (asserts) можно собрать в коллекцию и переиспользовать.
Именно этим и занимается библиотека validate.it.js. Ядро которой не превышает и ста строк и делает не так уж много:
- позволяет вызывать assert'ы цепочно
- собирает и обрабатывает результаты
Вышеописанное значит, что выполнив код вроде этого:
validate('Pa$$w0rd')
.hasLettersLatin()
.hasNumbers()
.has("!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+");
Вы получите вот такой результат.
{
ok: true,
base: 'Pa$$w0rd',
asserts: ['hasLettersLatin', 'hasNumbers', 'has'],
errors: []
}
Думаю это и так очевидно, но немного распишу
ok
bool — статус валидацииtrue
— валидация прошла успешноfalse
— валидация провалена
base
string — валидируемая строкаasserts
array — список имён вызванных assert'ов в порядке очерёдности их вызоваerrors
array — массив отчётов в формате Validation Report всех провалившихся assert'ов
Вот пример провалившейся валидации
validate('bob')
.hasLettersLatin()
.hasNumbers();
// -->
{
ok: false,
base: 'bob',
asserts: ['hasLettersLatin', 'hasNumbers'],
errors: [
{
path: [],
rule: 'hasNumbers',
details: {
string: 'bob',
subStrings: ["1","2","3","4","5","6","7","8","9","0"],
found: false,
message: '"bob" has no numbers'
}
}
]
}
Эта простая идея позволяет писать код валидации максимально приближённым к требованиям. При этом сами assert'ы получаются очень простыми и являются чистыми функциями. Их легко поддерживать, тестировать, и добавлять новые.
Кстати да, если вам не хватает какого-то assert'а — вы легко можете на-лету добавить его через @static .extend
или использовать assert .eval
.
Но не жадничайте своих assert'ов сообществу. Станьте контрибьютером!
Контрибьютерам
А теперь сюрприз. Самих assert'ов в библиотеке ещё практически и нет. Только парочка базовых вроде .has
, .match
, .eval
и ещё немного для примеров. Нету даже тех, которые я использовал в листингах этого поста. Всё дело в том, что мне хотелось привлечь внимание к идее, а не к реализации.
Более того, я считаю, что моё видение необходимых assert'ов может сильно отличаться от видения сообщества. И, делая этот инструмент "под себя" я могу сделать его неудобным для других JS разработчиков. И у меня возникла идея — привлечь к созданию этого инструмента JS сообщество. Что бы самому ничего не делать он покрывал потребности сообщества, а не мои собственные.
Я приглашаю JS разработчиков стать контрибьютерами validate.it.js.
Всех кто хочет контрибьютить в опенсорс, но не знает с какого проекта начать.
Всех кто хочет сделать для себя и для всего сообщества удобный инструмент валидации.
Вместе мы сможем наполнить коллекцию реально необходимыми всем нам assert'ами.
При этом контрибьютером может быть разработчик любого уровня. Ведь кто-то захочет заняться валидацией по длине строки, а кому-то, например, интереснее реализовывать проверку на соответствие даты всем вариантам стандарта ISO 8601.
К pull-request'ам предъявляются весьма мягкие требования:
- наличие тестов
- наличие описания — что бы вставить его в раздел с перечнем assert'ов.
Validation Report
Кстати о вышеупомянутых VR которыми наполняется массив .errors
в случае провалившихся assert'ов. Я искал какой-то стандарт для представления ошибок валидации. И по своему опыту знал, что банального true
/false
и даже null
/'error message'
недостаточно. И судя по всему, такого стандарта на сегодняшний день нету.
Зато есть замечательная идея читателя rumkin
"Универсальный интерфейс отчётов валидации" — @rumkin / Validation Report. git
Это простой DTO, который позволяет передать максимально подробную информацию о причинах провала валидации. Состоит из:
path
array — местоположение объекта проверки. В контексте валидации строк — это всегда пустой массив[]
.rule
string — имя assert'а или правило по которому проводилась валидация.details
object — детали или описание в "машино-понятном виде" причины провала валидации. У этого параметра нету чётко-стандартизированный структуры, кроме одного поля:details.message
string — описание в "человеко-понятном виде". Это не стандартное для VR поле, но в рамках данного проекта оно обязательно.
Пример:
{
path: [],
rule: 'notLongerThen',
details: {
string: 'markopolo',
length: 9,
max: 6,
message: '"markopolo" longer then 6 characters'
}
}
rumkin, кстати, написал небольшой тул для генерации Validation Report'ов. но ради 0Dependecy я не использую конкретно эту реализацию. Благо генерация VR достаточно простая задача.
Будет здорово, если VR однажды станут стандартом, хотя бы локальным.
P.S.
Не стесняйтесь задавать вопросы — буду только рад расширить contributer's guide или ридми проекта.
Автор: titulusdesiderio