hey all, i’m trying to write a spec for a map using s/keys
that may contain keys that are namespaced or unnamespaced:
(s/keys :opt [::foo ::bar ::baz] :opt-un [:foo ::bar ::baz])
alright, that’s all well and good! but now i’d like to reduce the duplication (in my real-life application, the vectors have ~10 items each)
(def ks [::foo ::bar ::baz])
(s/keys :opt ks :opt-un ks)
this doesn’t work, because ks
is a symbol within the keys
macro. i’ve tried unquoting:
(def ks [::foo ::bar ::baz])
(s/keys :opt ~ks :opt-un ~ks)
but this doesn’t work either—it fails the namespace-qualification assertion within the keys
macro. any pointers on how to reuse these keys? thanks 🙂That doesn't work indeed. You will need to write macros to accomplish this because s/keys
itself is a macro
@robhanlon I came up with this helper for composing keys:
(defmacro def-domain-map
"Return s/keys :req for fields supports passing in symbols for keys"
([spec required]
(let [req (eval required)]
`(s/def ~spec (s/keys :req ~req))))
([spec required opt]
(let [req (eval required)
opt (eval opt)
global-opt (eval global-keys)]
`(s/def ~spec (s/keys :req ~req :opt ~(into opt global-opt))))))
It works from clj and cljs:
(>def :work-log/id fuc/id?)
(>def :work-log/description string?)
(>def :work-log/begin tu/date-time?)
(>def :work-log/end tu/date-time?)
(def required-work-log-keys
[:work-log/id :work-log/description :work-log/begin :work-log/end])
(def optional-work-log-keys [])
(su/def-domain-map ::work-log required-work-log-keys optional-work-log-keys)
I wanted the required and optional keys to be reused in different parts of the app without needing to maintain two lists of these keys
at some point you have to ask yourself - is this easier than just saying the keys twice?
I think I have my answer here—I’ll just repeat the keys. Thank you 🙏