so I am reading docs and I am sure I will figure this out too, but someone could help me here a lot with a shortcut to speccing a hashmap that has 3 levels, the first is a fixed set of keys, second also a fixed set of keys, and the third is depending on the key either a string or a vector of keywors. so a valid example would be {:firstlevelkey {:label "string" :fields [:fieldone :fieldtwo]}} and I just want to say that on the first level only certain keys are allowed( one predefined set of keywords), second level always have to be both a :label and :fields and label always have to be string and the :fields always needs to be a vector of keywords from another set of predefined keys
i don't need a full solution, just tell me please what to use so I can read up on those specifics because it's taking forever to sieve through 🙂
I am trying to google > clojure spec "-un" and it's not going well
the more I read the less I understand.
> I just want to say that on the first level only certain keys are allowed
This isn't something Spec favors -- it follows the concept of "open for extension". So you will need additional predicates with s/and
to restrict keys (and remember that it's not really "idiomatic" to do that).
Ok, good to know, so I won't force it if it's not expected
The whole spec is going to end up looking something like this:
(s/def :key/label string?)
(s/def :field/keys #{:fieldone :fieldtwo ,,,}) ; your set of predefined keys allowed
(s/def :key/fields (s/coll-of :field/keys :kind vector?)) ; maybe you want min and/or max counts too?
(s/def :second/level (s/keys :req-un [:key/label :key/fields]))
(s/def :top/keys #{:firstlevelkey ,,,}) ; available top-level keys
(s/def :top/level (s/map-of :top/keys :second/level))
If your set of keys isn't known at spec-time, it's a bit harder but that's probably a good first cut outline to start with.
it's known 🙂 luckily
(with better names, of course)
That actually solved the restricted set of top-level keys, but at the expense of not requiring a minimum set either.
the naming of keywords is arbitrary, right? so I could do just :label instead of :key/label, it just pays off to have very specific names of symbols so there is no collision?
Right. Qualified names help avoid collisions.
Only s/keys
cares -- :req-un
treats the qualified names as just their unqualified part (`:req` would pay attention to the qualifier as well).
it's quite hard to grasp the pattern
I need to experiment with this, thank you very much
one more question that I think I know the answer for but still, if I want to spec anything, I always have to give a name to both the thing I want to spec and either use the definitions in place or create a named spec, right? So there is no way to write specs that would fit existing names automatically.
I'm not quite sure I'm following your question but I think the answer is "no, no way to do that in Spec 1".
Spec 2 does allow unqualified keys in a schema to have inline specs (predicates).
in app namespace I define something like (def whatever "string") and then in app.specs namespace I say (s/def whatever string?) and there is some magic to match them
probably a bad idea
Specs are in a completely separate "space" from regular Var names so you need to associate them yourself explicitly.
With functions, that's s/fdef
but there's nothing for other Vars.
You need to use s/valid?
or s/conform
to "apply" a spec to a value at runtime.
(does that answer your question?)
yes : )