I'm having some trouble with adding a spec for a keyword which is supposed to represent a function. I tried this
(s/fdef ::on-change
:args (s/cat :value :option/id))
(s/def ::props (s/keys :req-un [::options
::value]
:opt-un [::class
::centered?
::on-change
::variant]))
(s/fdef tabs
:args (s/cat :props ::props))
> Var clojure.test.check.properties/for-all* does not exist, clojure.test.check.properties never required
from trying to pass ::on-change
to fdef
After reading the API docs, it seems fdef
can only take a symbol naming a function.
My question is... how can I create a spec for the function that is passed as :on-change
?
It's funny you ask, I was just arriving to ask something similar. I have a spec describing a map, of which some keys are functions. I'd love to be able to specify that those functions should conform to a given fdef, but not sure how to accomplish that. As is, I just have the keys referring to the functions specced using fn?
Now I don't feel too silly asking 🙂
use ifn?
not fn?
you can use fspec
to do this as well, but there are some big caveats for generation
personally, I have not found it to be worth doing fspec
over ifn?
@alexmiller Does ifn?
allow you to specify the shape of the arguments that are passed in? I can't seem to find any examples doing so
no
you can do that with fspec
(same args as fdef
)
Tried a simple fspec
but that doesn't seem to compile. Hmm
(s/fspec ::on-change
:args (s/cat :value number?)
:ret any?)
(s/def ::on-change
(s/fspec :args (s/cat :value number?)
:ret any?))
I get this error when I try that. Is this expected? > Uncaught Error: Var clojure.test.check.properties/for-all* does not exist, clojure.test.check.properties never required
fspec uses test.check to ensure the correctness of the spec'ed input.
Thank you!
Remove the first arg:
(s/fspec :args (s/cat :value number?)
:ret any?)
Thanks! How do I associate it to the keyword which will use the spec?
how can I spec transient things like values of let bindings?
I see the suggestion to use s/assert in docs, but isn't that also something that I should only do in development and not make it part of the code that might be shipped eventually?
Is it possible to spec/validate java collections? this naive example doesn't work
(s/def ::foo int?)
(s/def ::bar string?)
(s/def ::m (s/keys :req [::foo ::bar]))
user=> (s/valid? ::m {::foo 1 ::bar "2"})
true
user=> (s/valid? ::m (java.util.HashMap. {::foo 1 ::bar "2"}))
false
It might be terrible but I recently had a similar issue, went for this solution:
(let [x (expr ...)]
(eval
`(s/def ::my-spec
(s/keys
:req-un
[~x]))))
😄
i am not sure about terrible but it's scary
it is. If you can convince yourself the expr doesn't do any IO then you can sleep well imo
In my case I had to create a qualified keyword who's name starts with a number. seems like :1foo
is a valid kw but :user/1foo
can't be read by the reader. (keyword "user" "1foo")
works
this is supposed to run in a browser 🙂
ah
Spec does not cover Java collections so you’d have to pour one into a Clojure collection first
Spec asserts can be turned off and even compiled out in prod code
thanks, I realized there has to be some solution because there were blogposts about how to use it, but now I know where to look for it.
What spec could validate that a map contains at least one true value?
any function that takes a value can be a spec
so rephrasing - what function could validate that a map contains at least one true value?
write that function and you're done
Ah, I was overthinking this - thanks
This is probably a terrible idea, but I guess everything is possible if you try hard enough
It would be nice, maybe in spec2, if the meanings of maps and sequences were relaxed a bit. But I'm coming at it from an esoteric use case
that seems way harder than converting the HashMap into a Clojure map
we do not have plans to support Java colls in spec 2
(s/valid? ::m (into {} (java.util.HashMap. {::foo 1 ::bar "2"})))
That's the trivial case, in reality I might be dealing with an arbitrarily nested java collection 😞
It's also unfortunate because it creates a bit of a mismatch between the validation mechanism and the applied predicates. In this example, get
works on java.util.Map
, so does seq, so conform*
can do its work. The only "hurdle" is map?
, which is a pretty stringent requirement. Why not expand spec a bit more towards reflecting intent and less implementation?
the intent is to validate clojure data
validating java colls was out of scope for us