Fork me on GitHub

Hello, I need some help with code structuring. I'm trying to implement API abstraction for my client app. And my question is: what should I use to generate groups of functions? For now, I consider Protocols and macros. But I'm not confident in that, so your advice is welcome. Above is my current implementation using maps.


you can just create a namespace with those functions and define a function that gives you an api client


(ns client.api.core) (defn api-client [base-url] {:url base-url}) ;; or whatever you want (defn create [client] ...)


which is kind of like defining a protocol and implementing it with a record, only with less clutter


you get a Connection, and then the functions have that arg


Thanks, interesting idea. But there one more question: in case of HOF how to return multiple functions?

Alex Miller (Clojure team)14:11:08

a protocol is mostly implemented as a map of functions


ok, I'll look at protocols closer


Hi, I'm using clj-memory-meter lib in my app and it works fine until I deploy my uberjar on openjdk:8-jre-alpine image. I then run into class not found errors for that lib. It appears to require some jdk agent tool. My question: is there an alternative for measuring object size that would work with simple jre image?


have you tried jvisualvm?


Which library do you guys use to read Excel files? Nothing other than docjure seems to be actively maintained.


I have used docjure with success.

👍 4

Can anyone point me to a tutorial for setting up a clojure project from scratch using deps.edn? I want to start a basic api project using compojure and use deps.edn. Looking at more advanced setups like juxt/edge is quite overwhelming and i'm looking for something simpler to learn from.


how far along are you? have you seen this?


if you want something simple, what's the motivation specifically for using deps.edn?


@tapegram I did see that but didn't find it too helpful. Thanks though.


@hiredman That looks great. Thanks

Mario C.19:11:40

Do println's cause performance issues?

Mario C.19:11:14

or are they negligible? or does it depends on the size of the content that you are printing out?


It costs non-0 time to perform one. How much depends on what is being printed, really. Whether it causes an issue, only you can say, based on your performance target.


It typically takes longer to print out 100 characters than 1, but even for the same number of characters, some "to string" converters for some types may be more compute-intensive than others.


Are you familiar with JVM and/or Clojure run-time profiling tools like Criterium? It can help you get some measurements for such things, if you are curious.

👍 4
Mario C.20:11:10

How can I differentiate from two different postgres exceptions? By getting the message and comparing the string values?


maybe, the exception types are likely different as well

Mario C.21:11:07

They are both of type org.postgresql.util.PSQLException

Mario C.21:11:41

One is duplicate key value violates... and the other is null value in column 'id' violates


do they have nested exceptions?


(one can check with .getCause)


hilariously, java.sql.SQLException introduces .getNextException


which is likely to be what you want in that case instead of .getCause

😒 4

oh, good catch

Mario C.21:11:43

I get nil value on .getCause and .getNextException

Mario C.21:11:59

I think just comparing the cause is really the only way

Mario C.21:11:24

wish the exception would have a unique code. That way I would compare that instead of a string


I don't know if they are useful in practice, but there are getErrorCode getServerErrorMessage and another one called getSQLState


with any luck the int ErrorCode is different for each case


ServerErrorMessage seems to hold a lot of data, potentially

Mario C.22:11:09

Thanks guys!


and based on something I just saw in my email I now wonder if we are coworkers, haha

Mario C.23:11:04

lol highly doubt it unless you work for a loan servicing company


lending platform

Mario C.23:11:40

oof close! was the email about postgresql exceptions?


yeah -honeybadger alerts from a runtime error in prod

eccentric J22:11:49

How do I compose multiple routes? Currently we supply a vector of (home-routes) but how do I compose the router with both home-routes and a similar function called api-routes in reitit?


@jayzawrotny since it's returning a vector to represent the routes, couldn't you use into to pull one set of routes in after the other?

eccentric J22:11:52

Like (into (home-routes) (api-routes))? I’ll try it! I got some success using (conj (home-routes) (api-routes)) but I’m not sure what I would do if I had another file of routes.


conj would make the new routes a vector inside the other vector, instead of combining the vectors


I'm not sure how your routing library handles this

eccentric J22:11:58

Interestingly into didn’t work

eccentric J22:11:39

This is the output of calling (conj (home-routes) (api-routes)) which is working


ahh, so api-routes returns a single subtree, conj makes sense in that case

eccentric J23:11:05

ok cool, what should I do if I had multiple route files like api-routes or home-routes?

(reduce conj [] [(home-routes) (api-routes)])
is what comes to my mind first


conj is already varargs


also that doesn't give you the structure you had before - you end up with extra [] around the whole thing (that may or not matter)


but really If home-routes is the "top" and everythig else is a child, why not (conj (home-routes) (api-routes) (foo-routes) (bar-routes))

eccentric J23:11:39

Oh, way more simple! Thanks.

eccentric J23:11:27

In general is one preferred over the other? (:id data) and (data :id)?


they behave differently if data can be nil


if either the map or the key is defined as a literal, I prefer to put that one first


otherwise get, unless it's well understood the type of the thing doing lookup

eccentric J23:11:20

That’s a good point, will switch to get.