This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-01-24
Channels
- # announcements (3)
- # aws (5)
- # babashka (10)
- # beginners (61)
- # calva (22)
- # clara (9)
- # clj-kondo (8)
- # cljdoc (8)
- # cljsrn (15)
- # clojure (44)
- # clojure-australia (2)
- # clojure-europe (31)
- # clojure-hungary (20)
- # clojure-nl (5)
- # clojure-uk (3)
- # core-logic (2)
- # cursive (2)
- # data-science (2)
- # datalevin (4)
- # datascript (6)
- # datomic (17)
- # defnpodcast (1)
- # figwheel-main (1)
- # fulcro (18)
- # graalvm (2)
- # introduce-yourself (2)
- # jobs (1)
- # jobs-discuss (59)
- # lsp (44)
- # music (1)
- # nrepl (2)
- # off-topic (26)
- # pedestal (2)
- # re-frame (12)
- # reagent (27)
- # releases (1)
- # remote-jobs (4)
- # rewrite-clj (2)
- # sci (12)
- # shadow-cljs (12)
- # sql (10)
- # uncomplicate (6)
- # vim (12)
- # xtdb (2)
@jimmyssj3 Yes, :as
introduces an alias for the namespace, e.g., [clojure.set :as set]
so you can call functions within it via that alias, e.g., (set/union s1 s2)
whereas :refer
directly imports the names into the current namespace. Sometimes that's nice for readability but it's generally recommended to use aliases as much as possible.
Because it makes it more explicit that a name comes from a different namespace:
(defn foo [x]
(bar/wibble x)) ; clear that wibble is from another ns
Whereas with
(defn foo [x]
(wibble x)) ; in this ns or :refer'd in?
The operator-like functions in core.async
are "unusual" names so it's easy to remember "Oh yeah, that's a core.async function!" but if you just saw (thread ...)
you might not immediately think "Ah, clojure.core.async/thread
!" whereas if you saw (a/thread ...)
or (async/thread ...)
it would be much more obvious.
I usually just give names that "click" on my head, but for somebody who's looking at my code it may not be like that
A good rule of thumb is [the.last.segment :as segment]
as long as that is unique in that file.
But there are some common abbreviations people use: [clojure.string :as str]
is pretty common, [clojure.core.async :as a]
, [clojure.core.spec :as s]
This is pretty good advice: https://guide.clojure.style/#use-idiomatic-namespace-aliases
Yeah I usually do that, eg: [com.brunobonacci.mulog :as u]
(3rd party lib, but I do that pretty often)
Those were all good, except I do not like the recommended alias on that page of as
for async.
Ooh, yeah, good catch. I've not seen as
for async
before -- either async
or just a
.
With core.async
you often see people alias it to a
or async
but also refer in some of the operator-like function names because it can make your code a bit more readable. For example, the Brave and True book has this example:
(ns playsync.core
(:require [clojure.core.async
:as a
:refer [>! <! >!! <!! go chan buffer close! thread
alts! alts!! timeout]]))
Personally, I think that's a bit overkill: I would only refer in those first four and then use the alias to reference the others, e.g., (a/close! c)
At my org, :refer
is used pretty sparingly. It ends up being easier to read if an alias is used, also avoids conflicts with function names.
☝️:skin-tone-2:Yup, this.
This is pretty good advice: https://guide.clojure.style/#use-idiomatic-namespace-aliases
I want to send POST commands to a Luminus web app with CSRF tokens. Is the below the correct way?
CSRF=`curl localhost:3000 | grep 'csrfToken = ".*"' | cut -d '"' -f 2`
# iaGamuMOBB9SJ0k23dMV8F9Yci3+e6MNytIO46cJNmsCK+cpxTpen4W1J5Ci7D17pSZDu0iD++e6PpPQ
curl -H "Content-Type: application/json" -H "X-CSRFToken: iaGamuMOBB9SJ0k23dMV8F9Yci3+e6MNytIO46cJNmsCK+cpxTpen4W1J5Ci7D17pSZDu0iD++e6PpPQ" -X POST -d {"id": "wakka wakka hey hey"} localhost:3000/json
I'm still getting an Invalid anti-forgery token reply.CSRF tokens are not for authentication.
If something is using cookies, you need a CSRF token. If something is using any other, non-persistent, way of authentication, then the token is not needed. For example, you can use the most basic thing there is - the Authorization
HTTP header. As long as the connection is done either via https://
or via http://
but to localhost
on the host that you fully trust, it will be fine.
To add a bit more details - if your specific example worked, it would mean that anyone would be able to change your config. CSRF tokens are tied to sessions, they are meaningless without them. A session can be either authenticated or not authenticated, but it has to be a persistent session.
Understood. I thought they might last for a second or two but I was just cargo culting 🙂
There's no magic here. :) Go with the most basic approach that is still safe and is guaranteed to work.
"Still safe" is hard to tell for someone who does not know anything about network security XD But I turned off CSRF middleware for my JSON routes.
You shouldn't just turn off that middleware - if you have a web app that uses cookies, the CSRF tokens must still be used.
But that middleware shouldn't be used for requests that don't care about cookies. In fact, that should probably already be baked into the middleware itself.
> "Still safe" is hard to tell for someone who does not know anything about network security
Sure, I was just referring to my mention of the Authorization
HTTP header, with basic auth.
It is there for all my other routes though:
["/" {:get home-page :middleware [middleware/wrap-csrf]}]
["/json" {:post (fn [req] (json-handler req)) :middleware [wrap-formats]}]
If the "/json"
route never checks for cookies and only accepts non-persistent basic auth, then it's alright. Mind that that cookie check can easily be not in your code but somewhere in the depths of Liminus - I've never used that framework so I don't know.
Thanks! 🙂 The only external thing I use in my JSON handler is muuntaja/wrap-format
so I'll investigate it
Well, those routes are fed to some function somewhere - it's also a place worth investigating, unless it's some generic routing library. And if not, I'd just read Liminus documentation - surely, there's a page on authentication in there somewhere.
Good point: reitit.ring/router
. I'll look at it too 🙂
is there any concise/built-in way to compare two collections but use ==
for numerical values? i.e. so that {:a 0}
and {:a 0.0}
are equal, etc?
No way that is built into Clojure, no. Someone may have written a library that does this.
user=> (def aaa {:a 0 :b 0.0}) #'user/aaa user=> (== (aaa :a)(aaa :b )) true user=> (doc ==) ------------------------- clojure.core/== ([x] [x y] [x y & more]) Returns non-nil if nums all have the equivalent value (type-independent), otherwise false ??
comparing a single value is straight forward in the manner you just put. Comparing two possibly deeply-nested maps is not so straightforward
yeah I guess there would be too many edge cases where the comparison behavior would be hard to define
like numerical values as keys — would {0 :a, 0.0 :b}
be equal to {0.0 :a, 0 :b}
, or would {0 :a, 0.0 :a}
be equal to {0 :a}
mainly just curiosity, but inspired by an issue I’m working on. I’ll ping you directly
I think my confusion stems from the the fact your talking about sets #{1 2 3} and have a example of a map {:a 1 :B 2 :c 3} From what I understand sets are hashed to make comparisons faster therefore 0 not equal 0.0 Where as in maps this is not the case. Pretty sure I'm missing something else ... main reason for my comment was to learn something new :)
For keys in maps, 0 is not equal (via clojure.core/=
) to 0.0 in Clojure
user=> (= {0 :a} {0.0 :a})
false
Also
user=> (get {0 :a} 0.0)
nil
I am not a Clojure core member, but did write a detailed article on the ins and outs of =
and ==
in Clojure here, and the Clojure core members approved it as a useful thing to publish: https://clojure.org/guides/equality
So I cannot tell anyone "This is the way things will always be in Clojure", because I don't have the authority to say that, but I can tell you how things are now, at least in most cases, and I have my educated guesses why, in many cases.
In particular, for hash-based sets and maps in Clojure, it seems to me very difficult, and/or inefficient, if it is even possible, to implement such a thing where the notion of equality for set members or map keys was clojure.core/==
, because then you would need a hash function that somehow made all of these things have the same hash value: Clojure ratio 3/2, float type 1.5, double type 1.5
@U0CMVHBL2 interesting, thanks!
I'm not in any sense a beginner, but I have not built a project using a web framework yet. That's something I want to do, and I've started by looking at Duct and Pedestal. I need something that makes a REST API easy, and works nice with a DB. What else should I consider? Advice on which to use?
There’s also Kit (and Luminus), but in general clojure frameworks aren’t frameworks per se, but collections on libraries set up ready for you
So honestly… spit out a new project with each of them, look at the code and see which one you like best
And if you like bits of one but not others, you can usually retrofit those parts over
The only one I’ve seen so far that’s all encompassing is Fulcro, but that’s doing some serious magic to need it
I've found that compojure
+ integrant
+ ring jetty/http-kit
to be a killer combination on the backend. Each library is small and easy to learn/use. The code you end up with should be modular and reusable. (Note: use compojure.core/routes
instead of defroutes
so it works nicely with dependency injection from integrant
).
Hey @U0AT6MBUL I looked over juxt/site a bit last night. Have you worked with it? Have impressions to share?
I have worked with juxt/yada
and really like it overall. The two things I liked most were: (a) a complete implementation of HTTP so I don't have to worry about all the edge cases; and (b) the declarative specification of the request and response.
juxt/site
is supposed to be the successor of juxt/yada
. I have not worked with juxt/site
except for trying out some small examples. I generally like what I've seen so far. I am planning on trying it out on an internal project shortly.
Check out this video on juxt/site
https://www.youtube.com/watch?v=JWa4NhjWNHQ