Fork me on GitHub
#clojure-spec
<
2020-02-20
>
rickmoynihan18:02:29

hmm… I’m wanting to spec some data that is coming from a 3rd party library, the Ataraxy router. It always uses the namespaced keyword :ataraxy/result to contain the parsed parameters for the route; however because ataraxy is configured on a route by route basis; the exact data structure differs, yet the ring request map always contains the same namespaced keyword. It strikes me that this is an unfortunate mistake; in that the same global keyword is being used to name different data. What is the best way to spec such a thing? I’m thinking a multispec is really the only option, that could dispatch on the first argument. :ataraxy/result is a vector of the form [:route/id ,,,route-params,,,]

rickmoynihan18:02:58

or I suppose I could coerce it into something else first 😞

rickmoynihan18:02:05

actually thinking it’s better to just spec the functions after it, that don’t depend on the :ataraxy/result key.

Alex Miller (Clojure team)18:02:04

you could just do something like (s/cat :route-id ::route-id :params (s/* any))

Alex Miller (Clojure team)18:02:26

but it depends a lot what route-params is

Alex Miller (Clojure team)18:02:39

if it's kwargs, then keys* would work great

jasonjckn21:02:29

my data looks like {::type "foobar" ::a ... ::b ... ::c ...} I want different s/keys validation based on the value of ::type whats the feature i'm looking for

jasonjckn21:02:42

it's like ~ conditional spec validation polymorphic to field value

jasonjckn21:02:58

i guess some sort of mulitmethod on the spec

jasonjckn21:02:16

reading about multi-spec now, this looks like it

jasonjckn21:02:23

for the :default case

jasonjckn21:02:29

what would be a canonical way to fail spec validation

jasonjckn21:02:32

defmethod tagmm :default

Alex Miller (Clojure team)21:02:20

(s/keys*) would automatically handle all registered specs for the tail of that

jasonjckn21:02:30

i want default case to fail

Alex Miller (Clojure team)21:02:01

you can definitely use multi-spec for this if that makes sense

jasonjckn21:02:29

what is a canonical way to implement :default

jasonjckn21:02:36

besides a conformer that always throws exceptions

Alex Miller (Clojure team)21:02:45

I don't understand enough what you're doing

Alex Miller (Clojure team)21:02:49

what does :default mean?

jasonjckn22:02:06

(s/def ::tag #{:a :b :c :d})
(s/def ::example-key keyword?)
(s/def ::different-key keyword?)

(defmulti tagmm :tag)
(defmethod tagmm :a [_] (s/keys :req-un [::tag ::example-key]))
(defmethod tagmm :default [_] (s/keys :req-un [::tag ::different-key]))

(s/def ::example (s/multi-spec tagmm :tag))
(gen/sample (s/gen ::example))
here's the example

jasonjckn22:02:13

can I do :default ::s/invalid ?

Alex Miller (Clojure team)22:02:20

oh, you mean multi-method default

jasonjckn22:02:30

(defmethod tagmm :default [_] ::s/invalid) 
will test that out

Alex Miller (Clojure team)22:02:42

the multimethod returns specs so you need to return a spec that always fails

Alex Miller (Clojure team)22:02:19

so probably more (constantly false) would work

jasonjckn22:02:08

that seems more canonical

jasonjckn22:02:14

particular in the spec explanations

jasonjckn22:02:19

it'd be nice to get something that made sense

vemv23:02:32

a q out of curiosity more than anything else: (spec/valid? (spec/coll-of char? :min-count 1 :max-count 20) "abc") doesn't work (and a straightforward impl would underperform anyway?) are there spec-related libraries offering a coll-of-like API that efficiently works on strings? I just like coll-of's API, in that it doesn't resemble regex