Fork me on GitHub
#beginners
<
2017-01-07
>
dhruv104:01:31

hey, I am running into a bit of a problem here while using clojure.spec.

dhruv104:01:52

here is my code:

(defrecord Car [name type num-of-wheels make])

(def car-types [:sedan :suv :truck :big-truck])

(s/def ::name string?)
(s/def ::type (s/spec keyword? :gen #(gen/elements car-types)))
(s/def ::num-of-wheels (s/and int? (s/int-in 4 18)))
(s/def ::make string?)

(defn namespaced-kwd-map->kwd-map
  [map]
  (reduce-kv (fn [acc k v]
               (assoc acc (keyword (name k)) v))
             {} map))

(defn car-gen
  []
  (gen/bind
   (s/gen (s/spec (s/keys :req [::name ::type ::num-of-wheels ::make])))
   #(gen/return (map->Car (namespaced-kwd-map->kwd-map %)))))

(s/def ::car (s/spec record? :gen car-gen))

(gen/generate (car-gen))

(s/exercise ::car 6) 

dhruv104:01:05

this works (map second (s/exercise ::car 6)) =>

(#exercises.spec_validation.Car{:name "", :type :big-truck, :num-of-wheels 8, :make ""} #exercises.spec_validation.Car{:name "", :type :suv, :num-of-wheels 10, :make "o"} #exercises.spec_validation.Car{:name "E9", :type :suv, :num-of-wheels 12, :make ""} #exercises.spec_validation.Car{:name "W", :type :sedan, :num-of-wheels 4, :make "yfo"} #exercises.spec_validation.Car{:name "", :type :truck, :num-of-wheels 5, :make "3l6a"} #exercises.spec_validation.Car{:name "Q5Og", :type :truck, :num-of-wheels 13, :make "w6F4r”}) 
(drop 198 (s/exercise ::car 200) gives me this error: ExceptionInfo Couldn't satisfy such-that predicate after 100 tries. clojure.core/ex-info (core.clj:4725)

dhruv104:01:23

how can i debug this problem?

dhruv104:01:39

also i just started playing around with clojure spec today so the code might not be the best. any advice on making it better would be awesome! thanks.

seancorfield05:01:14

@dhruv1: change num-wheels to just int-in -- the problem is that it can randomly generate int? values that aren't in 4..17

seancorfield05:01:10

The int-in spec will do what you want without int?

roelof08:01:22

someone who can help me figure out why if I have tests in my code , proto-repl will not be at a point I can work with it.

roelof11:01:00

if I have this spec : (s/def ::principalMakers (s/coll-of (s/keys :req-un [::name])))

roelof11:01:34

is it then possible that a generator generates this principalMakers [] ?

roelof11:01:13

I ask this because this fail message of a test :

{:spec
 (fspec
  :args
  (cat :artObject :paintings2.api-get/artObject)
  :ret
  :basic/output
  :fn
  nil),
 :sym paintings2.api-get/get-data-front-page,
 :failure
 {:clojure.spec/problems
  ({:path [:ret :name],
    :pred string?,
    :val nil,
    :via [:paintings2.api-get/name],
    :in [:name]}),
  :clojure.spec.test/args
  ({:objectNumber "AA-A-0",
    :principalMakers [],
    :title "",
    :description "",
    :dating {:year 1900},
    :objectCollection "",
    :colors []}),
  :clojure.spec.test/val {:id "AA-A-0", :name nil, :title ""},
  :clojure.spec/failure :check-failed}} 

dhruv115:01:55

@seancorfield ah i see. thank you!

roelof16:01:33

last question : can I do something like this in spec : :ret (s/or int? exception)

roelof16:01:01

so the return value is a integer or a exception trown from the code

roelof17:01:35

The function looks like this :

(defn get-data-front-page
  "Reads the title, description, date , collection, colors and url of a image"
  [art-object]
  (let [name (-> art-object
                 :principalMakers
                 first
                 :name)
        id (:objectNumber art-object)
        title (:title art-object)
        checked (s/conform ::artObject art-object)]
    (if (s/invalid? checked)
      (throw (Exception. "Something wrong with the front-apage"))
      {:id id :name name :title title}))) 

roelof17:01:46

so the function returns a exception with the text "Something wrong with the front-page" or the object {:id id :name name :title title}))) which has the name :basic/output

roelof17:01:26

I have to tell the fdef in :ret this otherwise my test are still failing

Alex Miller (Clojure team)18:01:59

Specs only talk about non-exceptional outcomes

roelof18:01:35

hmm, then I have to look well why this one is failing

roelof18:01:04

when I do this : (stest/summarize-results (stest/check 'paintings2.api-get/get-data-front-page))

roelof18:01:27

I see this spec message :

{:spec
 (fspec
  :args
  (cat :artObject :paintings2.api-get/artObject)
  :ret
  :basic/output
  :fn
  nil),
 :sym paintings2.api-get/get-data-front-page,
 :failure
 {:clojure.spec/problems
  ({:path [:ret :name],
    :pred string?,
    :val nil,
    :via [:paintings2.api-get/name],
    :in [:name]}),
  :clojure.spec.test/args
  ({:objectNumber "AA-A-0",
    :principalMakers [],
    :title "",
    :description "",
    :dating {:year 1900},
    :objectCollection "",
    :colors []}),
  :clojure.spec.test/val {:id "AA-A-0", :name nil, :title ""},
  :clojure.spec/failure :check-failed}}  

roelof18:01:10

so I assumed that because :name nill the principalMakers are empty so there is no name

roelof18:01:12

if that is so, the generated spec is not valid so it must show the exception

roelof18:01:56

Can you give any hint why the spec is really failing then

roelof18:01:46

@alexmiller here are all the specs :

(s/def ::id (s/and string? #(re-matches #"[A-Z]{2}-[A-Z]-\d{1,4}" %)))
(s/def ::objectNumber (s/and string? #(re-matches #"[A-Z]{2}-[A-Z]-\d{1,4}" %)))


(s/def ::name string?)
(s/def ::title string?)
(s/def ::description string?)
(s/def ::year (s/int-in 1900 2018))
(s/def ::objectCollection string?)
(s/def ::colors (s/coll-of string?))

(s/def :basic/output
 (s/keys :req-un [::id ::name ::title]))


(s/def ::dating  (s/keys :req-un [::year]))
(s/def ::principalMakers (s/coll-of (s/keys :req-un [::name])))
(s/def :basic/artObject
  (s/keys :req-un [::objectNumber ::principalMakers ::title]))
(s/def :detail/artObject
  (s/merge :basic/artObject (s/keys :req-un [::description ::dating ::objectCollection ::colors])))
(s/def ::artObject
  (s/or :basic :basic/artObject :detail :detail/artObject))
(s/def :artObject/body
 (s/keys :req-un [::artObject]))
(s/def :artObject/response
 (s/keys :req-un [:artObject/body]))

; spec to test some functions

(s/fdef get-objectNumbers
 :args (s/cat :response :artObject/response)
 :ret (s/coll-of ::objectNumber))

(s/fdef get-art-object
 :args (s/cat :response :artObject/response)
 :ret  ::artObject)

(s/fdef get-data-front-page
 :args (s/cat :artObject ::artObject)
 :ret :basic/output)

(s/fdef get-data-detail-page
 :args (:artObject :artObject)
 :ret :detail/artObject)

; some custom generators
(s/def ::id
 (s/with-gen ::id
  (fn [] (gen'/string-from-regex #"([A-Z]{2}-[A-Z]-\d{1,4})"))))

(s/def ::objectNumber
 (s/with-gen ::objectNumber
  (fn [] (gen'/string-from-regex #"([A-Z]{2}-[A-Z]-\d{1,4})")))) 

Alex Miller (Clojure team)18:01:57

Actually it say your name is nil but the spec is string?

roelof18:01:22

on a valid input this schould be the output :

{:name "Rembrandt Harmensz. van Rijn", 
 :title "Schutters van wijk II onder leiding van kapitein Frans Banninck Cocq, bekend als de ‘Nachtwacht’", 
 :id "SK-C-5"}  

roelof18:01:35

yep, but I have no clue why

roelof18:01:06

on a real input the name is a string , see my example I posted with a valid input

Alex Miller (Clojure team)18:01:37

Because :principalMakers is []

roelof18:01:52

and when I exercise :name I see this :

(["" ""] 
 ["5" "5"] 
 ["v9" "v9"] 
 ["b" "b"] 
 ["t1W" "t1W"] 
 ["2" "2"] 
 ["" ""] 
 ["jBVPdA" "jBVPdA"] 
 ["h27h5" "h27h5"] 
 ["" ""])  

roelof18:01:59

yep. but is it not true that this spec : (s/def ::principalMakers (s/coll-of (s/keys :req-un [::name]))) is taken care that principalMakers cannot be a empty vector ?

Alex Miller (Clojure team)18:01:33

But you can use :min-count 1

Alex Miller (Clojure team)18:01:55

In coll-of to make your spec more precise

roelof18:01:33

oke, so with that line principalMakers can be empty as I understand you

roelof18:01:51

and I can use :min-count to solve that

roelof18:01:10

Thanks, learned another thing about specs

Alex Miller (Clojure team)18:01:27

There are other options to coll-of as well

roelof18:01:35

now I have to find out where I have to put that :min-count part

roelof18:01:51

I have seen only : kind

roelof18:01:24

This is right : (s/def ::principalMakers (s/coll-of (s/keys :req-un [::name] :min-count 1)))

roelof18:01:43

I used that guide all the time

Alex Miller (Clojure team)18:01:53

You want it in s/coll-of

Alex Miller (Clojure team)18:01:06

So one paren to the right

roelof18:01:19

Thanks, the test is now schowing the code is working all-right

roelof18:01:30

{:check-passed 1, :total 1}

roelof19:01:08

second test is also working

roelof19:01:47

again thanks and have a good weekend

sb21:01:59

Hello, How possible to create with Friend authentication a solution which if user’s status is logged in, and s/he is on the home page (which is a public page), then I would like to show up an extra element (for logged in users)?

sb21:01:42

Is that possible with Friend authentication?