I find I often have this scenario:
{:type :foo
:bar 1
:baz 2}
Where I want my spec to say that the keys are :type, :bar and :baz, but also that the value of :type must be :foo. What's the most straightforward way to write that spec?
Bonus point where the generator for it properly constructs a map where :type is always :foo.have you looked at multi-spec?
seems like it would be a great match here
Multi-spec is actually where I have this problem, because the generator doesn't generate the correct :type
Like if we take the spec guide example: https://clojure.org/guides/spec#_multi_spec
Assume that instead of:
(s/def :event/type keyword?)
we have
(s/def :event/type #{:event/search :event/error})
If you try to generate an :event/event, they won't be valid to any of the type of events most of the time, because the generator doesn't know which of the :event/type to pick for the different multi-specs
I'd like to write a spec for a clojure.data.xml data structure and use said spec for generation as well. For example, say I have something like this:
{:tag :foo
:content [{:tag :bar
:content ["..."]}]}
I'm not sure what the best way to do that would be, though. That is, how do I write (spec/def ::foo ,,,) that specifies that :tag needs to be :foo and :content must conform to the ::bar spec, etc.
Is some combination of spec/keys and spec/and the way to go?It's been a while since I've used spec so this might be horribly wrong, but it might also solve both your problems ๐ ๐งต
Thank you! I'll give that a go. I tried flailing about with multi-spec a bit, but I didn't think of the merge into a base element spec, that's clever. ๐
I tested this for at least two seconds, so caveat emptor ๐
Sure thing, no worries. ๐
Actually, it's probably easier to write a spec for the Hiccup syntax using tuples and/or regexp ops.
(spec/def ::messageId
(spec/tuple #{:messageId} string?))
(spec/def ::from
(spec/tuple #{:from} string?))
(spec/def ::soap/Header
(spec/tuple #{::soap/Header}
::messageId
::from
;; etc
))
(spec/def ::soap/Envelope
(spec/tuple #{::soap/Envelope}
::soap/Header))
(->
(spec/gen ::soap/Envelope)
(gen/generate)
(xml/sexp-as-element)
(xml/emit-str)
(pretty-print-xml-string)
(println))
;;=>
<a:Envelope xmlns:a=" ">
<a:Header>
<messageId>I67NC7</messageId>
<from>733R0T1MGsY9cnx943</from>
</a:Header>
</a:Envelope>@didibus the example I gave above might offer one way to use generators with multi-specs. but as I said, I'm no expert in this stuff so there may be better ways to do it