Fork me on GitHub
#beginners
<
2019-11-06
>
sysarcher11:11:28

Hello everyone... does someone have a simple example of cljs-http? I'm very new to Clojure and can't seem to wrap my head around cljs-http. The README.md's examples haven't helped either... My test setup is a file.json in a directory (the file contains a JSON string). For serving, I'm using python3 -m http.server (it creates a simple server on localhost:8000 by default serving the contents of the current folder). What I've been unable to achieve is just get the contents of the file.json as-is (`curl` and opening the URL in the browser work fine... I tested with Python's requests package too)... What I need is something in Clojurescript please 🙂

herald11:11:30

Have you added [org.clojure/core.async "0.4.500"] as a dependency and the imports/requires listed at the top of the cljs-http readme to your namespace?

herald11:11:52

This should then work

(go (prn (<! (http/get ""))))

herald11:11:51

cljs-http isn't very newbie-friendly due to it using core.async. If you'd prefer to start off with simple callbacks, https://github.com/JulianBirch/cljs-ajax uses this

sysarcher11:11:56

thanks for the response @UCW9TUDNK... I'll give cljs-ajax a shot too Though I'm perplexed why this simple test isn't working :thinking_face: This is what I got when I ran your example

dev:example.core=> (go (prn (<! (http/get ""))))
#object[cljs.core.async.impl.channels.ManyToManyChannel]
{:status 0, :success false, :body "", :headers {}, :trace-redirects ["" ""], :error-code :http-error, :error-text " [0]"}

herald12:11:18

I'm getting the same error actually! Using npm serve to create a web server serving a json file. I'm also getting this error in my browser Access to XMLHttpRequest at '' from origin '' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

herald12:11:50

This however, works fine.

(go (prn (<! (http/get "" {:with-credentials? false}))))

herald12:11:30

So I'm guessing it's a CORS policy problem between the browser and these minimal web servers

sysarcher12:11:03

Interesting... yeah, the request to http://api.github.com worked for me too :man-facepalming: But I don't see the CORS issue in my browser

sysarcher12:11:25

huh?? I set up a full spring-boot application (for which I was preparing the client and it ran directly) Is this something we should report to the developer?

herald12:11:41

Sorry, I don't understand what you mean? Are you still getting the same 0 status response?

sysarcher12:11:12

no, I got the correct response ...

sysarcher12:11:14

and the data I was looking for got printed 🙂 It's strange why the simple python3 -m http.server didn't work ... In fact my idea (since I'm new to Clojure) was to figure out handling JSON using this python server first and I'd look into the authentication etc. with our actual server later. But this worked in the first attempt:

(go (prn (<! (http/get "" {:basic-auth {:username "user" :password "PassW0rt"}}))))

herald12:11:17

ahh that's great to hear! Yeah, I think it's definitely something with these micro http servers not being fully compliant.

parrot 4
bartuka12:11:44

hi, I am using clojure.spec heavily on my project and I started to have duplicated entries during my tests because of a mis-understanding of the parameter distinct in the s/coll-of function. This is my example:

(s/def ::value int?)
(s/def ::name string?)
(s/def ::insta (s/keys :req [::value ::name]))
(s/def ::list-values (s/coll-of ::insta :distinct true :into [] :count 3))
I would need to generate values where either ::name and ::value are distincts for every element of ::list-values.

bartuka12:11:58

right now, it only certifies that the whole map called ::insta is distinct. but it's possible to have equal ::value and different ::name for example

Michaël Salihi13:11:09

Hello everybody, I would to know if I can kick off a one command line simple http server in Clojure like Python does python3 -m http.server ? With tools.deps for eg. ? clj -Sdeps '{:deps {some/clojars {:mvn/version "x.x.x"}}}' -m http.server

Michaël Salihi14:11:39

@rahul080327 Yes it works, thx. Any chance to find equivalent without lein and any config ? With Clojure cli (tools.deps) ? I found this usefull list of one line http server command : https://gist.github.com/willurd/5720255. Clojure and lein-simpleton is mentionned in comments section.

metehan15:11:27

does anyone uses @r0man’s https://github.com/r0man/cljs-http here. I have a problem when i try to send a JWT key with http header it can be send via post method but get method header can not be attached

metehan15:11:11

[response (<! (http/get (str config/api-url "/api/v1/customer/" id)
                                    {:with-credentials? false
                                     :headers {"Authorization" @(rf/subscribe [:jwt-token])}
                                     }))]

danieroux19:11:20

This matches my code, which works fine:

(http/get api-uri
   {:with-credentials? false
    :headers           (if jwt-token
                         {"Authorization" jwt-token}
                         {})})

tzzh15:11:08

Hey, when I run

(do
  (for [a [1 2 3]] (println a)) 
  (for [a [4 5 6]] (println a)))
it only prints out 4, 5 and 6 (when I was expecting 1,2,3,4,5,6), why is it the case ?

tzzh15:11:52

oh actually I think I got it, for is lazy but as do returns the last expression the second one is evaluated

👍 4
metehan16:11:24

i think this is weird behavior i didn't undertand the reason why it doesn't print 1,2,3

tzzh16:11:51

ended up doing

(dorun
  (concat
    (for [a [1 2 3]] (println a))
    (for [a [4 5 6]] (println a))))

tzzh16:11:51

@m373h4n the reason is for gives you a lazy sequences so do as two lazy sequences and returns the last one which forces evaluation of the second one

metehan16:11:55

so stuff inside for block doesn't run anything

metehan16:11:05

interesting thanks

pez16:11:00

lazy is tricky. I hope to some day stop making mistakes like the above one.

Jan K16:11:01

you might want to use doseq instead of for here

➕ 4
noisesmith17:11:47

@thomas.ormezzano doseq is mostly the same as for, but runs for side effects instead of lazily producing data

👍 4
noisesmith17:11:41

so (doseq [a [1 2 3]] (println a)) (doseq [a [4 5 6]] (println a)) or (doseq [a (concat [1 2 3] [4 5 6]] (println a))

noisesmith17:11:03

or just (run! println (concat [1 2 3] [4 5 6]))

noisesmith17:11:12

(run! is like map, but for side effects)

bartuka17:11:41

still can't figure out how to solve my problem with clojure.spec

(s/def ::value int?)
(s/def ::name string?)
(s/def ::insta (s/keys :req [::value ::name]))
(s/def ::list-values (s/coll-of ::insta :distinct true :into [] :count 3))
I would need to both ::name and ::value to be distinct for every element in the collection ::list-values

bartuka17:11:49

I am trying to use custom generators to do that:

(gen/not-empty
             (check-gen/list-distinct-by
              ::value
              (gen/hash-map ::value (-> ::value s/gen) ::name (-> ::name s/gen))))
not sure if the right direction, but at least I have control over the distinct values in the map created

Alex Miller (Clojure team)17:11:12

can you write a function that verifies this property? if so, s/and an invocation of that function with your spec

Alex Miller (Clojure team)17:11:34

oh, you're on the generator side

bartuka17:11:59

yes, but I think your solution is better.. let me try

Alex Miller (Clojure team)17:11:10

well, won't help you with generation

Alex Miller (Clojure team)17:11:41

but you could just gen and then filter

bartuka17:11:03

way easier..

(s/def ::list-values
  (s/and (s/coll-of ::insta :distinct true :into [] :count 3)
         #(check-distinct-keys %)))

(defn- check-distinct-keys [entry]
  (and (apply distinct? (map ::value entry))
       (apply distinct? (map ::name entry))))

johnjelinek18:11:57

how do I bake a text file into a jar with tools.deps? Do I use :extra-paths? Is there already a resources convention?

Alex Miller (Clojure team)18:11:30

Add the directory containing it to :paths

👍 4
Alex Miller (Clojure team)18:11:56

Although I guess really it depends on the tool making the jar

johnjelinek19:11:43

is there a way to define this inline?

(defn- wrap [x]
  {:body x
   :statusCode 200})
something so I could do this?
(-> (something)
      #({:body % :statusCode 200})
      (.flush ....)
)

bfabry19:11:07

(as-> x {:body x :statusCode 200})

bfabry19:11:12

that's my favourite way

noisesmith19:11:39

you can literally use (wrap) in your second form as it's defined there

noisesmith19:11:15

or even ((fn [x] {... ...})) inline (but that's bad style)

bfabry19:11:35

I think he means he doesn't want to have to define wrap but doesn't want to have to do weird stuff to jump out of the thread

bfabry19:11:47

cljs.user=> (-> 2
       #_=>   (as-> x {:body x :statusCode 200})
       #_=>   )
{:body 2, :statusCode 200}

🎉 4
Ian Fernandez19:11:13

people, I'm new to spec generators

Ian Fernandez19:11:25

I want to generate an instant after 2010

Ian Fernandez19:11:54

(defn gen-MMyyDate []
  (gen/fmap #(.format (java.text.SimpleDateFormat. "MMyy") %)
            (s/gen inst?)))

Lennart Buit20:11:40

Take a look at gen/such-that

Mario C.22:11:37

Is it possible to return a collection of elements as is-- as in not wrapped in a list? I have this

(defn fields
  [config & args]
  (for [f args]
    [:field config f]))

(def layout
  [[:section {:name :initial
              :label "Initial"}
    [:field {:class "unique-class" :label "Number?"} :_att1]
    (fields {:class "common-class"}
            :_att2
            :_att3
            :_att4)]])
This returns what I expect, except ideally I would like to have the fields function return the vectors outside of a list. So the output ideally would look like:
[[:section
  {:name :initial, :label "Initial"}
  [:field {:class "my-class", :label "Hello"} :_att1]
  [:field {:class "common-class"} :_att2]
  [:field {:class "common-class"} :_att3]
  [:field {:class "common-class"} :_att4]]]

Mario C.22:11:10

Instead of

[[:section
  {:name :initial, :label "Initial"}
  [:field {:class "my-class", :label "Hello"} :_att1]
  ([:field {:class "common-class"} :_att2]
   [:field {:class "common-class"} :_att3]
   [:field {:class "common-class"} :_att4])]]

seancorfield22:11:46

(def layout [(into [:section ,,, [:field ,,,]] (fields ,,,))])

seancorfield22:11:20

(I think I have the nesting right there)

Mario C.22:11:53

Only thing is that there can be more elements after that first fields call.

noisesmith22:11:56

the fact that into (or concat etc.) are needed here actually touches on one of my favorite things about clojure: you can't alter the semantics of a parent form from a child

noisesmith22:11:14

there's a predictable meaning that isn't changed via "spooky" interactions

noisesmith22:11:39

so you can't change the arg count of a parent based on how the child is implemented

noisesmith22:11:58

which is why you need some explicit combining-of-collections operation to be done

seancorfield22:11:04

@mario.cordova.862 Then you'll want two into calls and it might be clearer to thread them...

noisesmith22:11:07

another option, especially since you aren't using quoted symbols here, is using `, with ~@, but into is more idiomatic with hiccup

seancorfield22:11:18

(def layout
  [(-> [:section {:name :initial :label "Initial"} [:field ,,,]]
       (into (fields ,,,))
       (into [[:field ,,,] [:field ,,,]]))])

Mario C.22:11:21

hmm that gives me an idea

noisesmith22:11:10

honestly I'm not sure why this never caught on in reagent code

user=> `[:a ~@(range 3)]
[:a 0 1 2]
maybe I am forgetting some gotcha

tdantas22:11:36

sorry to bother you guys I have the simple JS code and would love to port to clojure isolating the side effects how can I do that ?

var invoicesService = require('./service-layer/users')

function createInvoiceWebHandler(request) {
   const user = request.currentUser
   invoicesService.createInvoice(user, request)
}


var invoicesRepository = require('./data-later-layer/users')
var mailer = require('./mailer')

function createInvoice(user, invoice) {
  const invoiceTotal = invoice.total
  const itemsTotal = invoice.items.reduce((acc, item) => acc + (item.unitValue * item.qty), 0)

  if (itemsTotal != invoiceTotal) {
    throw "Invoite items sum different from invoice total"
  }

  const savedInvoice = invoicesRepository.save(invoice)
  mailer.sendInvoice(user.email, savedInvoice)

}

noisesmith23:11:18

why isolate side effects? every single line is a side effect isn't it?

noisesmith23:11:18

the interop translation is pretty mechanical, mostly just moving a ( one token to the left

tdantas07:11:43

My point is , how to write test easily for that function . The service layer depends of “repository” and “mailer” . How easily can I test

tdantas08:11:43

Probably the example I gave is not the best , as you said , we have side effect everywhere ... my solution for the problem was injecting the dependency which make my test super easy . A better example I will give you , news to understand how you guys do idiomatic clojure

tdantas08:11:00

var invoicesService = require('./service-layer/users')

function createInvoiceWebHandler(request) {
   const user = request.currentUser
   invoicesService.createInvoice(user, request)
}


var invoicesRepository = require('./data-later-layer/users')
var mailer = require('./mailer')

function createInvoice(user, invoice) {
  const invoiceTotal = invoice.total
  const itemsTotal = invoice.items.reduce((acc, item) => acc + (item.unitValue * item.qty), 0)

  if (itemsTotal != invoiceTotal) {
    throw "Invoite items sum different from invoice total"
  }

  const savedInvoice = invoicesRepository.save(invoice)
  mailer.sendInvoice(user.email, savedInvoice)

}

tdantas08:11:49

to better understand my point, I did some changes on the sample to introduce some ‘non-side-effect’ functionality. my point is, everyone is always saying “please, pure fuctions everywhere, isolate the side effects” , just need to see how clojurians is doing on real code. ( code is updated already )