Does anyone know if there is a clever way to use sort or friends to sort a sequnce as I’m doing here?
(let
headers-ontime (filter #(starts-with? % "Ontime-") headers-pre)
headers-penalty (filter #(starts-with? % "Penalty-") headers-pre)
headers-late (filter #(starts-with? % "Late-") headers-pre)
headers (concat (filter (fn [s] (and (not (member s headers-ontime))
(not (member s headers-penalty))
(not (member s headers-late))))
headers-pre)
headers-ontime
headers-late
headers-penalty)
...
)the idea is to move everything which DOES NOT start with “Ontime-” nor “Penalty-” nor “Late-” to the beginning but keep them in the same order, and then end the sequence with everything which was not filtered to the beginning.
I think there’s some function which splits a sequence into two sequences, one whose elements match the predicate and the second of elements which fail to match. But I suspect there’s a clever way to construct a sort-compare function.
perhaps https://clojure.org/guides/comparators might offer some tips?
alternatively you could just reduce and put each item in a suitable bucket and finally combine the buckets
chatgpt suggests something like this.
((juxt even? remove) even? [1 2 3 4 5])
;; => [(2 4) (1 3 5)]and then just concatenate the two sequences.
@lasse.olavi.maatta or use group-by to collate into a true and false bucket.
I think it's a bucketing + keeping the same order type of question. Either you bucket them to 4 groups as you wrote in the first message:
(defn bucket [s]
(cond
(str/starts-with? s "Ontime-") 1
(str/starts-with? s "Late-") 2
(str/starts-with? s "Penalty-") 3
:else 0))
Or in two buckets like your second message suggests:
(defn bucket [s]
(if (or (str/starts-with? s "Ontime-")
(str/starts-with? s "Late-")
(str/starts-with? s "Penalty-"))
1
0))
Then use the sort-by:
(sort-by bucket headers-pre)
All together:
(def headers-pre
["Foo"
"Ontime-A"
"Bar"
"Late-X"
"Penalty-1"
"Baz"
"Ontime-B"
"Late-Y"])
=> #'user/headers-pre
(defn bucket [s]
(cond
(clojure.string/starts-with? s "Ontime-") 1
(clojure.string/starts-with? s "Late-") 2
(clojure.string/starts-with? s "Penalty-") 3
:else 0))
=> #'user/bucket
(sort-by bucket headers-pre)
=> ("Foo" "Bar" "Baz" "Ontime-A" "Ontime-B" "Late-X" "Late-Y" "Penalty-1")I thought about doing that, but hesitated because I don’t think it is a stable sort. maybe i’m wrong. My first thought was that it didn’t guarantee that dont-care elements remain in the given order.
but I didn’t try it to see if my suspicion was justified.
I'm relying on the docstring here: > Guaranteed to be stable: equal elements will not be reordered.
bravo!
if the initial elements (of each category) are already guaranteed to be sorted wrt each other, wouldn't reduce (or group-by) be computationally better? just O(n) over the input, no need to compare elements to each other if all we need to know is the target bucket. of course if there's just a few elements it doesn't really matter.
group-by is documented to maintain order as well in each bucket.
yeah, group-by is just reduce in a trench coat as they say 🙂
about the length of the seq. yes it’s short. I’m just sorting the headers of a csv file so that the interesting columns are on the left and the want-to-know-more columns are on the right
A closely related elegant solution is to take advantage of the way vectors of the same length are sorted. The key-fn can return a vector containing the wishful sortable factors in the order they matter in sorting.
How do Clojurians implement and track authorisation logic within their applications? I'm interested in best practices, recommended libraries, methodologies, general strategies, strong opinions
1. One of the resources I'm aware of is this: https://eacl.dev 2. Another is OAuth 2.1 to delegate authorization to your app (it decouples authorization provider from your app/resource server), but JWTs may be insufficient for dynamic or granular condition checks, hence unsuitable for deep authorization. 3. LDAP has been used to imply limited associations e.g. group membership, but unsuitable for deep authorization. 4. A ChatGPT lookup on authorization led me to https://www.openpolicyagent.org and AWS Cedar; https://www.cedarpolicy.com/en
I feel like the question should become more focused to increase the likelihood of answers that are useful to your projects. What is "authorization" in your case? What are "applications"? To give a specific example - a typical stateless CRUD web-app backed by some RDBMS, with multiple regular users and admins. Users can change only their own data, admins can change anything, including other users. Probably the simplest thing to do and also the one that makes the most sense is to rely on the DB for the authorization via RLS. Ideally, the DB should then be aware of users. But it works even if it's not. Another example, from a somewhat different world - an Android app that needs to request authorization to access data of the user of that phone. These two have nothing in common apart from the words "app" and "authorization". :)
we used the whole aws ecosystem for handling auth recently since our servers are on ec2. we had to grant people different levels of access to our web app, so we generated an aws cloudformation template in clojure for setting up a load balancer with an https url, purely just for forwarding requests to our normal server with authenticated jwt tokens, redirecting to Cognito for login, and creating all the dns records, ssl certs and security groups. our server handler then checks the username and groups on those jwt tokens after verifying them, then every top-level endpoint of our app is restricted using custom policy expressions over those jwt claims.
maybe also useful to look at the public parts of how metabase handles auth, which I think is here: https://github.com/metabase/metabase/tree/master/src/metabase/auth_identity
I rely on https://www.keycloak.org/ to perform authentication. Access to specific resources is managed via a separate module that checks Keycloak’s access tokens. It works well enough and provides a lot of flexibility when you need to integrate the system with the customer’s LDAP or SAML flows. There exists a good-looking https://github.com/jgrodziski/keycloak-clojure to work with Keycloak in Clojure. I don’t use it personally, but its documentation is very good, and I probably will give it a chance in my next project. It has good examples of different types of authorization implementation.
We implemented support for both Google and Microsoft SSO and store time limited sessions in RDBMS. The downside is that this could complicate role-based authorisation if domain-based is not granular enough and you may have to implement roles by yourself. Albeit it is possible to federeate roles from the aforementioned SSO's but the problem is that then the roles are not in your control. Our app is "just" a SPA and functions in Google Cloud Run.
Is there a way to suppress compiler warnings in a dependent library that I don’t control? For instance, I’m getting:
WARNING: update-vals already refers to: #'clojure.core/update-vals in namespace: clojure.tools.analyzer.utils, being replaced by: #'clojure.tools.analyzer.utils/update-vals
WARNING: update-keys already refers to: #'clojure.core/update-keys in namespace: clojure.tools.analyzer.utils, being replaced by: #'clojure.tools.analyzer.utils/update-keys
WARNING: update-vals already refers to: #'clojure.core/update-vals in namespace: clojure.tools.analyzer, being replaced by: #'clojure.tools.analyzer.utils/update-vals
WARNING: update-keys already refers to: #'clojure.core/update-keys in namespace: clojure.tools.analyzer, being replaced by: #'clojure.tools.analyzer.utils/update-keys
WARNING: update-vals already refers to: #'clojure.core/update-vals in namespace: clojure.tools.analyzer.passes, being replaced by: #'clojure.tools.analyzer.utils/update-vals
WARNING: update-vals already refers to: #'clojure.core/update-vals in namespace: clojure.tools.analyzer.passes.uniquify, being replaced by: #'clojure.tools.analyzer.utils/update-vals
I think this is from a library that I’m depending on which depends on an old version of core.async.
I also have a reflection warning from another library. In my own code, I’d suppress it by setting *warn-on-reflection* to false, but I can’t do that in the library file in question. I’m working on a CLI-based program and whenever I start it from the CLI, I get these warnings spit out before anything else.If those warnings are gone in a more recent version of c.t.a.*, you could add that as a top-level dep in deps.edn and you'd be using that newer version instead of the transitive version.
(assuming no breaking changes)
Ah, good point. Let me try that. Any ideas for how to suppress the reflection warning?
It isn't a reflection warning BTW.
The ones I pasted in were not. There is another that is.
I didn’t include that one.
Okay.
Specifically:
Reflection warning, meander/util/epsilon.cljc:758:24 - reference to field val can't be resolved.Thanks, @seancorfield. I added the latest core.async version to my deps.edn and the replacement warnings went away. I always forget that I can force everybody to use the latest version if I specify the latest in my own deps.edn.
I remember that meander warning -- we only use it in tests I think -- and we're using 0.0.650 but I haven't double-checked that warning recently. It has certainly been addressed in the source (several years ago).
Yea, I’m using version 0.0.650.
there is no way to suppress this warning other than using a newer version of the library that addresses it
Thanks Alex. What about suppressing the reflection warning, or is that the one you're talking about?
I'm talking about the var "already refers to:" message
re the reflection warning, that is off by default, so the question is - where is it being turned on? depending on the answer, you might be able to address
looks like it is being turned on https://github.com/noprompt/meander/blob/74de6b1f651441092cc12d1c9012ef7086033040/src/meander/util/epsilon.cljc#L12- so probably can't easily address it from outside
which looks like maybe it was fixed but not released https://github.com/noprompt/meander/issues/230 ?
Right, it's in Meander itself. I can use the git SHA to grab the latest.
Hmm, yeah, we should probably do that do (but we only it in one file of tests and we don't generally care about reflection in tests)...