#clojure #clojure.spec
Вопрос:
Я хочу сгенерировать набор спецификаций на основе некоторых данных, которые я извлекаю из запроса. Я хотел бы динамически определить некоторые спецификации на основе данных, которые я получаю.
(def my-key :frame-data/pretty_name) ;;imagine this and the validator aren't hardcoded
(def validator string?)
(s/def my-key validator?) ;; defines my-ns/my-key, instead of `:frame-data/pretty_name.
(s/describe my-key) ;; string? ;;which sorta works, but its looking up `my-ns/my-key` instead of :frame-data/pretty_name
Моя цель-иметь спецификацию, которая выглядит так, как я написал:
(s/def :frame-data/pretty_name string?)
Я новичок в clojure, поэтому у меня нет четкого представления о том, как это можно сделать, но я попробовал несколько вещей:
(s/def (eval my-key) validator) ;;Assert Failed: k must be a namespaced keyword or resolveable symbol
(definemacro def-spec [key validator]
'(s/def ~key ~validator))
(def-spec my-key validator) ;; my-ns/my-key ;; returns the same as earlier
и много вариаций на этот счет, но я не уверен, как можно динамически определить спецификацию, но мне кажется, что так и должно быть.
Ответ №1:
Вы можете выполнить макрорасширение clojure.spec.alpha/def
формы, чтобы увидеть, до чего она расширяется:
(macroexpand `(s/def :frame-data/pretty_name string?))
;; => (clojure.spec.alpha/def-impl (quote :frame-data/pretty_name) (quote clojure.core/string?) string?)
Или взгляните на исходный код clojure.spec.alpha/def
.
Затем напишите свой собственный макрос, в котором не указан ключ спецификации, чтобы его можно было оценить:
(defmacro defspec [k spec-form]
`(s/def-impl ~k (quote ~spec-form) ~spec-form))
Пример:
(defspec my-key validator)
(s/valid? my-key "abc")
;; => true
(s/valid? my-key 123)
;; => false
(s/describe my-key)
;; => validator
Если вам не нравится , что (s/describe my-key)
возвращается validator
, попробуйте заменить (quote ~spec-form)
в определении макроса просто ~spec-form
, может быть, вам это понравится больше.
Комментарии:
1. Похоже, это больше соответствует моим потребностям, спасибо. Замена на просто
~spec-form
, вместо печати «валидатор» печатает#object[clojure.core#string_QMARK...]
вместо этого. Гораздо менее читабельно, но, возможно, это мои единственные два варианта.
Ответ №2:
Я бы хотел получить лучший ответ, но, похоже, это работает, только s/describe
возвращается validator
вместо string?
(eval `(s/def ~my-key validator)) ;; :frame-data/pretty_name
(s/describe my-key) ;; validator
(s/valid? my-key "some string") ;; true
(s/valid? my-key 123) ;; false
В качестве альтернативы работает странно выглядящий макрос, но двойная тильда выглядит странно, и странно использовать eval:
(defmacro define-spec [spec-key validator]
`(eval `(s/def ~~spec-key validator)))
(define-spec my-key validator) ;; :frame-data/pretty_name