Welcome! Please see the About page for a little more info on how this works.

0 votes
in Spec by

Question came up on slack about how to validate optional keys. I was going to point them to the docstring for s/keys but found it might need some clarification. The docstring in question:

([& {:keys [req req-un opt opt-un gen]}])
  Creates and returns a map validating spec. :req and :opt are both
  vectors of namespaced-qualified keywords. The validator will ensure
  the :req keys are present. The :opt keys serve as documentation and
  may be used by the generator.

  The :req key vector supports 'and' and 'or' for key groups:

  (s/keys :req [::x ::y (or ::secret (and ::user ::pwd))] :opt [::z])

  There are also -un versions of :req and :opt. These allow
  you to connect unqualified keys to specs.  In each case, fully
  qualfied keywords are passed, which name the specs, but unqualified
  keys (with the same name component) are expected and checked at
  conform-time, and generated during gen:

  (s/keys :req-un [:my.ns/x :my.ns/y])

  The above says keys :x and :y are required, and will be validated
  and generated by specs (if they exist) named :my.ns/x :my.ns/y 

  In addition, the values of *all* namespace-qualified keys will be validated
  (and possibly destructured) by any registered specs. Note: there is
  no support for inline value specification, by design.

  Optionally takes :gen generator-fn, which must be a fn of no args that
  returns a test.check generator.

I had expected to see something to the effect that if the keys specified by :opt or :opt-un were present, they would be validated against the specs for those keys. Instead it only mentions that they serve as documentation.

Seemed like this could perhaps use some clarification.

1 Answer

+1 vote
selected by
Best answer

"In addition, the values of all namespace-qualified keys will be validated (and possibly destructured) by any registered specs."

I read that and it gets a bit subtle. And it also makes me think then that the following would be false then, as `::c` is a registered, namespaced spec.

(s/def ::c pos-int?)
(s/valid? (s/keys :opt-un []) {:c "3"})

And the only way i can explain that is that optional keys are validated if they are present, beyond *all* namespace qualified keys being validated.
I was being a bit dense there i think, but I suppose that does cover it. I got confused by the ambiguity of "namespace-qualified keys" referring to the keys in the req, req-un, opt, and opt-un vectors as opposed to keys in the map. Anyways, thanks for the answer so late.