Fork me on GitHub
#beginners
<
2016-12-12
>
roelofw06:12:15

I will do today

roelofw07:12:27

@agile_geek got the test working like this :

testing "get data "
    (is (=  {:id 1 :name "Roelof" :description "rubbish" :date "12-02-1980" :collectie nil :colors "yellow"}  (api/read-data-painting  {:body {:artObject {:objectNumber 1 :principalMakers [{:name "Roelof"}] :description "rubbish" :dating { :year "12-02-1980"} :collectie nil :colors "yellow"}}} )  )))

roelofw07:12:37

Does this look good ?

roelofw07:12:17

it was a struggle but success is a good feeling

agile_geek07:12:17

That's a good example of a test in Clojure and the process we went thru shows how you can refactor to make things easier and separate concerns.

agile_geek07:12:48

Using the repl to poke at the data returned from API is something we do in Clojure all the time

agile_geek07:12:39

We refactored to separate the api call (which has a side effect - an http get) from the pure function that destructures the data

agile_geek07:12:56

making the destructure fn more testable.

roelofw07:12:27

yep and I can do the same for this function so I can test it :

(defn read-image-url
  "Reads the image-url"
  [id]
  (let [art-objects (-> (str "" id "/tiles")
                        (client/get {:as :json :query-params {:format "json"  :key (env :key)}})
                        :body
                        :levels
                        )

        url (filter #(= (:name %) "z4") art-objects)
        tiles (:tiles (first url))
        image (get-in tiles [0 :url] )
     ]
    {:id id :tiles image }))   

roelofw07:12:01

I think this one is not testable :

(defn do-both-in-parallel
  [ids]
  (let [paint-thread (future (pmap #(read-data-painting (read-json-data %))  ids))
        image-thread (future (pmap read-image-url ids))]
    (map merge @paint-thread @image-thread))) 

roelofw07:12:32

and I think this route file is also not testable:

(ns paintings2.routes.home
  (:require [paintings2.layout :as layout]
            [compojure.core :refer [defroutes GET]]
            [ring.util.http-response :as response]
            [ :as io]
            [paintings2.api-get :as api]
            [compojure.route :refer [resources]]
            [environ.core :refer [env]]
            [paintings2.api-get :as api]
            [clj-http.client :as client]))

(defn home-page []
  (let [url ""
        options {:as :json :query-params {:key (env :key) :format "json" :type "schilderij" :toppieces "True"}}]
        (layout/render
          "home.html" {:paintings (-> (client/get url options)
                                      api/read-numbers
                                      api/do-both-in-parallel)})))

(defroutes home-routes
  (GET "/" [] (home-page))
  (resources "/")  ) 

roelofw08:12:31

Can anyone help me with this problem : https://www.refheap.com/124217

agile_geek08:12:50

You can 'integration' test the app that uses those routes by passing it a mock request constructed by this library https://github.com/ring-clojure/ring-mock but you would have to mock out the api-calls again. You could do this by wrapping your test in a with-redefs or by injecting the api-call into the request map using a ring middleware function. Probably easier to use with-redefs for now as the middleware option, although possibly a better solution, would take you learning more concepts.

rauh08:12:35

@roelofw Read the lines 156... onwards of you stacktrace

agile_geek08:12:42

To test your do-both-in-parallel you could change it's arguments list to include a call-api-fn argument (like we started doing with read-data-painting) and use that call-api-fn in place of the real read-json-data. In the test pass it a fn that returns the same map using constantly. You could give it different versions of the fn in different tests to provide different maps to test. In the production code you would pass do-both-in-parallel the real read-json-data.

roelofw08:12:07

@rauh : I have read that part. but I do not see if the error is in the read function or the do-both-in-parallel

agile_geek09:12:37

This line at paintings2.api_get$read_image_url.invokeStatic(api_get.clj:45)

agile_geek09:12:56

says that your problem in in line 45 of api_get.clj

rauh09:12:03

You'll need to be able to read the stacktraces, it takes some practice. It does tell you the exact line and what function it calls into

agile_geek09:12:45

You are passing a keyword to something that is expecting a sequence (a list or vector type collection)

rauh09:12:01

Just giving the solution won't really teach you to debug problems IMO

rauh09:12:27

The calls happen from bottom to top.

roelofw09:12:05

Oke, so I have this image-thread (future (pmap #(read-image-url (read-image-data %)) ids))]

rauh09:12:47

Trust me on this one: Read the lines very carefully, even if it takes you 5 minutes.

roelofw09:12:51

which calls read-image-data which looks like this : ` (defn read-image-data [id] (->(str "https://www.rijksmuseum.nl/api/nl/collection/" id "/tiles") (client/get {:as :json :query-params {:format "json" :key (env :key)}}))) `

rauh09:12:32

So usually when you get an exception in clojure.core, you look at the edge where your library FIRST calls into clojure.core. Since it's unlikely that the bug is in clojure.core 🙂

rauh09:12:58

So find the line where it goes from YOUR-NS ---to--> clojure.core

rauh09:12:41

Then check the line one above, that's the clojure.core function that is being called. That is the one you're calling incorrectly

rauh09:12:05

This is the general approach for exceptions, not just this one.

roelofw09:12:08

that is here : ` at clojure.core$pmap$fn__6970$fn__6971.invoke(core.clj:6736) at clojure.core$binding_conveyor_fn$fn__4676.invoke(core.clj:1938) `

roelofw09:12:42

so the error is here :

at paintings2.api_get$do_both_in_parallel$fn__7399$fn__7400.invoke(api_get.clj:55)
 

roelofw09:12:33

which is this line : ' image-thread (future (pmap #(read-image-url (read-image-data %)) ids)) `

rauh09:12:34

The calls happen from bottom to top. You backtrace from top to bottom

rauh09:12:55

Then find the first line where you're in your ns

rauh09:12:14

Read from 156 down.

roelofw09:12:06

oke, then its this line : at paintings2.api_get$read_image_url.invokeStatic(api_get.clj:45)

roelofw09:12:38

which points to this code : id (get-in response ::body :objectNumber)

rauh09:12:49

What does the line above say?

roelofw09:12:12

What I tried to do was to get the body and objectnumber of the response

roelofw09:12:45

Which need to be this : id (get-in response [:body :objectNumber])

roelofw09:12:35

and it worked

roelofw09:12:42

thanks for the lessons

rauh09:12:44

Do you understand why? Do you understand the error message?

roelofw09:12:56

I do not have to do something about the warnings

roelofw09:12:22

yes, I did a double :: and forget to make a vector of it

rauh09:12:30

"Don't know how to create ISeq from: clojure.lang.Keyword" ??

rauh09:12:16

It wanted something of type ISeq, which means something that can be passed to (seq ...) like either a vector or a list or sequence

roelofw09:12:22

yes, it wants to make a seq but it cannot make it because it finds a keyword

rauh09:12:29

But instead you gave it a keyword

rauh09:12:44

Good, so this is the general approach of reading stacktraces

rauh09:12:02

Always find the first call into other-than-your-ns

rauh09:12:32

Then no guesswork is necessary

roelofw09:12:09

Thanks for the lessons

roelofw09:12:20

so I learned another things today

rauh09:12:32

From the post: "As a general rule, we want to look at the deepest (earliest) point in the stacktrace that we wrote. Sometimes an error will arise from deep within a library or Clojure itself–but it was probably invoked by our code somewhere"

roelofw09:12:12

I will read that

agile_geek09:12:13

Kyle's (@aphyr) whole series 'Clojure from the ground up' is worth reading IMO

iecya09:12:24

hello! I am having issue on using http kit to start a server. I tried to start the repl, run these commands (right from the docs)

(use ‘org.httpkit.server)
(defn app [req]
  {:status  200
   :headers {"Content-Type" "text/html"}
   :body    "hello HTTP!"})
(run-server app {:port 8080})
but seems that doesn’t work, I get The localhost page isn’t working Am I missing something or doing something wrong..?

olslash10:12:08

i just ran that in my repl and it works fine, must be another variable

iecya10:12:22

i tried a couple more time then i gave up and moved using ring, thanks anyway @olslash 🙂

olslash10:12:50

that always sucks

iecya10:12:32

ring or http kit?

olslash10:12:15

having to give up 🙂

iecya10:12:59

i guess that my lack of experience on using these kind of tools doesn’t help either xD I keep hitting a lot of walls everytime i try to do something 😆

olslash10:12:25

ive been doing that for the past month, great way to learn

iecya10:12:06

definitely! it takes a bit longer probably but it is so satisfying when suddenly you “see the light”!

kyle_schmidt13:12:12

Is there a better way to say this:

(defn get-users []
  (let [users (mc/find-maps db "documents")]
    (map #(into {} %) (map #(seq (select-keys % [:first_name :last_name])) users))))

manutter5113:12:42

What are you trying to get as the result?

kyle_schmidt13:12:32

vector of maps

st13:12:22

@kyle_schmidt isn’t (map #(select-keys % [:first_name :last_name]) users) enough? (and usually favor first-name instead of first_name )

kyle_schmidt13:12:28

ahh yes, perfect!

roelofw15:12:26

I have this :

(not(every? true? args))  

roelofw15:12:48

how can I add a test if one of the args is true

dpsutton15:12:02

so you want to filter down to only the true ones and see if there are only 1 in that list?

roelofw15:12:24

I tried :

( and (not(every? true? args)) (every? true? args)))     ( and (not(every? true? args)) (not-any ? true? args)))   ( and (not(every? true? args)) (some true? args)))  

roelofw15:12:32

but they all fall on a case

roelofw15:12:15

@dpsutton : no, I try to take care if args = [ false, false, false] the outcome will be false instead of true

dpsutton15:12:09

your first part is always false. you have (and (not predicate) (predicate))

roelofw15:12:57

bummer, so back again to the drawing board

roelofw15:12:26

this challenge is bugging me for a long time

rauh15:12:44

Fun fact, the solution is only 4 characters 🙂

rauh15:12:01

Not that this will help you. I'm sorry. SCNR

roelofw15:12:07

I think this must be solved by some? not-any or something like this

rauh15:12:53

Hint: Switching the word "true" for "false" still gives an equivalent problem description: "Your function should return true if some of the parameters are true, but not all of the parameters are true"

roelofw15:12:20

@rauh : that looks if a combination of some and/or not-any

roelofw15:12:10

nope, this one ( and (not(some true? args)) (not-any? true? args)))

roelofw15:12:34

fails here :

AIL in (test2) (1_half_truth_test.clj:11)
Half truth - test 2
expected: (= (half-truth true false true) true)
  actual: (not (= false true))  

roelofw15:12:54

and if I only use some it fails here :

FAIL in (test1) (1_half_truth_test.clj:7)
Half truth - test 1
expected: (= (half-truth true true true true) false)
  actual: (not (= true false)) 

marciol15:12:46

Hi everyone. Do you know if are there some tool/generator to generate test files for a project?

roelofw15:12:19

I only know spec , @marciol

marciol15:12:21

spec generates the boilerplate needed around the test itself, such as namespace definition, etc, etc?

roelofw15:12:08

no idea, I have heard of it and I have read this page : http://clojure.org/guides/spec

dominicm15:12:30

@marciol What kind of thing are you trying to generate?

marciol15:12:10

Some tool that helps to organize test files with a proper convention

dominicm15:12:30

@marciol How do you mean "proper convention" what would you consider as a proper convention?

dominicm15:12:49

Also, if this is what you want — spec isn't useful to you

marciol15:12:05

for example, I was browsering pedestal examples so I got to this file

marciol15:12:23

Seems that for the namespace hello-world, the authors of this example created an correspondent test namespace called hello-world.service-test

marciol15:12:49

and the same pattern could be observed in other examples

marciol15:12:34

but i don’t know if it is a convention of this project or if there is a broader and accepted way of organize tests

dominicm15:12:38

I don't think this is a clojure-wide convention. As a community, we don't have much by way of "What's best for clojure" in terms of conventions. We just think in generals "What's good for testing"

dominicm15:12:03

I've also seen a convention where you would name the test namespace hello-world-test

dominicm15:12:14

Also nothing wrong with myapp.test.hello-world either really.

marciol15:12:55

ok, so what one think that works better for their context

marciol15:12:53

could not work for other. I get the point. Thanks

vandr0iy17:12:30

I found a really strange example of behaviour in clojure.specs; here's how to reproduce:

(let [arst {:a [1 2 3]
            :b {:c #{::z ::x ::c}}}]
  (s/keys :req-un (get-in arst [:b :c])))
This spits out an AssertionError, that tells me that all the keys must be namespace-qualified - which they are! What is really going on here?

vandr0iy17:12:25

clojure version used: 1.9.0-alpha14

rauh17:12:36

@vandr0iy s/keys is a macro

agile_geek18:12:35

@dominicm I tend to stick to hello-world-test simply because a lot of tooling expects this pattern (CIDER and Porjectile for example)

dominicm18:12:56

@agile_geek examples using other systems were pointed at. Overall, it probably doesn't matter much.

alexmiller18:12:17

@vandr0iy the value for :req-un must be a literal vector of namespaced keywords

alexmiller18:12:31

not an expression that evaluates to one

dominicm18:12:48

@agile_geek other examples exist (and were linked). So I guess it's about the project you copy. I don't think it matters really. Just be consistent and I'll find it.

roelofw19:12:41

If I have this code :

(defn read-image-url
  "Reads the image-url"
  [response]
  (let [art-objects (-> response
                        :body
                        :levels
                        )

        id (get-in response [:body :objectNumber])
        url (filter #(= (:name %) "z4") art-objects)
        tiles (:tiles (first url))
        image (get-in tiles [0 :url])
        ]
    {:id id :tiles image}))   

roelofw19:12:46

is this object right { :body {:levels {:name "z4" { [:tiles {[:url " http: //example.com ]} ]} }}

roelofw19:12:58

if I have balanced everything right ?

dpsutton19:12:22

i don't think that can be right

dpsutton19:12:32

you have {:name "z4"

dpsutton19:12:38

which is the first k/v pair of a map

dpsutton19:12:51

and then you have a single object {... so its not a k/v pair

dpsutton19:12:54

so it can't be right

dpsutton19:12:08

and then in that map, you have a single vector, so that can't be a valid map

dpsutton19:12:16

remember what it takes to be a map

dpsutton19:12:20

you have to have a key and a value

roelofw19:12:31

oke, I try to change a orginal response which looks like this : https://www.refheap.com/124220

dpsutton19:12:18

what? i'm just pointing out what you wrote earlier above isn't syntactically valid

roelofw19:12:17

I know, i tried it out of my head but then I make to many mistakes

dpsutton19:12:32

then i'm not sure what your question is

dpsutton19:12:46

you were asking if this object was right and that's certainly not

dpsutton19:12:51

that's what i was saying

roelofw19:12:26

yes, thanks for that

roelofw19:12:40

That was my own conclusion

roelofw19:12:34

What is wrong here :

"body": {
		"levels": [{
			"name": "z4",
			"tiles": [{
				url: "http: //lh6.ggpht.com/pA_7SmGBE7N60aZu5PD5emVDobNJ424v2eDWJGoZ_cDbTquRT7QukK3a4I6K-nHieHIMFBVsNiwHrJRjOAutVYiswg=s0"
			}]
		}]
	}  

roelofw19:12:54

with a validator I see a error message on the tiles line

jswart19:12:42

url: is not valid JSON all keys must be strings in JSON. ”url”:

unbalanced21:12:26

does anyone happen to have a link to the in-progress clojure desk reference book?

unbalanced21:12:42

I can't seem to scroll back that far in the #announcements history

unbalanced21:12:50

❤️ u 🙂