к обсуждению спеки - там есть мультиспека еще, можно попробовать ее и req-un если очень хочется извратиться
но я не уверен что стоит
я уже схему смотрю 😃 спасибо за ссылку на refined
видимо стоило это сделать как только 1.9 альфа вышла 😃
сейчас добавили возможность удалять спеки из регистра
думаю как-то покрутить, попробовать схема-стайл сделать
а чем удаление поможет?
типа
{:user/name string?
:user/email string?}
так если ты из реестра удалил, как проверка будет работать?
или в какой-то контест заворачивать?
ну оно в ф-ии проверке будет регать, потом проверять, потом дерегать
ну это так
идея
just for fun
а реестр это что?
var?
atom?
(defonce ^:private registry-ref (atom {}))
а можно как-то приватную переменную сделать публичной?
если да, тогда переменную можно сделать динамической
и не иметь проблем с параллельным доступом
реестр спек
глобальная штука
(require '[clojure.spec.alpha :as s])
(let [reg #'s/registry-ref]
(.setDynamic reg true)
(assert (.isDynamic reg))
(with-bindings {reg (atom {})}
(clojure.spec.alpha/registry)
#_(clojure.spec.alpha/def :foo/:foo int?)
#_(clojure.spec.alpha/valid? :foo/foo 1)))
только это не работает
видимо, потому что defonce
та не, добавлять спеку в глобальный реест с подхаченным ns и потом удалять
короче вряд ли будет работать
просто покрутить хочется от нечего делать
@fmnoise а как в схеме сделать and, т.е. проверить одну схему, а затем другую?
в смысле?
делаешь просто and и проверяешь обе схемы
эксперименты с реестром закончились манки-патчингом) https://gist.github.com/fmnoise/56fdd0951f593e668f4a5e40dc771577
в смысле and?
(def a {:a s/Int}) (def b( {b s/Int}) (def z (and a b)) ???
так смерж просто
это ж мапки
а если они вложенные
а елси у них один ключ?
в смысле один ключ?)
(def a {:a s/Int}) (def a’ {:a (s/pred #{1 2 3}))
Int тогда избыточный
второй схемы хватит
там надо мозги перестроить от спеки
что подчас тяжело
другая философия совсем
допустим, у меня есть сущность
и у нее есть разные сосотяния
причем состояние не одно поле
ну вот там начинается веселое
а набор полей
constraint
а толку?
если он ошибку не выдаст подробную
это просто предикат проверить
там с ошибками так себе
там можно добавить свой типа символ
который будет означать ошибку
'does-not-looks-good
типа такого
это сама спека делает?
или расширение?
что именно?
символ добавить
https://speakerdeck.com/kachayev/keep-your-data-safe-with-refined-types?slide=32
вот как тут в примере
ты сам добавляешь
а, я думал это что-то вроде `{:a {^:name ’symbol} s/Int}
оно тебе его просто в валидации возвращает типа как код ошибки
типа осмысленное сообщение об ошибке можно сделать
ребята развили эту же идею и с guards в refined https://speakerdeck.com/kachayev/keep-your-data-safe-with-refined-types?slide=62
вообще у самой схемы composability очень слабое место
у spec еще хуже 😃
нет, у spec лучше
пример https://speakerdeck.com/kachayev/keep-your-data-safe-with-refined-types?slide=49
и следующий слайд
так как из фукнции вернуть спеку?
верне вернуть то можно
но толку
в смысле из функции
я про то, что схема которая сложнее чем просто мапка это вещь в себе
ну типа (fn [] (s/keys ....)
добавить\убрать что-то в нее не получится
оно вернет
например когда ты в constrained завернул что-то, потом ты не сможешь сделать assoc или dissoc
а без constrained она очень простая
и большинство задач не решает
кстати вот эта фишка и была довольной сильной мотивацией при создании schema-refined
мы намучились тогда со схемой прилично
что-то мне подсказывает, что я буду все руками делать 😃
короче идеального решения нет
все ждали что спека его принесет
но спека не о валидации вообще
а почему {(s/optional-key :foo) s/Keyword
а не {:foo (s/optioinal-key ....}
?
так вроде не нужно изобретать schema-tools, чтобы делать select, assoc и прочее
спроси авторов
которые уже давно забросили ее
Видимо, нужно разделять форму данных и валидацию данных под конретную бизнес операцию. Рыжиков в подкасте говорил, что в FHIR большинство полей опциональные
ну вот да
спека про форму
схема хз про что
но спеку я “сломал”
вчерашним примером
на java это можно сделать
на спеке - для ключей с неймспейсом - нет
{:publication/id 1
:publication/translation {:publication.translation/title "article"
:article.translation/content "some content"}
:article/image-url "http://..."}
пока это единственное непонятное место в спеке
так а еще раз, что у тебя не получилось?
посмотри java код
есть публикация и статья
есть перевод публикации и перевод статьи
первые - родители для вторых
и у статьи может быть переопределенный метод, возвращающий перевод статьи вместо перевода публикации
т.е. в clojure это как-то так:
{:publication/id 1
:publication/translation {:publication.translation/title "article"}}
это публикация
и у нее есть “метод” :publication/translation
и я хочу его расширить
новыми ключами
и ключи должны быть с неймспейсом
для req-un можно сделать
интересно именно для req
в смысле расширить*
s/merge
ты один раз определил и вот оно
смотри
есть (s/def :foo (s/keys :req [::a ::b]))
а есть его расширение
(s/def ::foo' (s/merge ::foo (s/keys :req [::c}))
и это работает только на первом уровне
а для вложенных не работает
т.е. единсввенное что я могу сделать:
{:publication/id 1
:article/translation {:publication.translation/title "article"
:article.translation/content "foo"}}
вместо
{:publication/id 1
:publication/translation {:publication.translation/title "article"
:article.translation/content "foo"}}
т.е. есть фукнция, работающая с публикацией
и ее переводами
и я не могу ей передать статью
т.к. у нее переводы не в том ключе лежат
так статью не можешь или публикацию?
функция принимает публикацию
но статья - это расширение публикации
и фукнкция должна работать с ней
получается ключ :article/translation, а должен быть :publication/translation
ну сделай на первом уровне через :opt [:article/translation :publication/translation]
публикация не знает своих потомков
и отдельно проверяй что какой-то из них точно должен быть
потомки знают публикацию
потом появится галерея
и еще чего-нибудь
по факту это циклическая зависимость
не понял про потомков
у тебя мапка с id и translation
ага
только они с неймспейсами
ну в мапке у тебя или publication неймспейс или article
нет
должен быть только publication у всех
(defn title [publication]
(-> publication :publication/transalation :publication/title))
скорми ей статью
так вообще тогда проблемы не вижу
а где начинается article?
у тебя во внутренней мапке либо :article.translation/content
либо :publication.translation/content
?
или как?
(ns publication.translation)
(s/def ::title string?)
(s/def ::translation (s/keys :req [::title]))
(ns publication
(:require [publication.translation :as translation]))
(s/def ::id int?)
(s/def ::publication (s/keys :req [::id ::translation/translation]))
(ns article.translation
(:require [publication.translation :as base]))
(s/def ::content string?)
(s/def ::translation (s/merge ::base/translation (s/keys :req [::content])))
(ns article
(:require
[publication :as base]
[publication.translation :as base.translation]
[article.translation :as translation]))
(s/def ::image-url string?)
(s/def ::article (s/merge ::base/publication
(s/keys :req [::image-url])
;;;;;
(s/keys :req [::translation/translation :as ::base/translation])))
> у тебя во внутренней мапке либо :article.translation/content
либо :publication.translation/content
?
{:publication/id 1
:publication/translation {:publication.translation/title "article"}}
{:publication/id 1
:publication/translation {:publication.translation/title "article"
:article.translation/content "foo"}
:article/image-url "http://"}
{:publication/id 1
:publication/translation {:publication.translation/title "article"
:gallery.translation/author "foo"}
:gallery/image-urls ["http://"]}
или не понятно?
ну тут надо просто определить все что только может быть под ключем :publication/translation
publication.translation/title я вижу req
на зависимости посмотри
остальноe opt
не может publication.translation зависеть от article.tranlation
будет цикл
и это не расширяемо
ну тут зависимостей не может быть
т.е. это невозможно в спеке сделать
тут напихал ключей, написал ф-ю которая проверяет их дополнительно
ну или добавлять тип и делать через мультиспеку
я б добавил в :publication/translation
:publication.translation/type
в общем, в спеке это не делается для этих данных тут нужен какой-то (s/merge-in)
и зависимо от него уже через мультиметод решал
да, но этот тип должен быть в верхней мапе по логике
ну и что
т.е. данные под спеку подгонять? 😃
наверное наоборот должно быть
я не понял тип у публикации или у перевода?
{:publication/id 1
:publication/translation {:publication.translation/title "article"
:article.translation/content "foo"}
:article/image-url "http://"
:publication/type :article}
о, тип у публикации и от него зависят ключи перевода
{:publication/id 1
:publication/translation {:publication.translation/title "article"
:publication.translation/type :article
:article.translation/content "foo"}
:article/image-url "http://"}
так не правильно
это простой случай, переводов может быть много
но это не рассматриваем
ну тогда да, придется в каждый перевод добавлять тип
чтобы спека работала
(s/def ::article (s/merge (s/merge-in ::base/publication
[::base.translation/translation]
(s/keys :req [::translation/content]))
(s/keys :req [::image-url])))
вот что-то такое нужно
попробую поподробнее расписать и в #clojure-spec вбросить
давай)
вагную тебе там скажут у тебя кривая схема данных)
кривые данные?
=D
короче да
тут можно порешать 2 ключами с типами
может можно как-то и без типа, а привязаться к наличию специального атрибута
например article.translation/content или gallery/image-url
типа если есть gallery/image-url значит это галерея
если есть article.translation/content значит это артикл
но это опять же применение типа, просто базирующееся на том что некие ключи соответствуют одному типу, а некоторые другому
плюс в том что мультиспека позволяет диспатчить как угодно
вот пример похожий на твой
(s/def ::base (s/keys :req [:pub/title]))
(s/def ::image (s/merge ::base (s/keys :req [:img/url :img/alt])))
(s/def ::article (s/merge ::base (s/keys :req [:article/title :article/author])))
(def types {:img ::image
:article ::article})
(defn trans-type [m]
(types (some (-> types keys set) (->> m keys (mapv (comp keyword namespace))))))
(defmulti t identity)
(defmethod t :default [v] (trans-type v))
(s/def ::t (s/multi-spec t :translation.type))
@kuzmin_m ^^