Fork me on GitHub

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?)