integrant

NoahTheDuke 2021-04-15T03:47:40.003700Z

hey all! i'm trying out integrant in my app and it's going well enough, but i have a question that's not answered by the docs: i want access to the object created by integrant/`init-key` so i can reference it in the rest of my app. is that possible?

NoahTheDuke 2021-04-15T03:48:19.004500Z

if not, should I bind the result to an atom and then (reset! state nil) in halt-key! ?

NoahTheDuke 2021-04-15T04:13:00.005100Z

oh, this is the returned object from init, not init-keys. nevermind, i have figured it out

NoahTheDuke 2021-04-15T13:27:38.006600Z

actually, follow up. my current app has a function connect which connects to the running mongo instance using an aero config file, and then uses defonce to create a db var with the returned mongo db connection

NoahTheDuke 2021-04-15T13:28:41.007700Z

and then in the meat of the app, that db var is required and used, which assumes that the connection has been made and the var has been defined, etc

imre 2021-04-15T13:44:39.008300Z

I'd avoid using defonce with integrant

👍 1
imre 2021-04-15T13:46:30.008800Z

or to be broader, I'd avoid global defs for state with integrant

NoahTheDuke 2021-04-15T13:49:17.009300Z

what is the more functional approach to write something like that?

imre 2021-04-15T13:50:01.010300Z

just return the state from an init-key

imre 2021-04-15T13:50:08.010700Z

and it'll be in the system map

imre 2021-04-15T13:50:40.011600Z

and if your other component needs it, #ref that in the config

NoahTheDuke 2021-04-15T13:51:56.012100Z

i'm sorry, i meant in a function like

(defn superusers []
  (mc/find-maps db "users" {$or [{:isadmin true}
                                 {:ismoderator true}
                                 {:tournament-organizer true}]}))

NoahTheDuke 2021-04-15T13:52:33.012700Z

the current codebase is filled with functions like this, that assume the connection exists in the db var

NoahTheDuke 2021-04-15T13:53:43.013900Z

is it better to pass the integrant system into all of these calls? or to assign it to a global variable and require it?

imre 2021-04-15T13:55:25.014500Z

If you need a quick first pass, I'd go with the former out of those two

imre 2021-04-15T13:56:10.015400Z

but you could make that fn a component itself sort of how handler/greet returns a function at https://github.com/weavejester/integrant#initializing-and-halting

NoahTheDuke 2021-04-15T13:56:25.015700Z

that page doesn't load for me

imre 2021-04-15T13:56:51.016200Z

apologies, wrong link

imre 2021-04-15T13:58:15.017400Z

or you could make a component that returns a reify over a protocol, wrapping your db and exposing all the db-needing functions

NoahTheDuke 2021-04-15T13:58:37.017700Z

hmmmm interesting!

imre 2021-04-15T13:59:01.018200Z

or some groupings of functions in between one-fn-per-component and all-db-fns-in-one

imre 2021-04-15T13:59:09.018500Z

that's suitable to your usecases

NoahTheDuke 2021-04-15T13:59:30.019Z

yeah makes sense

NoahTheDuke 2021-04-15T13:59:54.019700Z

i'll have to think about this a bunch, see what makes most sense

imre 2021-04-15T13:59:57.019800Z

the point of integrant is that you build up components that are self-contained enough so that other, dependent components only need to pass in args that are relevant to their usecases

imre 2021-04-15T14:01:10.021Z

You can eliminate most if not all global state this way as all of it will be inside the system map.

NoahTheDuke 2021-04-15T14:04:08.021500Z

yeah, that's definitely the goal cuz it's a ball of spaghetti right now

imre 2021-04-15T14:05:01.022600Z

hah, I've learned that not even integrant can guarantee spaghettilessness 😄

NoahTheDuke 2021-04-15T14:05:10.022900Z

23 different namespaces require that db var, which makes it "easy" but also very messy and means we have to be careful when writing tests or running things individually

imre 2021-04-15T14:05:45.023300Z

you can have one component per namespace for example

imre 2021-04-15T14:06:12.023900Z

each returning something that wraps its functions, requiring the db component

NoahTheDuke 2021-04-16T15:23:47.026700Z

by "wraps its functions", do you mean:

(defn example [{db :system/db}]
  (defn db-fn1 [arg]
    (do-something db arg))
  (defn db-fn2 [arg]
    (do-something db arg))
  ...)

imre 2021-04-16T15:42:53.027Z

Not really, more like:

imre 2021-04-16T15:44:03.027200Z

(defn example [{db :system/db}]
 {:db-fn1 (fn [arg] (do-something db arg))
  :db-fn2 (fn [arg] (do-something db arg))})

👍 1
imre 2021-04-16T15:44:33.027400Z

even better if you create a protocol defining the interface for fn1 2 etc

👍 1
imre 2021-04-16T15:44:41.027600Z

and reify that within example

NoahTheDuke 2021-04-16T15:47:28.028Z

thanks for the example, that's helpful

imre 2021-04-16T15:48:13.028200Z

you're welcome

NoahTheDuke 2021-04-15T14:06:51.024400Z

that's a neat trick. i realize i'm asking a lot, but do you have any projects/examples of that kind of logic?

imre 2021-04-15T14:08:29.025800Z

I'm afraid I don't have anything I'd be allowed to share. But integrant is a phrase specific enough that a github code search should give you some examples

NoahTheDuke 2021-04-15T14:15:19.026Z

coolio, thanks so much for the help

imre 2021-04-15T14:16:14.026600Z

Nb, good luck! It takes a little getting used to but it's worth it