Fork me on GitHub
#clojure
<
2021-04-27
>
Noah Bogart14: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".

Noah Bogart14: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).

Noah Bogart14: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

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

Noah Bogart15: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}]

Noah Bogart15: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:

👌 6
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.

Noah Bogart15: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

Noah Bogart15: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 9
Tommy DeVito15:04:08

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

Noah Bogart15: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

👍 3
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:

Noah Bogart18: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. 👍

Noah Bogart14:04:26

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

Vincent Cantin18:04:30

I found a problem with update-in : it doesn't behave as one could expect when ks is [] In its implementation, it is assumed that ks contains at least 1 key. However, the documentation of the function doesn't mention it.

Vincent Cantin18:04:57

I noticed that assoc-in also assumes at least 1 key in its implementation. I guess that there is an implicit expectation that the user doesn't use them with no keys in ks .

thumbnail18:04:29

What would you expect update-in to do if ks is []?

Vincent Cantin18:04:26

passing m directly to the function in parameter.

Vincent Cantin18:04:40

Instead, it passes nil

Alex Miller (Clojure team)18:04:22

use update in this case

Vincent Cantin18:04:40

@U064X3EF3 I ran on this situation by having a computed value for ks.

Vincent Cantin18:04:18

I will write my own version of update-in to support this case, but I wanted to report the issue, still.

Alex Miller (Clojure team)18:04:40

it's been reported and declined in the past

👌 5
Vincent Cantin18:04:33

Yes, that's exactly this.

Vincent Cantin18:04:54

Maybe the docstring of update-in could mention that the path requires at least 1 segment.

andy.fingerhut19:04:44

It isn't as official as the built-in doc strings, of course, but checking whether there is an example or note about this on community-contributed docs page on http://ClojureDocs.org , and adding a new one if there is not already, would be good: https://clojuredocs.org/clojure.core/update-in

andy.fingerhut19:04:50

And anyone can do that.

👍 3
Adam 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

Alex Miller (Clojure team)19:04:47

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

👍 3
Alex Miller (Clojure team)19: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)))

👏 9
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.

Alex Miller (Clojure team)21:04:58

kind of a map juxt kind of thing