Как всегда генерировать данные для дополнительных ключей в спецификации?

Если у меня есть спецификации, как

(clojure.spec/def ::person (clojure.spec/keys :req [::name ::address] :opt [::age]))

И когда я делаю

(clojure.spec.gen/generate (clojure.spec/gen ::person))

Есть ли способ сказать генератору всегда учитывать дополнительный ключ (и) при генерации данных для него?

Я знаю, что это можно сделать с помощью пользовательских генераторов, но я хотел знать, есть ли уже доступные для него функциональные возможности или, возможно, более простой подход, который не требует от меня определения пользовательского генератора.

2 ответа

Решение

Мой подход состоял в том, чтобы пройти через форму (используя clojure.spec.alpha/form) этой спецификации, объедините необязательные ключи в необходимые ключи, если спецификация была создана с использованием clojure.spec.alpha/keys и, наконец, восстановить спецификацию.

(defn merge-opt-keys
  "Merges optional keys into requried keys (for specs which are created using `clojure.spec.alpha/keys`) using a spec's form/description"
  [fspec]
  (let [keymap (into {} (map (fn [pair] (vec pair)) (partition 2 (rest fspec))))]
    (->> (cond-> {}
           (contains? keymap :opt)
             (assoc :req (vec (concat (keymap :req) (keymap :opt))))
           (contains? keymap :opt-un)
             (assoc :req-un (vec (concat (keymap :req-un) (keymap :opt-un)))))
         (mapcat identity)
         (cons 'clojure.spec.alpha/keys))))

(clojure.spec.alpha/def ::name string?)
(clojure.spec.alpha/def ::desc string?)
(clojure.spec.alpha/def ::book (clojure.spec.alpha/keys :req [::name] :opt [:desc]))

(clojure.spec.gen.alpha/generate (clojure.spec.alpha/gen (eval (merge-opt-keys (clojure.spec.alpha/form ::book)))))

Я думаю, что короткий ответ на ваш вопрос "нет", но вы могли бы s/merge Ваша спецификация с той, которая требует дополнительных ключей:

(s/def ::name string?)
(s/def ::age pos-int?)
(s/def ::person (s/keys :req [::name] :opt [::age]))

(gen/sample (s/gen ::person)) ;; ::age not always gen'd
(gen/sample                   ;; ::age always gen'd
  (s/gen (s/merge ::person (s/keys :req [::age]))))

И вы могли бы написать макрос, который генерирует s/keys Spec W / Generator, который делает это.

Другие вопросы по тегам