Валидация строк с validate.it.js

Если вспомнить все ТЗ с описаниями валидации полей — они всегда выглядили примерно так:


  • не должно быть короче 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 сообщество. Что бы самому ничего не делать он покрывал потребности сообщества, а не мои собственные.


I need you


Я приглашаю 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 или ридми проекта.

© Habrahabr.ru