Let's say we have a map, like {::age 36}
that we can validate with
(s/def ::age number?)
(s/def ::my-map (s/keys :req [::age]))
Now, let's say that we have a function that can take either a valid value, or something that can be deref
d to obtain a valid value.
Here is a predicate that deref
able values will satisfy:
(defn deref? [x]
#?(:clj (instance? clojure.lang.IDeref x)
:cljs (satisfies? IDeref x)))
We can write a spec-producing function that produces the kind of spec we want:
(defn value-or-deref [spec]
(s/or :value spec
:deref (s/and deref? #(s/valid? spec (deref %)))))
Let's try it out
(s/valid? (value-or-deref ::my-map) {::age 36}) ; true
(s/valid? (value-or-deref ::my-map) {::age "foo"}) ; false
(s/valid? (value-or-deref ::my-map) (atom {::age 36})) ;true
(s/valid? (value-or-deref ::my-map) (atom {::age "foo"})) ; false