This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-03-04
Channels
- # announcements (19)
- # babashka (11)
- # babashka-sci-dev (9)
- # beginners (71)
- # calva (25)
- # cider (1)
- # clara (36)
- # clj-kondo (47)
- # clojure (65)
- # clojure-dev (64)
- # clojure-europe (9)
- # clojure-nl (2)
- # clojure-seattle (1)
- # clojure-uk (2)
- # clojured (8)
- # clojurescript (17)
- # cursive (9)
- # data-science (36)
- # datahike (11)
- # emacs (10)
- # figwheel-main (19)
- # fulcro (15)
- # graalvm (12)
- # humbleui (5)
- # introduce-yourself (3)
- # jobs (10)
- # leiningen (4)
- # lsp (24)
- # malli (7)
- # nextjournal (23)
- # off-topic (1)
- # pedestal (2)
- # polylith (6)
- # portal (1)
- # re-frame (3)
- # reitit (2)
- # releases (2)
- # remote-jobs (1)
- # reveal (9)
- # shadow-cljs (13)
- # spacemacs (6)
- # xtdb (3)
(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?(as for what that first argument actually is in this case, you'd need to read the Integrant documentation -- I've never used Integrant)
Hello. When I tried to evaluate an expression like (run-jetty #'handler {:port 3000})
, subsequent evaluations seemed to be blocked.
How to avoid this?
use (run-jetty #'handler {:port 3000 :join? false})
instead
https://ring-clojure.github.io/ring/ring.adapter.jetty.html
(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])
(let [joy-class (get-in multiplayer-game-state [:ryan :class])
ryan-class (get-in multiplayer-game-state [:ryan :class])])
perhaps you are after something like this?e
or with nested calls
(:class (:ryan multiplayer-game-state))
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)
.
Perhaps post more code for context - lets us see how you're consuming the class names.
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(let [{{joe-class :class} :joe
{ryan-class :class} :ryan} multiplayer-game-state]
...)
@imxingquan does this work for you?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
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)))
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))
Filter a list of maps to only include maps that have a particular entry:
(filter #(= "expected-value" (:expected-key %)) maps)
user=> (filter #(= :red (:color %)) [{:item "Tomato", :color :red} {:item "Spinach", :color :green}])
({:item "Tomato", :color :red})
You mean you want to find all maps that have an expected key, and return the value?
OK, simply combine functions:
(map :item (filter #(= :red (:color %)) maps)
This is where the “threading macros” come handy. We can write the same as:
(->> maps
(filter #(= :red (:color %)))
(map :item))
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
map
will read the lazy sequence, therefore evaluating it. It will itself return a lazy sequence.
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
Actually it is some thing like hashmap so I am using (.getItem)
So I am getting error as No matching field found
Can you please share a code snippet of what you are trying to do?
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)
This should not have anything to do with the laziness. How are you using map
?
If you want to extract the name + filter, this should work:
(->> zip-entries
(map #(.getName %))
(filter #(= "core.clj" %)))
I assume you tried (map .getName)
?
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))
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 errorWhat libraries are you using? Is request
from clj-http
?
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]
?
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))
?
Or: {:pre [(int? hours)]}
in a function you call from your handler?
What is the easiest way to convert JSON to EDN?
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?
not clojure related but cat is kind of an abuse in this case, you can just use <
like jet --from json --to edn < file.json
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
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)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..
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.
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))
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)
I'll try the println calls again tomorrow, it's pretty late here and I closed my laptop already! 😁
And that makes sense about replacing as-> with a let.. kinda went over my head that I can use let instead there.
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
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?
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
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...
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 #","))
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?
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
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."
@U5JPZFFR6 why doesn't make sense in elixir?