👋:skin-tone-4:
Hey guys, I'm looking to send emails with Clojure. I'm new to the Java eco-system. I saw that there is this clojure lib (postal : https://github.com/drewr/postal), which wraps Jakarta Mail. Anyone have an opinion on Postal or Jakarta Mail ? Thanks 😉
Another one to evaluate is <https://github.com/toyokumo/tarayo>
it’s pretty simple.
However, for the moment, I’m mostly sending emails via Amazon SES. Works great (with the cognitect aws libraries)
.
Given a collection like:
[{:lname "ln", :fname "First user", :email "<mailto:x.y+1@domain.com|x.y+1@domain.com>", :mobile "+1111111121", :secondarychannels [{:m "email", :mv "<mailto:x.y+first1@domain.com|x.y+first1@domain.com>"} {:m "email", :mv "<mailto:x.y+first2@domain.com|x.y+first2@domain.com>"} {:m "sms", :mv "+1111111111"}]} {:lname "ln", :fname "Second user", :email "<mailto:x.y+3@domain.com|x.y+3@domain.com>", :mobile "+1111111112", :secondarychannels [{:m "email", :mv "<mailto:x.y+second1@domain.com|x.y+second1@domain.com>"} {:m "email", :mv "<mailto:x.y+second4@domain.com|x.y+second4@domain.com>"} {:m "sms", :mv "+1111111131"}]}]
I want something like: {:emails [“<mailto:x.y+1@domain.com|x.y+1@domain.com>” “<mailto:x.y+first1@domain.com|x.y+first1@domain.com>” “<mailto:x.y+first2@domain.com|x.y+first2@domain.com>” “<mailto:x.y+3@domain.com|x.y+3@domain.com>” “<mailto:x.y+second1@domain.com|x.y+second1@domain.com>” “<mailto:x.y+second4@domain.com|x.y+second4@domain.com>”], :sms [“+1111111121” “+1111111111" “+1111111112” “+1111111131"]}
There are multiple ways to do this and I have a working approach of first gathering all the emails and then gathering all the mobiles and merging them together. I’m looking for an alternate solution that reads like:
Pseudocode:
let x = {:emails [] :sms []}
for every element in collection->secondarychannels, if m == email, append mv to emails collection (in x), else append mv to sms collection (in x).
What is a good way to write the same in Clojure?@mail.acharyarahul.now Disclaimer: I’m also just a beginner/intermediate Clojure programmer.
But have a look here: https://clojuredocs.org/clojure.set
My instinct is that you can define your collection as a set of maps (AKA kind of like tagged tuples in relational algebra).
And then you want to use a combination of project
select
and index
to achieve what you are doing. My assumption is that your code becomes a bit clearer, more declarative that way. To get the hang of it I suggest you open a REPL and just play around with these functions a bit and some example data (that’s what I would do).
I also happened to look into project
, select
and index
on clojuredocs when I saw these comments. I wonder if there's a blog post/tutorial introducing these to people unfamiliar with SQL/relational algebra...
@regen SQL is based on relational algebra. I learned it with the book Datenbanktheorie H.Buff (german). The basic operations and theory are rather simple. It’s based on set and tuple data structures. I’m not aware of an online resource, but I recommend learning the general first (relational algebra) and then apply it to SQL and/or Clojure’s sets.
Thanks for the info! I can't read german, but I'll try and find a book on relational algebra.
@denis.baudinot, thanks a lot for pointing to the helpful resources. Will try out playing around with those commands. A few questions though: > AKA kind of like tagged tuples in relational algebra Not really aware of a concept of “tagged tuples” in relational algebra. Can you point to relevant examples?
@mail.acharyarahul.now what I meant is just tuples. The tagging occurs by giving each position a key so to say. You can think of a map (or record, row etc.) as a tuple, but with names for each position. There must be a better word for it, but I don’t remember it, so I just said “tagged tuples”. Similarly, a vector (mathematics) has positional semantics like (23, 7, 42); but we sometimes say x, y, z to refer to each position in the vector. Again, probably there are better words for this as well :)
@denis.baudinot, got it. Thanks!
you could group secondarychannels by :m
and then use the values I believe
There’s a library called meander that was written for this sort of thing. If you’re new to Clojure it may or may not be a good fit, but you might be interested anyway. https://github.com/noprompt/meander
Your pseudocode seems to easily translate to a reduce
I played around a bit for fun and ended up with with this:
(->> stuff
(mapcat :secondarychannels)
(reduce (fn [agg {k :m v :mv}]
(update agg (keyword k) conj v)) {}))
where stuff is your original structureUnless you specifically need the ordering, sets seem like a better semantic fit for your two resulting collections.
I agree with @simongray on that the set would be the right data structure for these kinds of operations. There are std functions for sets, like select, join and so on, which seem to be the right fit for the transformations you want to do.
I mean, what you're building here IS an index and this IS a relational database
Anyone familiar with clojure.java-time? I’m having trouble finding the right function in the docs to convert a string timestamp to a more readable date-time format.
Given a timestamp in ISO-8601 "2020-11-22T19:00:01.000939-06:00"
, how would I be able to convert it to something like “Nov. 22, 2020 07:00 PM” or even “2020-11-22 7:00 PM”?
the "format" and "formatter" from this ns are imported into the primary ns, and are made for this sort of thing https://github.com/dm3/clojure.java-time/blob/master/src/java_time/format.clj
I have a hunch this library makes more sense if you are familiar with the underlying classes it wraps
(which kind of begs the question regarding using the library...)
there is a simple looking example in the README though
(format "MM/dd" (zoned-date-time 2015 9 28))
=> "09/28"
Thank you. I’ll try out format
Hi everyone, I'm trying to get started with compojure (after a few years' hiatus), and I'm having a hard time getting route/resources
to work. I'm not even able to get <http://clojure.java.io|clojure.java.io>.resource
to work:
mkdir resources
echo 'hi' > resources/x
clojure -e "(require '<http://clojure.java.io|clojure.java.io>) (prn (<http://clojure.java.io/resource|clojure.java.io/resource> \"x\"))"
nil
clojure 1.10.1 for reference
have you tried using the a name.extension convention? https://docs.oracle.com/javase/8/docs/technotes/guides/lang/resources.html#res_name_context
Yep, if I use the filename hello.txt
as in the clojuredocs example (https://clojuredocs.org/clojure.java.io/resource) I still get nil
I think I got it, I wanted route/files
instead of routes/resource
. I'm not sure why, I guess I don't really understand resources
If you use route/files
, you'll find your app won't work when you build an uberjar and run it (via java -jar
).
I see, so that's more for accessing files on a target system then? So how do I get resource to work?
You need resources
on your classpath to be able to pick up files from it. That's why running clojure
like that isn't seeing it.
Ok, I'm just using tools.deps, not lein, so I assume that's something lein-ring handles for you? So it's probably just a flag I pass to clojure when I run my program
(! 729)-> mkdir jstaab
(! 730)-> cd jstaab/
(! 731)-> mkdir resources
(! 732)-> echo 'hi' > resources/x
(! 733)-> clojure -e "(require '<http://clojure.java.io|clojure.java.io>) (prn (<http://clojure.java.io/resource|clojure.java.io/resource> \"x\"))"
WARNING: When invoking clojure.main, use -M
nil
(! 734)-> clojure -Sdeps '{:paths ["resources"]}' -e "(require '<http://clojure.java.io|clojure.java.io>) (prn (<http://clojure.java.io/resource|clojure.java.io/resource> \"x\"))"
WARNING: When invoking clojure.main, use -M
#object[java.net.URL 0x34a1d21f "file:/Users/sean/clojure/jstaab/resources/x"]
^ like soOr put a deps.edn
file in that directory that has :paths ["resources" "src"]
(assuming you also have a src
folder)
neat, I'll try that
That did the trick! Thanks Sean, you remain the best.