Fork me on GitHub
#clojure-spec
<
2016-09-02
>
seancorfield01:09:53

@gfredericks does the maven clojure plugin do the same monkey-patching of clojure.test that lein does? http://build.clojure.org/job/java.jdbc-test-matrix/453/CLOJURE_VERSION=1.9.0-alpha11,jdk=Sun%20JDK%201.6/console <— seems to be the same exception as I got testing locally

seancorfield01:09:11

Not sure how to tell Maven’s plugin not to do the Leiningen naughtiness 🙂

gfredericks01:09:21

com.theoryinpractise.clojure.testrunner is your culprit

gfredericks01:09:47

I don't know about maven clojure, but it smells like something similar

gfredericks01:09:25

this seems a lot harder to work around given the constraints

seancorfield01:09:45

It rebinds the report function

gfredericks01:09:16

do you get to choose an alternate version of the clojure-maven-plugin if you want?

seancorfield01:09:13

I could override it for my project I think, yes (this is for java.jdbc)

seancorfield01:09:31

Is there a version that is compatible with test.check’s clojure_test stuff?

gfredericks01:09:34

alternately, any tactic you can think of that lets you require test.check.clojure-test prior to that line running will fix it

gfredericks01:09:41

no I was just imagining forking it :)

seancorfield01:09:48

Hahaha… ok...

gfredericks01:09:58

so a user.clj could work if you can keep it out of the release jar

seancorfield02:09:17

Would that run with Maven?

gfredericks02:09:25

it runs when clojure boots up

gfredericks02:09:41

user.clj is a pretty reliably way to slip something in before just about anything else happens

seancorfield02:09:33

Pretty sure that doesn’t work with Boot? (more an FYI but…)

seancorfield02:09:52

I don’t remember… but it was discussed in #boot a while back...

gfredericks02:09:18

probably something about their magical space age classloader thing

seancorfield02:09:15

And how do you get it to be loaded for Maven running Clojure?

gfredericks02:09:30

@seancorfield heck for that matter adding a (:require clojure.test.check.clojure-test) to any of your namespaces should also work

gfredericks02:09:03

putting the user.clj on the classpath means that clojure.core reads it at then end of its loading

gfredericks02:09:23

e.g. src/test/resources/user.clj or whatever

seancorfield02:09:50

Ah, yeah, that worked...

talios02:09:27

@seancorfield you can define/give your own test runner script to clojure-maven-plugin if you need to

seancorfield02:09:12

Dynamically requireing that namespace when my test namespace loads seems to do the trick — and I can remove the Leiningen monkey-patch setting as well.

talios02:09:47

<configuration>
  <testScript>src/test/clojure/com/jobsheet/test.clj</testScript>
</configuration>

talios02:09:50

in the pom

seancorfield02:09:19

I have this now in my test ns:

