Fork me on GitHub
#clojure-spec
<
2018-03-28
>
leontalbot02:03:39

Hello! I am looking for a way to give free pass empty string values when provided as optional

leontalbot02:03:04

(s/def ::item (s/keys :req-un [::id] :opt-un [::url]))

leontalbot02:03:18

I could check for url, but if empty string, since it is in opt-un, let it be valid

leontalbot02:03:55

(but if was in :req-un, then should apply full ::url spec)

leontalbot02:03:03

is there a way to achieve that?

leontalbot02:03:50

It does work for nil but I’d like it to work for “” too

seancorfield03:03:06

@leontalbot I think what I'd do there is use :opt/url as the :opt-un key and define it as a spec like

(s/def :opt/url (s/or :url ::url :empty empty?))
which will allow it to be the full ::url string spec or nil or ""

seancorfield03:03:00

(or however you want empty values to be spec'd... maybe #{nil ""})

leontalbot03:03:13

cool! Thanks @seancorfield. Is this good too to you:

leontalbot03:03:22

(defn valid-map?
  “dissoc keys with empty vals to let optional keys pass, then validate...”
  [spec m]
  (s/valid? spec (apply dissoc m (for [[k v] m :when (#{“” nil} v)] k))))

leontalbot03:03:25

(valid-map? ::my-spec-map my-map)

leontalbot03:03:25

probably less general

seancorfield03:03:57

@leontalbot I wouldn't use that approach because now you have a custom function, not just a spec. You can't conform or explain with that approach.

seancorfield03:03:28

It also wouldn't work for specs that have keys whose values can be nil or "" but are still required.

seancorfield03:03:16

FWIW, we have exactly this situation at work and the approach I suggested is basically what we do.

seancorfield03:03:09

Spec is pretty flexible and powerful -- and the ability to use multiple qualified keywords for "similar" unqualified keys specs lets you deal with a lot of context.

seancorfield03:03:17

Another aspect to consider is having different versions of specs at different "layers" in your application. For example, we have API-level specs (which deal with strings mostly) and we have domain-level specs (and we have a few DB-level specs as well).

leontalbot03:03:59

and one last question:

leontalbot03:03:38

why :opt/url and not :url/opt?

leontalbot03:03:01

Yeah I guess this is to map this hierarchy:

leontalbot03:03:11

(s/keys :req-un [::id] :opt-un [::url]))

seancorfield04:03:46

Yeah, if the unqualified key is :url then the options are :<something>/url

👍 4
leontalbot13:03:36

Wanted to get end-user error message from spec. Useful for webform field validation.

leontalbot13:03:48

Found Phrase library.

leontalbot13:03:34

Seemed a bit overkill though. As I just wanted is attach an error text to a spec.

leontalbot13:03:31

(s/explain (st/spec pos-int? {:reason “positive”}) -1)
; val: -1 fails predicate: pos-int?,  positive

(s/explain-data (st/spec pos-int? {:reason “positive”}) -1)
; #:clojure.spec.alpha{:problems [{:path [], :pred pos-int?, :val -1, :via [], :in [], :reason “positive”}]}

leontalbot13:03:06

Maybe use the :reason field accessible with explain?

leontalbot13:03:34

Wanted to know what you would do for form validation, thanks!

guy13:03:40

theres https://github.com/bhb/expound as well which might be helpful

leontalbot14:03:51

Yes looked at this. Not sure if we can “extract” only the error string

👍 4
bbrinck14:03:39

@leontalbot Would this work?

(expound/def :user/name string? "should be a valid name")

(defn msg [spec val]
  (if (s/valid? spec val)
    nil
    (expound/error-message spec)))

(msg :user/name "John") ; => nil
(msg :user/name :John) ;  => "should be a valid name"

leontalbot14:03:29

Hey! Thanks for answering @bbrinck! Fantastic! Thanks!

bbrinck14:03:52

np, let me know if you have any other questions about expound

leontalbot14:03:10

Ok! Nice lib btw!

bbrinck14:03:18

thanks! 🙂

borkdude17:03:08

@bbrinck Sometimes I get an error message from expound when it complains about not being able to render an error (exact error message I don’t have handy). Wouldn’t it be better in that case to print the vanilla spec error instead of only the expound error?

bbrinck17:03:46

@borkdude Is this a bug in expound? Or a case where your spec has conformers, perhaps?

bbrinck17:03:37

Gotcha. Yeah, in that case, it might very well make sense to just print the default error. Let me think about that. Thanks for the idea!

bbrinck17:03:24

Although if you’re using conformers, the vanilla spec error may not be very helpful either 🙂

bbrinck17:03:32

But still better than nothing, for sure

borkdude17:03:03

Usually when I run into this, I turn off expound, re-run the code, inspect the error, fix it, and enable expound again.

bbrinck17:03:25

Agreed, that’s a pain. I’ll fix it.

seancorfield18:03:33

@bbrinck FWIW, that was why we tried and then stopped using Expound -- we have several conformers in our specs.

bbrinck18:03:50

@seancorfield Would it work in your case to just fallback to s/explain?

bbrinck18:03:13

Depending on how many conformers you have, I guess at some point you would rarely see an expound error

bbrinck18:03:01

So the fix @borkdude suggested is a good idea, but it’s mostly useful for projects that have a relatively small number of conformers compared to the total usage of spec

bbrinck18:03:20

@seancorfield FWIW, apparently pinpointer works with conformed values

seancorfield18:03:28

@bbrinck Well, the main cases where we wanted Expound's better messages were all conformer-specs, so we just fell back to explain ourselves 🙂

seancorfield18:03:51

I'll take a look at pinpointer -- I hadn't heard of that.

👍 4
lilactown20:03:26

has anyone used spec-tools to generate swagger objects?

lilactown20:03:45

I'm having trouble with getting the response schema's to generate correctly