I’ve been experimenting with clojure.spec on a new web project, as a way of validating data coming in from the client. We’re using JSON API as the lingua franca between the server and client, which adds a not-insignificant amount of ceremony and overhead to the JSON payload. So once I got one namespace’s endpoints spec’ed correctly, the obvious next step was to generalize it with a function that looked like this (edited for brevity):
What we’re accomplishing here with (keyword api-ns "attributes")
is building a keyword that looks like :my-project.api.users/attributes
. Or at least, that’s what I thought we were doing; here’s the error I ran into:
CompilerException java.lang.AssertionError: Assert failed: k must be namespaced keyword or resolvable symbol
That’s weird — (keyword api-ns "attributes")
on the REPL is building a clojure.lang.Keyword
like I expected it to, so why is an assert that it’s a keyword failing?
The answer lies in how the macro behind clojure.spec/def
is implemented. Macroexpanding (s/def (keyword "spec-test-ns" "name") string?)
gives you:
So since it’s quote
ing our input, it’s not resolving to the keyword before the assert. The solution I came to is to build a macro that unquotes my keyword building before s/def
is called:
I generally prefer non-macro solutions when available, but as of now, this seems like the only way to DRY up this particular pattern.