Hi there – I’m quite new and am adding spec to my re-frame project to verify app state on updates. I’m also trying to structure the DB map. But it seems what I want to do isn’t allowed, so maybe my approach is off Any pointers would be greatly appreciated.
In structuring the DB, one way to go is nested maps:
{:person {:name "Bob"
:passion "Painting"}}
Another way for this example might be namespaced keywords:
{:person/name "Bob"
:person/passion "Painting"}
I started with this, but when adding Spec I tried to do something like:
(s/def ::person/name string?)
But it didn’t seem to take – maybe because it wouldn’t be possible to resolve whether this is :person/name in the current namespace or :name in another namespace?
Is this not an idiomatic way of doing things? Am I using namespaced keywords incorrectly? Should I just stick with nested maps?
As for being idiomatic, I think there is a trend towards increasing use of qualified (ie namespaced) keywords, but there are still situations where you may want or be fine with unqualified keywords. A major deciding factor is if the keys will end up inside maps where there may be name clashes. If so you can avoid this with qualified keys.
The :person map needs to be qualified, but it’s qualified under the current namespace whereas the :person/name and :person/passion keywords are “qualified” under a non-existant “:person” namespace. Correct?
It seems bad to have them be qualified to different namespaces, but I like being able to use :person/name.
You can use any qualified keyword you want for the Spec. :person/person for example. As you noted, the qualified keywords inside the hash map do not have to be tied to a code namespace – and they are “just” Specs – and neither does the overall Spec for the map.
Would you then separate those specs into different namespaces (and files)? To me, it seems like a lot to create a new file with a small number of specs, simply to namespace them – which is why the “un-namespaced” form :person/name worked for me.
Is there convention on where/how to organize specs in a project?
I’d stick the specs in the same Clojure file unless you have a reason to do otherwise. Then you can also use the ::name shorthand to get a ns qualified key for the current ns.
An example for when you need to put specs somewhere else is if you need to add specs for someone else’s library.
I think the spec guide is the best resource you’ll find. If that doesn’t suffice, you can try posting in the Clojurians Slack and hope for a reply by Alex Miller
Overall, I think I see where you’re coming from. Ns qualified keys didn’t make sense to me at first. I don’t think you have to worry too much about it. Ns qualified keys are great for avoiding conflicts. But if you don’t need that yet, normal keywords are probably fine!
No, but there are some considerations that might apply which would provide some guidance:
If you are writing a library that you want folks to be able to use on Clojure 1.8 or earlier, you must put the specs in a separate namespace that those users can simply ignore.
If you are writing a library targeting Clojure 1.9 or later, if you want usage of your specs to be optional for users, you still need to put them in a separate namespace that users can either require or not.
Otherwise, it’s generally easier to keep your function specs (fdef) with the functions that they describe (above them is probably clearer).
For data specs, it’s reasonable to keep them all together in one namespace, perhaps with predicate functions that they depend on.
If you have a large number of data specs, they’ll probably fall naturally into several different domain groups and each could be in its own namespace.