Fork me on GitHub
#beginners
<
2022-03-04
>
quan xing06:03:17

(require '[ring.adapter.jetty :as jetty]
         '[ring.util.response :as resp])

(defmethod ig/init-key :adapter/jetty [_ {:keys [handler] :as opts}]
  (jetty/run-jetty handler (-> opts (dissoc :handler) (assoc :join? false))))

(defmethod ig/init-key :handler/greet [_ {:keys [name]}]
  (fn [_] (resp/response (str "Hello " name))))
the underline in :handler/greet [_ {:keys [name]}] what's means in this? ignore the first argument? what's the first argument?

seancorfield07:03:51

(as for what that first argument actually is in this case, you'd need to read the Integrant documentation -- I've never used Integrant)

Hao Liang07:03:24

Hello. When I tried to evaluate an expression like (run-jetty #'handler {:port 3000}), subsequent evaluations seemed to be blocked. How to avoid this?

Godwin Ko07:03:05

use (run-jetty #'handler {:port 3000 :join? false}) instead https://ring-clojure.github.io/ring/ring.adapter.jetty.html

Hao Liang07:03:11

Ah ok, thanks. That’s helpful.😀

1
quan xing10:03:55

(def multiplayer-game-state
  {:joe {:class "Ranger"
         :weapon "Longbow"
         :score 100}
   :jane {:class "Knight"
          :weapon "Greatsword"
          :score 140}
   :ryan {:class "Wizard"
          :weapon "Mystic Staff"
          :score 150}})

(let [{{:keys [class weapon]} :joe} multiplayer-game-state]
  (str "Joe is a " class " wielding a" weapon))
how can I destructing joe's class and ryan's calss the same time.
(let [{joy-class :joe/class
       ryan-class :ryan/class
       } multiplayer-game-state])

Tuomas-Matti Soikkeli10:03:57

(let [joy-class (get-in multiplayer-game-state [:ryan :class])
       ryan-class (get-in multiplayer-game-state [:ryan :class])])

Tuomas-Matti Soikkeli10:03:04

perhaps you are after something like this?e

Tuomas-Matti Soikkeli10:03:13

or with nested calls

(:class (:ryan multiplayer-game-state))

teodorlu10:03:07

When you destructure, you destructure into local variables that have the same name as the key name. If you destructure both joe's class and ryan's class, they would both be named class. I don't think that's what you want! I suggest using plain key lookups like (-> state :joe :class).

teodorlu10:03:56

Perhaps post more code for context - lets us see how you're consuming the class names.

pavlosmelissinos12:03:53

when you have a bunch of similar things, perhaps a vector is a better representation than a map, e.g.:

(def multiplayer-game-state
  [{:name :joe
    :class "Ranger"
    :weapon "Longbow"
    :score 100}
   {:name :jane
    :class "Knight"
    :weapon "Greatsword"
    :score 140}
   {:name :ryan
    :class "Wizard"
    :weapon "Mystic Staff"
    :score 150}})

(defn player-status [{player-name :name
                      :keys [class weapon]}]
 (str (name player-name) " is a " class " wielding a " weapon))

(map player-status multiplayer-game-state)
edit: I would add a :players key under "state" and put the vector under that level because state is more naturally a map (you want to represent heterogeneous stuff, like players, rounds, etc) edit2: I also wouldn't store the player names as keywords, that's a bit weird

kraf12:03:20

(let [{{joe-class :class} :joe
       {ryan-class :class} :ryan} multiplayer-game-state]
  ...)
@imxingquan does this work for you?

👀 1
1
👍 1
quan xing13:03:09

thanks everyone. each answer is an inspiration to me

clojure-spin 2
❤️ 2
maverick11:03:23

I have a do-seq inside a function and also I am using when condition which will return value but it is not returning anything

Ferdinand Beyer11:03:11

Can you share some code please? I guess you might be thinking in an imperative way. E.g. code like this will not work as expected:

(doseq [i (range 10)]
  (when (= i 5)
    (* i 5)))

Ferdinand Beyer11:03:26

doseq is not meant to return something, it is meant for side effects such as printing to the console, logging, network calls, etc. If you want to return something, you might want to use functions such as map , filter, etc:

(filter (fn [i] (= i 5)) (range 10))

maverick11:03:32

Okay thanks

maverick12:03:04

How do I filter list of map and return particular key value only?

Ferdinand Beyer12:03:11

Filter a list of maps to only include maps that have a particular entry:

(filter #(= "expected-value" (:expected-key %)) maps)

Ferdinand Beyer12:03:19

user=> (filter #(= :red (:color %)) [{:item "Tomato", :color :red} {:item "Spinach", :color :green}])
({:item "Tomato", :color :red})

maverick12:03:29

No but it will return the whole map. I want to return only the value

maverick12:03:06

Like in above example I want to return Item value for color red

Ferdinand Beyer12:03:06

You mean you want to find all maps that have an expected key, and return the value?

Ferdinand Beyer12:03:51

OK, simply combine functions:

(map :item (filter #(= :red (:color %)) maps)

Ferdinand Beyer12:03:33

This is where the “threading macros” come handy. We can write the same as:

(->> maps
     (filter #(= :red (:color %)))
     (map :item))

Ferdinand Beyer12:03:31

Easy to read: 1. Take the sequence of maps 2. Filter by those who have a red color 3. Only keep their values for the :item key

maverick12:03:32

But the filter is returning lazy seq so I am not able to get :item value

Ferdinand Beyer12:03:14

map will read the lazy sequence, therefore evaluating it. It will itself return a lazy sequence.

Ferdinand Beyer12:03:31

If you need to force the realization, you can either chain another doall to it, or use mapv that returns a vector instead of a lazy sequence

maverick12:03:46

Actually it is some thing like hashmap so I am using (.getItem) So I am getting error as No matching field found

Ferdinand Beyer12:03:35

Can you please share a code snippet of what you are trying to do?

maverick12:03:13

I have a java.util.zip.ZipEntry file in which there are lots of files and I am trying to filter out name of the file so it uses (.getName) So I am able to do the filter part but it is returning LazySeq so I am not able to do (.getName)

Ferdinand Beyer13:03:39

This should not have anything to do with the laziness. How are you using map?

Ferdinand Beyer13:03:14

If you want to extract the name + filter, this should work:

(->> zip-entries
     (map #(.getName %))
     (filter #(= "core.clj" %)))

Ferdinand Beyer13:03:07

I assume you tried (map .getName)?

maverick13:03:33

I tried (map (.getName (filter #(function (.getName %)))zip-entries))

Ferdinand Beyer13:03:27

Note that filter returns a sequence, and .getName is not defined on the sequence. You need to apply it to each item in the sequence. This is what (map #(.getName %)) does. Without the ->>: (filter #(function %) (map #(.getName %) zip-entries))

zendevil.eth15:03:55

I’m sending an http request like so:

(-> session
      (content-type "application/json")
      (request (str "/api/roles/" (:db/id student-role) "/courses")
               :body (cc/generate-string {:requiresSpecialNeedsCert true
                                         :requiresInterpreter true
                                         :hours 1
                                         :minutes 30
                                         :location "Home"
                                         :workFilePath ""
                                         :comments "i love this kid"
                                         :startDate start-date
                                         :endDate end-date
                                         :student (:db/id student-role)
                                         :subject (:db/id subject)})
               :request-method :post))
This is my handler in the server:
["/roles/:roleId/courses" {:post {:parameters {:path {:roleId int?}
                                                   :body {:subject int?
                                                          :workFilePath string?
                                                          :requiresInterpreter boolean?
                                                          :serviceType int?
                                                          :comments string?
                                                          :requiresSpecialNeedsCert boolean?
                                                          :teacherOfRecord int?
                                                          :startDate string?
                                                          :endDate string?}}
                                      :handler enroll-student-in-course-handler}}]
But I’m getting this response:
{:status 400, :headers {"Content-Type" "application/json; charset=utf-8", "X-XSS-Protection" "1; mode=block", "X-Frame-Options" "SAMEORIGIN", "X-Content-Type-Options" "nosniff"}, :body "{\"error\":\"invalid request: Assert failed: (int? hours)\"}"}
I don’t have hours check in my body anyway, but I’m still getting this error

Ferdinand Beyer15:03:16

What libraries are you using? Is request from clj-http?

Ferdinand Beyer16:03:23

It is hard to help you from your code snippet without knowing what functions are actually called. Your client-side code looks suspicious to me. You seem to set a content type on a session map instead of a request map, and does request take arguments like [map url & key-value-pairs]?

Ferdinand Beyer16:03:46

Lastly, the server response will depend on your middleware. Your code looks like Reitit, but the error response does not look like a reitit coercion error response. Maybe your handler is indeed called and somewhere there is an (assert (int? hours))?

Ferdinand Beyer16:03:15

Or: {:pre [(int? hours)]} in a function you call from your handler?

Rob Haisfield19:03:52

What is the easiest way to convert JSON to EDN?

Rob Haisfield19:03:19

That looks like it will do it. The examples were small though… so would I just slurp the json file and use that as the input?

Rob Haisfield19:03:30

Or I guess I would do ‘cat file.js’ and pipe that into my jet command?

👍 1
pavlosmelissinos19:03:52

not clojure related but cat is kind of an abuse in this case, you can just use <

pavlosmelissinos19:03:31

like jet --from json --to edn < file.json

Rob Haisfield20:03:57

Awesome, thank you! This ended up working: jet -p --from json --to edn < tweet.js > tweet.edn There was a bit at the front of the .js file that was messing it up window.YTD.tweet.part0 = but I deleted that and now I have it all and wow it’s so much prettier in this format

Vilim Krajinovic21:03:22

Hi guys, completely new to clojure. I have one issue in which im not sure whats going wrong. I used the reagent template to build a frontend clojurescript/backend clojure application but i dont think reagent plays a part in this error. In my frontend code i have something like this

(defn fetch []
  (go (let [response (<! (http/post "" {:body @input-openapi}))]
        (reset! response-from-service response))))
The http/post is from cljs-http library. What im trying to do here is send a POST request to the backend which will contain in its body the @input-openapi which is just an atom which gets reset to the value of a textarea html element. and then i try to set the resposne-from-service atom to the response value. My backend has some routes defined in a reitit ring router and this is what im targeting from the frontend, its a simple rest endpoint
(reitit-ring/router
      [["/" {:get {:handler index-handler}}]
       ["/local" {:get {:handler local-fetcher-handler}}]
       ["/fetch" {:post {:handler fetch-handler}}]
       ])
Only the /fetch one is important right now. my fetch-handler looks like this
(defn fetch-handler
  "Fetches the spec reviewer service response for provided openapi in request body"
  [request]
  (fetcher/fetch-local (str (:body request)))
  )
I dont know if the str here is necessary, but i did it because when i pass a (slurp [filename]) which just reads an .json file i have locally i noticed the slurp reads the file as a string and when i use the slurp method everything works fine. The fetch-local does the following
(defn fetch-local
  "Fetches the response of the locally running spec reviewer service with the provided openapi."
  [openapi]
  (as-> (call-local-spec-reviewer openapi) response
        (if (success? response)
          {:status  (:status response)
           :body    (json/read-json (:body response))
           :headers (:headers response)}
          {:status  (:status response)
           :body    (:body response)
           :headers (:headers response)})))
it takes the openapi string, and tries to call the locally running other backend service i have and maps the response on success or error. The call-local-spec-reviewer does the following
(defn call-local-spec-reviewer
  "Calls the locally running spec reviewer instance with the provided openapi spec"
  [openapi]
  (http/post local-spec-reviewer-url
             {:body             openapi
              :content-type     "application/json"
              :throw-exceptions false}))
which basically just makes a post request to the other backend service i have. I'm unsure what the exact issue is but this is my thinking: I think that somewhere my json openapi string which im trying to pass from fronted to backend gets converted into something other then a string and then when i try to send it to my other application which reviews the actual openapi spec it returns 400 since it cant read the json on its end. In the fetch-local function if i change the openapi i get from frontend to a (slurp "resources/openapi/openapi.json") which contains the exact same thing i paste into the textarea on the frontend side, everything works fine. I hope im making some sense, im completely new to clojure 😄 (started fiddling around with this app like 5 days ago)

Vilim Krajinovic21:03:32

I wanted to debug this somehow. But i couldn't figure out how to use breakpoints in IntellJ/Cursive in this clojurescript frontend clojure backend setup. I can use the repl for both frontend part and backend part. But i'm unsure how to catch in a breakpoint in intellj what frontend sends to the backend..

seancorfield22:03:59

A lot of the time, folks just add println calls in strategic places and see what those values are. Be careful that println returns nil -- a useful trick is (doto (my expression) println) which invokes println on (my expression) but then returns (my expression), so you can use that in a (-> ... (doto println) ...) pipe.

seancorfield22:03:11

Your use of as-> would be more idiomatic as a let BTW:

(let [response (call-local-spec-reviewer openapi)]
    (if (success? response)
      {:status  (:status response)
       :body    (json/read-json (:body response))
       :headers (:headers response)}
      {:status  (:status response)
       :body    (:body response)
       :headers (:headers response)}))
as-> is intended for use in the middle of a -> pipeline where you have an expression that needs the value in a position that is not either the first or last argument so you get:
(-> (my expression)
      (do-stuff arg2 arg3)
      (as-> x (do-more-stuff arg1 x arg3))
      (and-then 2 3))

Vilim Krajinovic22:03:45

When I tried println statements I believe I ran into a problem of it just printing something that looks like a object reference instead of the string value. But that was when setup was much worse than what it is now (I'm kinda learning clojure by doing with this project, so lots of mistakes)

Vilim Krajinovic22:03:13

I'll try the println calls again tomorrow, it's pretty late here and I closed my laptop already! 😁

Vilim Krajinovic22:03:08

And that makes sense about replacing as-> with a let.. kinda went over my head that I can use let instead there.

Vilim Krajinovic10:03:35

Ok managed to make the POST request work correctly, what fixed it was adding a [ring.middleware.json :refer [wrap-json-body]] into my middleware

1
sP0re22:03:03

Question from a newbie 🙂: Which style do you usually use for your functions? "Classic style", thread-first macro, thread-last macro. I'm still learning, I ve just watched a conference on YouTube and I really loved the thread-last for its readability and for how much makes the problem easy.. but of course I have to practice all of them to have an idea. What do you think?

Michael Stokley22:03:59

it depends on the data being threaded through. if it’s a map, many of the core functions expect the map first, so thread first. if it’s a collection, thread last, because core functions that work on collections expect the collection last. or if it’s a series of domain functions and the data is neither a list or a map as such, it depends

☝️ 1
seancorfield00:03:01

Also, if you have a ->> pipeline, you may be able to rewrite it using the transducer-arity versions of map/`filter`/etc and avoid having to compute all those intermediate lazy sequences...

seancorfield00:03:33

For example:

(->> (str/split test-ids #",")
     (map str/trim)
     (map parse-long)
     (remove zero?)
     (into []))
could be
(into []
      (comp (map str/trim)
            (map parse-long)
            (remove zero?))
      (str/split test-ids #","))

Jonathan Chen03:03:37

This is probably answered already, but is there an explanation for the choice of core functions to not have consistent placement of the main threaded data element?

seancorfield04:03:26

(lots of gems in the FAQ)

👍 1
sP0re06:03:23

Thank everyone for your answers 🙂

Mno10:03:16

Especially during development and if the performance isn't important I would do each step in a let step because it's really nice for debugging also it generally adds legibility. But that might be a code smell

clj.max22:03:14

Once you understand the choice of arg first/last, it actually really makes sense. I ended up missing in in Elixir, where they basically copied the idea of threading (slightly differently) but almost everything is thread-last - which often doesn't make sense - for "consistency."

1
sP0re21:03:32

@U5JPZFFR6 why doesn't make sense in elixir?

clj.max21:03:45

I found that often times the thing I wanted to pass in wasn't in the last position, whereas in Clojure it's different between collection-fns and other fns, like mentioned above.