Fork me on GitHub
#clojure-spec
<
2017-08-08
>
didibus02:08:49

Can I spec a map in a way where I want it to have at-least one of two keys. So a valid map would be: {:x :y} {:x} or {:y}, but {} would not be valid?

souenzzo02:08:23

@didibus

(s/valid?
  (s/or :a (s/keys :req [:foo/bar]
                   :opt [:bar/foo])
        :b (s/keys :req [:bar/foo]
                   :opt [:foo/bar]))
  {}
  )

didibus02:08:35

Ah yes, that should work!

joshjones03:08:42

@didibus @souenzzo it's even more concise:

(s/valid?
  (s/keys :req-un [(or ::x ::y)])
  {:y 42 :x 33})

souenzzo13:08:09

Why does it do not generate`{:x 33}`?

(gen/sample (s/gen
                (s/keys :req-un [(or ::x ::y)])))
=>
({:x -1, :y 0}
 {:x 0, :y -1}
 {:x 0, :y -1}
 {:x 0, :y 1}
 {:x 0, :y -1}
 {:x -2, :y 0}
 {:x 6, :y -21}
 {:x 6, :y 0}
 {:x 4, :y -23}
 {:x -8, :y 117})

mbarbieri14:08:08

I'm playing for the first time with spec. I want to generate a spec from a contract map and test if another map is conforming. As first step I would like that all keys in the map are present in the contract map. But i read the contract from a json file, converting to unqualified keywords, so

(s/keys :opt-un (keys contract-map))
gives me Assert failed: all keys must be namespace-qualified keywords Do I have to convert them to qualified? Or is it a better way to handle it?

misha15:08:43

@mbarbieri :opt-un expects coll of qualified keys (which might point to their specs), -un part indicates that data will have no namespaces in keys.

mbarbieri15:08:22

@misha ok data will have no namespaces, but not even the keys I'm passing in the spec will have, hence the failure. If I'm not wrong, that exception happen because my code will evaluate in something like:

mbarbieri15:08:41

(s/keys :opt-un [:a :b :c])

misha15:08:03

it is ok for data to have no namespaces, but in spec declaration you need to supply namespaces, so later you can assign actual specs to those qualified keys, and s/keys will check not only keys presence, but will check values against those specs as well

misha15:08:37

you need to supply qualified keys, yes. how exactly - depends on your use case. If you are want to generate spec once and put in the source file – just come up with a namespace and list those manually. If you need to generate that spec anew dynamically every time – that'd seem odd to me

misha15:08:03

might be useful in any case

stathissideris15:08:33

I’m here in case there are any questions 🙂

misha15:08:51

I have one! not lib related tho :)

(s/explain-data
  (s/map-of qualified-keyword? fn? :min-count 0)
  {:foo/bar 9})
;;=>
#:clojure.spec.alpha{:problems ({:path [1],
                                 :pred fn?,
                                 :val 9,
                                 :via [],
                                 :in [:foo/bar 1]})}

(get-in {:foo/bar 9} [:foo/bar 1]) 
;;=> nil
(replaced string with number to illustrate my confusion with 1 as path inside 9(?))

misha15:08:22

what 1 in :in [:foo/bar 1] is supposed to point to?

misha15:08:51

how do I actually use this :in path? Which data structure do I apply it to?

misha15:08:33

from 
:in - the key path through a nested data val to the failing value. In this example, the top-level value is the one that is failing so this is essentially an empty path and is omitted.

misha15:08:28

(s/def :foo/bar (s/tuple int? int? fn?))
(s/explain-data
  (s/map-of qualified-keyword? :foo/bar :min-count 0)
  {:foo/bar [1 2 3]})
gives :in [:foo/bar 1 2], where 2 makes sense as index of val 3, but 1 still does not, at least while I am thinking in terms of (get-in data in)

souenzzo17:08:20

I'm about 5 minutes waiting this repl command

(-> (test/check `spec-utils/atributos-do-pattern)
        (test/summarize-results))
There is some way to limit the number of tests that test/check do?

spinningtopsofdoom17:08:50

I think

(-> (test/check 10`spec-utils/atributos-do-pattern)
        (test/summarize-results))

souenzzo17:08:52

IllegalArgumentException Don't know how to create ISeq from: java.lang.Long clojure.lang.RT.seqFrom (RT.java:542)

souenzzo17:08:53

Another question: how to define zero args? (s/fdef foo :args (s/cat)) works, but not sure if it's right...

spinningtopsofdoom18:08:37

Oops my mistake I was thinking of the test.check library

(-> (test/check spec-utils/atributos-do-pattern {:num-tests 10})
        (test/summarize-results))
see this link for details (look for ::stc/opts) https://clojure.github.io/clojure/branch-master/clojure.spec-api.html#clojure.spec.test/check

spinningtopsofdoom18:08:37

I think (s/fdef foo :args (s/cat)) is correct. s/cat constructs a sequence on 0 - N specs (like it's regex namesake) so no specs given to it should mean zero arguments expected

kenny18:08:13

Is there a best practice you guys are following regarding enabling of instrumentation? I am finding that many times if I don't enable or forget to enable it, my functions will silently fail and I receive an obtuse error message. Normally I would have an assertion at the beginning of the function to produce a clear error message but I figured spec instrumentation would replace this.

souenzzo13:08:52

Checkout (clojure.spec.test.alpha/instrumentable-syms) and (stest/instrument)

souenzzo20:08:37

What is the current status of clojure.spec.specs.alpha? (just curious/studding)

mrkaspa22:08:44

is there a library to convert clojure.spec/explain-data to something friendly to respond in a json api? I was using the struct library I want something similar

Oliver George22:08:55

Has anyone written a spec function coverage tool? e.g. report the percentage of functions with a spec defined by namespace or project.

souenzzo13:08:08

It's easy to write... Checkout clojure.spec.test.alpha/instrumentable-syms and clojure.repl/dir-fn

Oliver George23:08:53

Thank you I'll try those

Oliver George01:08:12

Thanks for the help. I'm writing clojurescript which isn't quite as self aware but I had success on a few fronts.

Oliver George01:08:27

Simple case is checking a namespace.

(defn var->sym [var]
  (let [{:keys [ns name]} (meta var)]
    (symbol ns name)))

(defn interns->fn-syms [interns]
  (->> (vals interns)
       (filter (comp fn? deref))
       (map var->sym)))

(deftest sims-logic-coverage
  (let [instrumentable-syms (stest/instrumentable-syms)
        instrumentable? #(contains? instrumentable-syms %)
        fn-syms (interns->fn-syms (ns-interns 'sims.logic))]
    (doseq [fn-sym fn-syms]
      (is (instrumentable? fn-sym) "No s/fdef for function"))))

(test/run-tests)

Oliver George01:08:03

The "total coverage" option was possible but messy. I had to use cljs.analyzer.api to know what namespace and interns existed.