Skip to content

Instantly share code, notes, and snippets.

@stathissideris
Last active August 15, 2017 12:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stathissideris/8d892c78c76b6d615ced53004ea32840 to your computer and use it in GitHub Desktop.
Save stathissideris/8d892c78c76b6d615ced53004ea32840 to your computer and use it in GitHub Desktop.
Make clojure.spec behave more like schema in terms of keys strictness (no unknown keys allowed)
;;; strict keys mode for spec
;; disclaimer: untested, most likely buggy
(require '[clojure.spec.alpha :as s])
(require '[clojure.walk :as walk])
(defmacro only-keys
[& {:keys [req req-un opt opt-un] :as args}]
`(s/and (s/keys ~@(apply concat (vec args)))
(s/map-of ~(set (concat req
(map (comp keyword name) req-un)
opt
(map (comp keyword name) opt-un)))
any?)))
(defn- strict-keys-form [form]
(walk/postwalk
(fn [x]
(if (= x 'clojure.spec.alpha/keys)
`only-keys
x))
form))
(defn- strict-keys-reg [reg]
(->> reg
(map (fn [[name spec]] [name (s/form spec)]))
(remove (fn [[_ form]] (= :clojure.spec.alpha/unknown form))) ;;ignore these and hope for the best
(filter (fn [[name _]] (keyword? name))) ;;replace value specs only
(map (fn [[name form]]
`(clojure.spec.alpha/def ~name ~(strict-keys-form form))))))
(defn with-strict-keys* [fun]
(let [spec-forms (strict-keys-reg (s/registry))]
(with-redefs [clojure.spec.alpha/registry-ref (atom (s/registry))]
(doall (map eval spec-forms))
(fun))))
(defmacro with-strict-keys [& forms]
`(with-strict-keys* (fn [] ~@forms)))
;;example
(s/def ::filename string?)
(s/def ::filter-last number?)
(s/def ::a (s/keys :req-un [::filename ::filter-last]))
(s/valid? ::a {:filename "foo" :filter-last 4 :foobar "haha"})
(with-strict-keys
(s/valid? ::a {:filename "foo" :filter-last 4 :foobar "haha"}))
(with-strict-keys
(s/explain ::a {:filename "foo" :filter-last 4 :foobar "haha"}))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment