Возвращаясь к валидации внешних данных: схема подошла как раз, только там коэрсии встроенные перед валидацией, а не после. Коэрсии после валидации впрочем дописываются на раз-два через schema.spec.core/run-checker
и обратная конверия "нормальные типы -> JSON" тоже без проблем.
А как можно валидировать что-то до коэрции?
Очень просто. Схема говорит "здесь разрешен только строковый ключ такого вида", а коэрсия коэрсит его в какое-то внутреннее представление.
Например, схема может говорить "строка по регэкспу #"\d{2}INV-\d{8}#"
, а коэрсия превращает "77INV-08333333#"
(без дополнительных проверок) в {:account/inventory-kind 8 :account/inventory-number 8333333}
Ну понятно, что можно проверить наличие или отсуствие каких-то полей или провести операции над строками. Но тебе ведь все равно это дело надо переводить во внутренее представление и еще раз проверять, но уже другие ствойства… Я для себя принял такой подход, сначала беру данные и пробую их коэрсить, а потом спекой валидирую уже внутрее представление данных, а не сериализованные. Мне кажется так оно понятней, ты или трансоформирушь данные или их валидируешь, разделение ответственности, что-ли…
У меня схема для внешних данных является, гм, схемой для внешних данных. Если валидация прошла, значит пришедший кусок данных валиден.
Пост-коэрсеры схемы внешних данных конвертируют внешние данные во внутреннее представление. Выход пост-коэрсера должен быть валидным внутренним представлением.
Спека описывает внутреннее представление, и это инвариант, который соблюдается внутри системы.
Обратно, перед сериализацией запускаются пре-декоэрсеры, которые конвертируют из внутреннего представления во внешнее, и результат проверяется схемой внешних данных, чтобы убедиться, что исходящее представление валидно.
Так что у меня три сущности: 1) схема внешних данных; 2) набор правил для перевода из внешнего представления во внутреннее, прицепленный к внешней схеме; 3) спека внутреннего представления.
Все правильно, я просто не понял зачем раунд валидации до коэрсии. Если данные не скоерсились - то они не пройдут валидацию внутренного представления… Разве что тебе важно поймать несоответсвие схемы внешних данных как можно скорее, по какой-то причине.
Мне важно поймать чуть-невалидные внешние данные. Например, если у меня есть коэрсия ключей "foo_bar" -> :foo-bar
, то мне важно не пропустить ключ "foo-bar"
.
Вместо того, чтобы делать это в коэрсерах, я это делаю в схеме. Кроме того, спека требует дополнительных телодвижений для "в этой мапе только такие ключи, а других нет", а в схеме это естественно.
спасибо, что поделился
А вот, кстати, по поводу “в этой мапе только такие ключи, а других нет“. Как это решается? Я понимаю, что это противоречит концепции открытого мира, но все же интреесно как это обходится, если “очень нужно”
В спеке нужно руками проверять, а в схеме это модель по умолчанию.
Поэтому я и взял схему: она fail-closed, а не fail-open, как спека.
Ну собсно я и спрашиваю как это руками проверять? Есть какие-то практики или реально бегать по мапке с сравнимать каждый ключ…?
Я не видел готового. Потребовалось - сделал бы что-то вроде #(= (set (keys %)) #{...})