has anyone given much thought to (for the purposes of using multi-spec liberally across many namespaces) dynamically constructing "qualified keywords" at reader / compile time ?
(in clojurescript)
i've attempted both a plain macro and a tagged literal reader
this is the macro:
(defmacro ns-grouped-keyword [group keyword]
`(keyword (clojure.string/join "/" [~(str *ns*) ~group]) ~keyword))
and this is the reader:
(defn read-ns-grouped-keyword [s]
`(let [[group# kwd#] (clojure.string/split ~s #"/")]
(keyword (clojure.string/join "/" [~(str *ns*) group#]) kwd#)))
even the latter apparently only gets expanded and not eval'd at reader time
such that:
(s/keys :opt-un [#my/reader "blah/blaz"])
will not work
where as I had expected it to get eval'd to: (s/keys :opt-un [:the-current-namespace/blah/blaz])
before keys
expansion time
I am trying to use clojure.spec for validating the inputs to my API handlers. What’s the idiomatic pattern for usage here? I was thinking of creating a generic fn which takes in spec and the data to be validated. This fn can throw an exception when the validation fails. This fn would be called as the very first thing in the handlers, thus if the data is not valid the exception will act like an early exit. But this is using exceptions for control flow. The other approach I can think of is wrapping all my handlers with an if-else block that do this validation. This is fine, except it sort of makes my handlers less readable. What’s a clean approach that people have discovered for validating (and coercing) data using spec.
Can’t you have a single wrapper fn
that basically does
(let [res (s/conform input)]
(if (:s/invalid? res)
(build-error-reponse)
(real-handler input) ; real-handler could be an input to this fn
)
)
Then the real-handler
doesn’t have to worry about whether the input is valid, and your outermost layer (i.e. the one that first receives the request) can call this instead of real-handler
directly. And if you want more details out of build-error-response
you could use explain-str
or explain-data
instead of conform
. Or perhaps I’m misunderstanding the situation.This works @jeffrey.wayne.evans! Thanks!
Although I would like to have different spec for different handlers, but I presume that can be passed in as a parameter to this fn
a well, right?
Sure! You would still have to somehow organize the specs per handler. Not sure if there is an established pattern for that but you could always have a keyword based map for them (handler function name as key?)
reitit has a spec-module, you can declare request & response specs to endpoints, and there is a set of predefined middleware / interceptors to use them for validation, also coercion. Example here: https://github.com/metosin/reitit/blob/master/examples/ring-spec-swagger/src/example/server.clj