Fork me on GitHub
#beginners
<
2022-01-24
>
jimmysit000:01:23

Is there a difference between using :refer and :as ?

seancorfield00:01:09

@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.

jimmysit000:01:04

Why are aliases prefered?

seancorfield00:01:04

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

seancorfield00:01:28

Whereas with

(defn foo [x]
  (wibble x)) ; in this ns or :refer'd in?

jimmysit000:01:41

Ah okay. Thanks

seancorfield00:01:59

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.

jimmysit000:01:16

Is there any kind of styling recommendation for the :as names?

jimmysit000:01:20

Or a styling guide

jimmysit000:01:50

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

seancorfield00:01:59

A good rule of thumb is [the.last.segment :as segment] as long as that is unique in that file.

seancorfield00:01:36

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]

jimmysit000:01:37

Yeah I usually do that, eg: [com.brunobonacci.mulog :as u] (3rd party lib, but I do that pretty often)

R.A. Porter01:01:49

Those were all good, except I do not like the recommended alias on that page of as for async.

seancorfield01:01:37

Ooh, yeah, good catch. I've not seen as for async before -- either async or just a.

seancorfield00:01:53

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)

Jacob Rosenzweig03:01:03

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.

seancorfield04:01:52

☝️:skin-tone-2:Yup, this.

Nom Nom Mousse08:01:39

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.

p-himik10:01:43

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.

p-himik10:01:58

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.

Nom Nom Mousse10:01:47

Understood. I thought they might last for a second or two but I was just cargo culting 🙂

p-himik10:01:18

There's no magic here. :) Go with the most basic approach that is still safe and is guaranteed to work.

Nom Nom Mousse10:01:25

"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.

p-himik10:01:37

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.

Nom Nom Mousse10:01:57

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]}]

p-himik10:01:14

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.

Nom Nom Mousse10:01:12

Thanks! 🙂 The only external thing I use in my JSON handler is muuntaja/wrap-format so I'll investigate it

p-himik10:01:13

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.

Nom Nom Mousse10:01:42

Good point: reitit.ring/router. I'll look at it too 🙂

Noah Moss15:01:37

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?

andy.fingerhut15:01:17

No way that is built into Clojure, no. Someone may have written a library that does this.

randomm char15:01:25

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 ??

dpsutton16:01:11

comparing a single value is straight forward in the manner you just put. Comparing two possibly deeply-nested maps is not so straightforward

Noah Moss16:01:59

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}

dpsutton16:01:33

is this just for a test or in “regular” code?

Noah Moss16:01:33

mainly just curiosity, but inspired by an issue I’m working on. I’ll ping you directly

dpsutton16:01:03

ha just noticed who you are!

👀 1
randomm char16:01:56

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 :)

dpsutton16:01:01

I haven’t seen any mention of sets in this discussion

andy.fingerhut17:01:34

For keys in maps, 0 is not equal (via clojure.core/=) to 0.0 in Clojure

andy.fingerhut17:01:01

user=> (= {0 :a} {0.0 :a})
false

andy.fingerhut17:01:14

Also

user=> (get {0 :a} 0.0)
nil

andy.fingerhut17:01:14

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

🙌 1
💯 1
andy.fingerhut17:01:07

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.

andy.fingerhut17:01:48

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

Noah Moss15:01:22

@U0CMVHBL2 interesting, thanks!

Nundrum22:01:52

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?

lsenjov22:01:50

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

lsenjov22:01:08

So honestly… spit out a new project with each of them, look at the code and see which one you like best

lsenjov22:01:27

And if you like bits of one but not others, you can usually retrofit those parts over

lsenjov22:01:48

The only one I’ve seen so far that’s all encompassing is Fulcro, but that’s doing some serious magic to need it

Nundrum22:01:03

No wonder I can't figure out which way to go.

dorab22:01:58

Might also want to look at juxt/site.

Nundrum22:01:09

That's an interesting choice

Rupert (All Street)22:01:29

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).

Nundrum15:01:51

Hey @U0AT6MBUL I looked over juxt/site a bit last night. Have you worked with it? Have impressions to share?

dorab17:01:37

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.

👍 1