Fork me on GitHub
#clojure
<
2021-04-27
>
NoahTheDuke14:04:24

Reloaded Workflow question/discussion: I have moved my app to Integrant and like it a lot so far, but I'm starting to rub up against some pain points of "no globals"/"no implicits".

NoahTheDuke14:04:26

Previously, in my db.clj file, I had a function called connect that would defonce an object that contained the connection to the database. I would call this during initialization. Then I could freely import that db object into whichever namespace I needed it and use it directly in any functions. Felt very similar to how various other languages do database connections (at my job we use Knex and Objection.js, but Rails is similar).

NoahTheDuke14:04:18

Now I create the database connection in the system and it's held inside of the returned system object, which I then pass into my ring app (which further passes down into my websockets/sente handlers), so every function that uses the database has to destructure/`get-in` the db object from the request or it has to be passed in from one of those functions as a new arg. I have it all working, but it feels significantly more cumbersome than being able to simply require and use.

delaguardo14:04:47

the idea is to have ring app included into system, then your db can be simply added as a reference to constructor of ring app

NoahTheDuke15:04:28

That's how I have it:

(defmethod ig/init-key :web/app [_ {:keys [db]}]
  (make-app db))
and then
(defn wrap-db [handler mongo]
  (fn [req]
    (handler (assoc req :system/db (:db mongo)))))

NoahTheDuke15:04:44

and then the REST handlers look like this:

(defn login-handler
  [{db :system/db
    {:keys [username password]} :params}]
and the sente event handlers look like:
(defmethod ws/-msg-handler :lobby/delete-game
  [{{db :system/db
     {:keys [username isadmin ismoderator]} :user} :ring-req
    {:keys [gameid]} :?data}]

NoahTheDuke15:04:37

it's not bad, just cumbersome, you know?

dharrigan15:04:29

I have a similar setup with my applications (although I use JUXT Clip, not integrant). I pass into my ring handler the setup (that has been configured via juxt clip, which includes references to connections to the db etc). I then pass that "opaque deity" collection into each function that needs it (and passes it along if required).

delaguardo15:04:53

you can add all the handlers into the system and use db reference everywhere its needed

dharrigan15:04:03

I did think it was cumbersome, but you know what it brings? A very easy way to test things, you can construct the application configuration (map) to hold whatever values you want for testing...

dharrigan15:04:35

and you can isolate individual functions, passing into them just the configuration map data that is useful for them.

dharrigan15:04:11

I have an example of using juxt clip, a database and passing the data long here:

👌 2
delaguardo15:04:36

I don’t think one should use configuration map as is in any function outside of implementation for ig/init-key function

dharrigan15:04:41

the map is called app-config

dharrigan15:04:08

Here's an example of where I pull out the configured database connection to pass into next.jdbc for it to do its thaannng.

NoahTheDuke15:04:34

i thought about adding all of the handlers to the system, saw that in https://github.com/eeng/mercurius, but that felt like even more of a restriction than how it works currently

NoahTheDuke15:04:53

thanks for that repo, @U11EL3P9U, i'll check it out!

dharrigan15:04:10

sure, np. it's just my approach, I hope it helps, or maybe it doesn't 🙂

Michaël Salihi15:04:49

Maybe my Integrant / Reitit version of Sean Corfield's Usermanager example can help how I figure out the conn managment: https://github.com/prestancedesign/usermanager-reitit-integrant-example

thumbsup_all 3
Tommy DeVito15:04:08

@U11EL3P9U are you following the polylilth arch in your starship example?

NoahTheDuke15:04:33

@UFBL6R4P3 i read through that one when i first started this process! it wasn't super helpful because of how reitit and compojure differ, but I plan on moving my app to use reitit and will be re-reading your example repo when i do that

👍 1
dharrigan16:04:41

@UHCLEHF4N I've borrowed some things, but rejected others whilst I'm still evaluating it

dharrigan16:04:59

I'm not convinced yet by the approach

Tommy DeVito16:04:04

thanks :thumbsup:

NoahTheDuke18:04:40

thanks for the input, everyone. been thinking about this a lot recently and all of the responses and ideas are helpful

Michaël Salihi19:04:56

You're welcome. 👍

NoahTheDuke14:04:26

am i doing this correctly? Should it be working like this?

Helins18:04:20

It's true the docstring advertises a seq and not a list, yet this made me uncomfortable

(list? (list* [1 2]))

;; => false

alexmiller19:04:47

it's unusual to use list* and it's an old function so pre-dates some later rework of sequences that occurred

👍 1
alexmiller19:04:00

but it does what it says :)

borkdude20:04:22

I probably use list? less often than list* (you almost always need seq?)

ag21:04:25

I need to write a function. The header so far looks like this:

(defn update-entities
  "Updates maps in a coll by applying functions one by one to items in the
  coll. Each function takes an old value and returns new value of an
  item. Number of updated entities in the coll would always be equal to number
  of functions passed. The total number of maps in the coll, remains the same."
  [ents f & fs]
Every single idea I have right now about how to write the actual body of this function feels lame to me. Anyone wants to give it a shot? While my lazy and slow brain gestates a "perfect" solution.

walterl21:04:57

Are all functions applied to each map, or is f applied to (first ents), (first fs) to (second ents), etc.?

ag21:04:47

every function applied to a single element in the coll f1 to the first f2 to the second, and so on. if there are fewer functions that elements, remaining elements won't change

walterl21:04:32

As in (map #(%1 %2) fs ents)?

walterl21:04:44

It just won't satisfy that last part: > if there are fewer functions that elements, remaining elements won't change

Michael Gardner21:04:25

(map #(%2 %1) ents (concat fs (repeat identity)))

👏 3
walterl21:04:33

Ah, a positive use of concat. Nice, @U01R1SXCAUX 👍

ag21:04:56

Whoa. How did you even?... man, you folks are so awesome.

ag21:04:07

The actual mechanics requires fewer parts (even fewer parentheses) than the number of words in the docstring for the function. So cool.

walterl21:04:12

That's often a sign of a good docstring 😉

ag21:04:18

Yes, I'd like to be one of those coders who write more English and more tests and less the actual code that's meant for the execution.

alexmiller21:04:58

kind of a map juxt kind of thing