(def with-spec? (try
                  (require 'clojure.java.jdbc.spec)
                  (require 'clojure.spec.test)
                  ;; require this to workaround rebinding of report multi-fn
                  (require 'clojure.test.check.clojure-test)
                  (let [syms ((resolve 'clojure.spec.test/enumerate-namespace) 'clojure.java.jdbc)]
                    ((resolve 'clojure.spec.test/instrument) syms))
                  (println "Instrumenting clojure.java.jdbc with clojure.spec")
                  true
                  (catch Exception _
                    false)))

seancorfield02:09:46

Works with Leiningen and Maven!

talios02:09:16

Sweet - PRs welcome to improve that default test runner script as well.

kurt-yagram15:09:54

I have a spec that looks like this:

(s/def ::value <???>)
(s/def ::type #{"type-date", "type-weirdo", "no-value"})
(s/def ::ent (s/keys :req-un [::type] :opt-un [::value]))
(s/def ::ents (s/coll-of ::ent))
Depending on the type, the value needs to be different, e.g., for type-date, the value should be a date. I know there are multimethods that can, and probably should, be used, but I just can't get it right. What comes at the <???>, so I can have different predicates for value depending on the value of type?

Alex Miller (Clojure team)15:09:27

have you looked at s/multi-spec?

spinningtopsofdoom15:09:30

I ran into this problem two days ago you want to make N :value specs. (e..g.`(s/def :my/value <???>)`, (s/def :other/value <???>), (s/def :one.more/value <???>) and then dispatch those :value's with a multimethod

kurt-yagram15:09:14

yeah, I'm looking at it. but it seems to be somewhat different. So, I could do:

(defmulti ent-type ::type)
(defmethod ent-type "type-date" [_]
   (s/keys :req-un [::value])
(s/def ent-type (s/multi-spec ent-type ::type))
So I day value is required for "type-date". But that still doesn't solve the problem. I must be missing something...

kurt-yagram15:09:49

@spinningtopsofdoom Allright, and I don't need to care about the namespaces? - I mean, the map contains just 'value', no namespaces. I'll give it a few tries.

Alex Miller (Clojure team)15:09:54

instead of using ::value, you could define many :foo1/value :foo2/value :foo3/value specs

Alex Miller (Clojure team)15:09:07

each defmethod would use a different one in :req-un

spinningtopsofdoom15:09:43

@alexmiller is there any movement on having spec/keys take a map of keywords and specs (e.g.)

(spec/def :one-map (spec/keys :req-un {:value <one spec>})
(spec/def :other-map (spec/keys :req-un {:value <other spec>})
So that you don't have to have registered specs for spec/keys

Alex Miller (Clojure team)15:09:07

well, it will never take inline specs

Alex Miller (Clojure team)15:09:16

we might possibly loosen the constraint between key name and spec name, but it’s part of the design that s/keys doesn’t use inline specs and I don’t expect that to change

Alex Miller (Clojure team)15:09:39

the idea is to encourage defining semantics for attributes

spinningtopsofdoom15:09:28

The constraint between key name and spec name is what I would like to loosen. So then my example would be

(spec/def :one-map (spec/keys :req-un {:value ::one-spec})
(spec/def :other-map (spec/keys :req-un {:value ::other-spec})
Correct?

Alex Miller (Clojure team)15:09:11

yeah, something like that has been mentioned, but I do not know whether we’ll end up doing it or not

spinningtopsofdoom15:09:18

Well I'll make a Jira ticket for that if it's not already there, then. Thanks for the clarification.

Alex Miller (Clojure team)15:09:42

I do not know of a jira ticket for this

gerstree15:09:30

Hi everybody, very new to specs, but have already a good part implemented. Now I need a little help on something that is probably very easy, but I'm stuck. I have a def that verifies a url to be a s3 url (s/def ::s3-url #(str/starts-with? % "s3"). Using it to check a single argument works. Now I have a function with 2 arguments (from-url and to-url) of which 1 needs to conform to that spec. Can anyone hint me for the solution.

gerstree15:09:37

This is what I have:

(s/def ::valid-url is-valid-url?)
(s/def ::s3-url #(str/starts-with? % "s3"))

(defn sync
  "sync an s3 folder with a local folder, this works both ways"
  [from-url to-url])

(s/fdef sync
        :args (s/and (s/cat :from-url is-valid-url? :to-url is-valid-url?)
                     ??? this is where I get lost ???))

Alex Miller (Clojure team)15:09:08

(s/fdef sync
        :args (s/cat :from-url is-valid-url? :to-url is-valid-url?))

Alex Miller (Clojure team)15:09:24

I guess I’m also wondering what the difference is between is-valid-url?, ::valid-url and ::s3-url

Alex Miller (Clojure team)15:09:53

given that you have specs, I would actually use the specs in the sync fdef

gerstree15:09:14

Good one, I will have to clean that up

Alex Miller (Clojure team)15:09:24

(s/fdef sync :args (s/cat :from-url ::s3-url :to-url ::s3-url))

gerstree15:09:25

The function will be called either (sync "" "") or (sync "" "")

gerstree15:09:53

What I am trying to spec is that either url is an s3 url

Alex Miller (Clojure team)15:09:55

right so you could have something like:

(s/def ::file-url #(str/starts-with? % “file://“))
(s/def ::s3-url #(str/starts-with? % “s3://“))
(s/def ::aws-url (s/or :file ::file-url :s3 ::s3-url))
… then use ::aws-url in the sync fdef

Alex Miller (Clojure team)15:09:08

or whatever is appropriate

Alex Miller (Clojure team)15:09:33

oh, you want a constraint across the args!

gerstree15:09:48

Yes, is that beyond what spec is meant for?

Alex Miller (Clojure team)15:09:03

You can do it with s/and like you were or you can do that in the :fn spec too

Alex Miller (Clojure team)15:09:45

fn is used for constraints between args and ret or also across args

Alex Miller (Clojure team)15:09:13

Only the args spec is checked in instrumentation though so that might be what you want

gerstree15:09:59

Ah, been reading about it all day and understood the args / ret, but of course that works for across args as well.

Alex Miller (Clojure team)15:09:30

Yeah fn gets the conformed version of both args and ret

gerstree15:09:42

That might be the easiest way to accomplish it.

gerstree16:09:17

Also read about 10 times that stest/instrument only does :args, now I know that is true 😉

gerstree16:09:01

I have the :fn version working. I'm still curious about a solution in the :args part, where I got stuck to begin with. That way, if people use my sync function they can simply (stest/instrument `sync) and work from there.

Alex Miller (Clojure team)16:09:11

(s/fdef :args (s/and (s/cat :from-url ::aws-url :to-url ::aws-url)
                   (fn [{:keys [from-url to-url]}]
                     (or (s3-url? from-url) (s3-url? to-url)))))

Alex Miller (Clojure team)16:09:53

the second function in the s/and receives the conformed version of the first part of the and

Alex Miller (Clojure team)16:09:09

so that will be a map with :from-url and :to-url keys

gerstree16:09:31

Works like a charm and is actually simple enough

gerstree16:09:51

Learned a lot today, thanks for helping me get through that last part!

kurt-yagram17:09:57

is it possible to use `s/multi-spec' on different specs? Something like - but this doesn't work:

(defmulti m [::type ::op])
(defmethod m ["date-type" "do"] [_]
  (s/keys ...))
...
(s/def .... (s/multi-spec m [::type ::op]))

Alex Miller (Clojure team)17:09:11

that defmulti definition looks wrong - it takes a dispatch function not a vector

Alex Miller (Clojure team)18:09:30

(defmulti m #(vector (::type %) (::op %))) maybe?

Alex Miller (Clojure team)18:09:59

or it’s a great opportunity to use juxt :)

Alex Miller (Clojure team)18:09:09

(defmulti m (juxt ::type ::op))

kurt-yagram18:09:53

oh, cool... thx!

Alex Miller (Clojure team)18:09:39

and then the retag value is not valid either - should either be a tag or a fn of generated value and dispatch-tag

lvh22:09:37

huh; are instrumentation exceptions supposed to escape clojure.test assertions?

lvh22:09:06

oh, wait, never mind; only some of them are outside assertions 🙂