clojure-spec

2022-04-07T03:27:34.264919Z

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.

Alex Miller (Clojure team) 2022-04-07T04:08:51.460819Z

have you looked at multi-spec?

Alex Miller (Clojure team) 2022-04-07T04:10:24.985349Z

seems like it would be a great match here

2022-04-07T05:31:51.360319Z

Multi-spec is actually where I have this problem, because the generator doesn't generate the correct :type

2022-04-07T05:34:29.402239Z

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

flowthing 2022-04-07T07:45:15.885369Z

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?

lassemaatta 2022-04-07T08:47:16.243529Z

It's been a while since I've used spec so this might be horribly wrong, but it might also solve both your problems ๐Ÿ˜ƒ ๐Ÿงต

lassemaatta 2022-04-07T08:47:33.029269Z

lassemaatta 2022-04-07T08:48:38.438859Z

flowthing 2022-04-07T08:51:31.666609Z

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. ๐Ÿ‘

lassemaatta 2022-04-07T08:51:33.480979Z

I tested this for at least two seconds, so caveat emptor ๐Ÿ™‚

flowthing 2022-04-07T08:51:42.990649Z

Sure thing, no worries. ๐Ÿ™‚

flowthing 2022-04-07T09:19:47.123179Z

Actually, it's probably easier to write a spec for the Hiccup syntax using tuples and/or regexp ops.

flowthing 2022-04-07T09:21:01.496129Z

(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>

๐Ÿ‘ 1
๐Ÿงผ 1
lassemaatta 2022-04-07T15:00:56.967539Z

@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