This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-02-15
Channels
- # aws (1)
- # beginners (6)
- # boot (25)
- # cider (30)
- # cljs-dev (50)
- # cljsrn (45)
- # clojure (98)
- # clojure-austin (5)
- # clojure-czech (1)
- # clojure-dev (21)
- # clojure-dusseldorf (29)
- # clojure-germany (2)
- # clojure-greece (117)
- # clojure-italy (2)
- # clojure-nl (4)
- # clojure-russia (26)
- # clojure-serbia (10)
- # clojure-spec (123)
- # clojure-turkiye (1)
- # clojure-uk (27)
- # clojured (13)
- # clojurescript (57)
- # core-async (18)
- # cursive (13)
- # datomic (20)
- # defnpodcast (16)
- # emacs (8)
- # events (2)
- # figwheel (3)
- # instaparse (1)
- # jobs (3)
- # jobs-discuss (39)
- # klipse (9)
- # lumo (100)
- # mount (1)
- # numerical-computing (1)
- # off-topic (22)
- # om (34)
- # onyx (17)
- # pedestal (1)
- # perun (29)
- # re-frame (60)
- # reagent (16)
- # remote-jobs (8)
- # rethinkdb (6)
- # ring-swagger (19)
- # rum (1)
- # slack-help (1)
- # specter (3)
- # untangled (1)
- # yada (17)
What’s the best practice for validating data submitted to a server with spec (for an API)? Should I use just check with s/valid?
and use code to return an error? Or maybe use an assert and throw an exception?
Your first suggestion sounds sensible to me.
(In general people try to avoid relying on exceptions in pure functional code since it breaks the input->output contract. )
Right. That makes sense. Thanks.
I need to have either bool
or string
, why this generates single value wrapped in a vector? (gen/generate (s/gen (s/alt :s string? :b boolean?)))
Howdy. Assuming I'm writing a spec looking like this:
(s/def ::labels (s/coll-of string?))
(s/def ::values (s/coll-of integer?))
(s/def ::chart (s/keys :req-un [::labels ::values]))
how would I go about validating that both :labels
and :values
are of equal length in :chart
?@smogg You could do it like this (s/def ::chart (s/and (s/keys :req-un [::labels ::values]) #(= (count (::labels %)) (count (::values %)))))
thank you @dergutemoritz
So today I am trying to generate a list.
(s/def ::team (list? (s/+ ::team-member)))
I have verified that ::team-members can be generated. What obvious thing am I missing this morning?
@cryptorat yeah, if you want a coll of something you'd use coll-of
if you want to specify what collection you want to get with s/coll-of
you'd pass a :kind
Interesting semantic differences between s/merge
and clojure core merge:
(s/def :foo/bar string?)
(s/def :baz/bar nat-int?)
(s/def ::bar-map (s/merge (s/keys :req-un [:foo/bar])
(s/keys :req-un [:baz/bar])))
I would expect :baz/bar
to override :foo/bar
, but AFAICT it and
s the specs together for that key.s/merge
behaves like s/and
The term merge might be misleading
merge
macro
Usage: (merge & pred-forms)
Takes map-validating specs (e.g. 'keys' specs) and
returns a spec that returns a conformed map satisfying all of the
specs. Unlike 'and', merge can generate maps satisfying the
union of the predicates.
It means: union of required keys, union of optional keys
And I guess that there is some rule for handling cases where a key appears in one spec as required and in the other as optional
Could you test it?
You can create an interactive snippet with klipse e.g : http://app.klipse.tech/?cljs_in=(require%20%27%5Bclojure.spec%20%3Aas%20s%5D)%0A%0A(s%2Fdef%20%3Afoo%2Fbar%20string%3F)%0A(s%2Fdef%20%3Abaz%2Fbar%20nat-int%3F)%0A%0A(s%2Fdef%20%3A%3Abar-map%20(s%2Fmerge%20(s%2Fkeys%20%3Areq-un%20%5B%3Afoo%2Fbar%5D)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20(s%2Fkeys%20%3Areq-un%20%5B%3Abaz%2Fbar%5D)))%0A%0A(s%2Fvalid%3F%20%3A%3Abar-map%20%7B%3Abaz%2Fbar%201%7D)
(Sorry for the long url)
(s/def :foo/bar #(< 1 % 10))
(s/def :baz/bar #(< 5 % 20))
(s/def ::bar-map (s/merge (s/keys :req-un [:foo/bar])
(s/keys :req-un [:baz/bar])))
(s/valid? ::bar-map {:bar 2})
=> false
(s/valid? ::bar-map {:bar 11})
=> false
(s/valid? ::bar-map {:bar 7})
=> true
(require '[clojure.spec :as s])
(s/def :quux/flib int?)
(s/def ::bar-map (s/merge (s/keys :req-un [:quux/flib])
(s/keys :opt-un [:quux/flib])))
(s/valid? ::bar-map {}) ;; false
No matter what’s the order inside the merge?
Order doesn’t matter. I guess this behaviour is consistent with the and
ing of the value specs.
makes sense
Merge means: the value must pass each merged spec
The specs are not combined or unions though
What I was hoping for was someway to relax an existing spec that I’ve defined. For example, a new map that has blank strings initially ready for a user to fill out.
@vikeri not built in i don’t think, but you could just have a type checking predicate i suppose
@spieden Alright thanks, in the end I decided to use a vector instead, better when serializing
@vikeri you can do something like (s/map-of int? string? :kind sorted?)
@alexmiller That sure looks like it, didn’t think of that you could use :kind
for broader things than vector?
, list?
etc.
it’s an arbitrary function
however, it does affect gen - if you use, either it should gen or you should also use :into
I’m not sure there is some way to get the sorted map constraint in there and make map-of gen (without supplying a custom gen for the overall coll)
hum, trying to figure out this:
(s/exercise (s/keys :req #{:lol/id} :opt #{:lol/name}))
ExceptionInfo Couldn't satisfy such-that predicate after 100 tries. clojure.core/ex-info (core.clj:4725)
somewhere in the specs for id and name or in a spec they rely on you are using s/and
the way generates work for s/and is it uses the generator for the first predicate and then filters the output using such-that
that is going in to the weeds a little bit on what happens as you compose test.check generators
first thought that the problem was strings limited by length, but it’s probably combination of :req and :opt, since having both key specs as :opt passes s/exercise
because you can generate random strings for a long time before you get one that is less than or equal to column_size
since this seems to work:
(s/exercise (s/keys :req #{} :opt #{:lol/name :lol/id}))
([{} {}] [{} {}] [{} {}] ...)
I don't recall the details of how the generator for s/keys works, but it may ignore :opt most of the time, or some large enough amount of the time for it to mostly work when you move the keys to opt
any thoughts on infinite recursion during spec generated gen? only difference i can see is the “call depth” where the recursion happens — it’s deeper in the case that’s going into the infinite loop
@spieden you have to always give the generator a "way out"
so one of the s/or or s/keys needs to have a nil, or fully optional params, etc.
there is, but the generator needs something to bottom out at, if you say "go 2 levels deep", what's it going to gen at the bottom level? Has to be a empty map, or a nil, or something.
or basically a scalar value.
any hint from the stacktrace where the problem is?
there’s no stack trace, it just spins endlessly and eats CPU when i try to sample one from it
here’s the code in case you feel like taking a look: https://gist.github.com/spieden/49b6df8c2dae9f8e659edac3b974bb94
::sgr/parallel -> ::sgr/branches -> ::sgr/states -> ::sgr/state -> ::sgr/parallel is the only cycle i can find
yeah, I don't think that's the problem
if you have a loop like taht you'd get a stack overflow
oh here’s the other spec ns https://gist.github.com/spieden/58f0fac97f725522860f2b68e25f3769
the problem here is that spec creates the generators very eagerly.
and so I've seen really long times to create generators.
one thing worth trying is to add :gen-max 3
to all of your coll-of generators
that’s frequently a problem for me in composite gens as the default is 20 and it doesn’t take much nesting for that to be big
thanks @alexmiller @tbaldridge !
I have a pending enhancement to change the default to 3 :)
I'm wondering if it's possible to statically analyze, for each non-trminal, the smallest structure it can generate -- then, after gen hits a certain size, it tells everything to use it's smallest choice
thus, instead of controlling depth, gen merrily does its thing until it hits say 1000 nodes, then it goes "oh -%&#$&" and it tries to finish as quickly as possible
hey guys, I forgot how do you select random multiple elements out of a vector? something like gen/elements
but for a random number elements instead of just one
❓ what a spec would look like for a map where values depend on each other? e.g. date range with keys :from
and :to
where :to
always should be bigger that :from
?
@ag don't use sample to build new generators
(gen/vector (gen/elements ...)) should do what you were asking