Created
October 14, 2017 11:44
-
-
Save stuarthalloway/f4c4297d344651c99827769e1c3d34e9 to your computer and use it in GitHub Desktop.
I think it would be a mistake to introduce temporal coupling to prevent typos.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; I think it would be a mistake to introduce temporal coupling to prevent typos. | |
;; The example program below lets you identify "missing" keys specs at | |
;; the time and place of your choosing, and then handle them as you | |
;; deem appropriate, without imposing those decisions on other | |
;; users of spec. | |
(require '[clojure.spec.alpha :as s] | |
'[clojure.set :as set]) | |
(defn keyspec-form? | |
"Returns true if x is the s/form of a keys spec" | |
[x] | |
(and (sequential? x) (= 'clojure.spec.alpha/keys (first x)))) | |
(defn keyspec-form-keys | |
"Returns set of keys mentioned in s/form of a keys spec" | |
[x] | |
(into | |
#{} | |
(comp (filter vector?) | |
cat) | |
x)) | |
(defn keyspec-keys | |
"Given an s/registry, returns a map from keys spec names to | |
the set of qualified keys referenced by the spec." | |
[registry] | |
(into | |
{} | |
(comp (map (fn [[k v]] [k (s/form v)])) | |
(filter (fn [[_ v]] (keyspec-form? v))) | |
(map (fn [[k v]] [k (keyspec-form-keys v)]))) | |
registry)) | |
(defn possible-keyspec-typos | |
"Given a spec registry, returns a map from keys specs to | |
keynames missing from the registry. Useful for e.g. finding | |
typos when you believe you have specified all keys" | |
[registry] | |
(let [ksks (keyspec-keys registry) | |
rks (into #{} (keys registry))] | |
(into | |
{} | |
(comp (map (fn [[n ks]] | |
[n (set/difference ks rks)])) | |
(filter (fn [[_ ks]] (seq ks)))) | |
(keyspec-keys registry)))) | |
(comment | |
(s/def ::foo int?) | |
(s/def ::bar (s/keys :req-un [::foo ::quux])) | |
;; this will show you that ::quux is missing | |
(possible-keyspec-typos (s/registry)) | |
) | |
Something like this could work for catching missing/mistyped kyes in multi-specs:
Added
(defn multi-spec-form?
"Returns true if x is the s/form of a multi spec"
[x]
(and (sequential? x) (= 'clojure.spec.alpha/multi-spec (first x))))
(defn multi-spec-sub-specs
"Given a multi-spec form, call its multi method methods to retrieve
its subspecs in the form of [multi-method-key sub-spec-form]."
[multi-spec-form]
(let [[_ multi-method-symbol & _] multi-spec-form]
(->> (resolve multi-method-symbol)
deref
methods
(map (fn [[spec-k method]]
[spec-k (s/form (method nil))])))))
and changed keyspec-keys
like
(defn keyspec-keys
"Given an s/registry, returns a map from keys spec names to
the set of qualified keys referenced by the spec."
[registry]
(into
{}
(comp (mapcat (fn [[k v]] (let [form (s/form v)] ;; <---------- changed this map
(if (multi-spec-form? form)
(map (fn [[sk ss]] [[k sk] ss]) (multi-spec-sub-specs form))
[[k form]]))))
(filter (fn [[_ v]] (keyspec-form? v)))
(map (fn [[k v]] [k (keyspec-form-keys v)])))
registry))
and now it returns
(possible-keyspec-typos (s/registry))
;; =>
{:spec-test.core/bar #{:spec-test.core/quux},
[:spec-test.core/msg :count] #{:spec-test.core/num}}
I found the following macro variation to enforce some of Plumatic Schema's strictness useful too https://gist.github.com/jeroenvandijk/4fb995a45e49d0e9986d2005ee5840d9
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Very cool! Just a head up though for anyone using this code - this won't catch missing/mistyped keys in multi-specs.