Welcome! Please see the About page for a little more info on how this works.

+1 vote
in Libs by

hi folks, I am trying out Integrant library for state management. It's quite intuitive when it comes to managing (starting/halting) servers but I am confused regarding a certain use case.

I want to use integrant to instantiate and destroy an object. As usual I defined defmethods init-key and halt-key! . The problem is that I want a reference to the initialised object from init-key . Is there an idiomatic way to do this? Should I just have it available in an atom during init-key ?

(defmethod ig/init-key :my-space/my-key [_ opts]
  (let [account-id (:account-id opts)
        license-key (:license-key opts)]
    (MyObject. account-id license-key)))

;;How do I use the instantiated my object?

2 Answers

+1 vote
by

When you call (ig/init config), it calls ig/init-key in the dependency order of your config map and returns a map where each key is one of the defined init-keys. In your case, it will return {:my-space/my-key #<MyObject…>}. Depending on how you’ve structured your app, you can put this map into a var or an atom, or you can pass it as an argument to your ring middleware so every request has a reference, etc.

To call the methods of MyObject, you’d write (.someMethod (:my-space/my-key system))

0 votes
by

You use that :my-space/my-key as a parameter to another component, that component will then receive the instance of MyObject that got created.

For example, if you're using Integrant with Aero, it would look something like this:

;; my_space.clj
(defmethod ig/init-key ::my-key [_ opts]
  (let [account-id (:account-id opts)
        license-key (:license-key opts)]
    (MyObject. account-id license-key)))

(defmethod ig/init-key ::other-key [_ {:keys [my-object]}]
  (println "Got object" my-object))

;; config.edn
{:ig/system
 {:my-space/my-key    {:account-id  1
                       :license-key 2}
  :my-space/other-key {:my-object #ig/ref :my-space/my-key}}}
by
I want to use it outside defmethod. I want to use `MyObject` in normal functions.
by
Then it would be a misuse of Integrant where you pretty much throw the whole thing out of the window. You can supply those normal functions with an extra argument that comes from somewhere and has that object, you can create those function as closures in the `::other-key` component, you can make them a part of a protocol and return an implementing object from the `::other-key` component. So there are solutions to your problem, and creating a global singleton is not a proper one.
by
Not sure I understood the solution about "normal functions with an extra argument...can create those function as closures..". Can you point me to an example?
by
Every function has a caller. Instead of relying on some globally available singleton, you can make each caller pass an extra argument with the instance of `MyObject` to the functions that need it. Closures are a different approach where you close over a value, e.g. in `(let [x 1] (fn [] x))` that `fn` is a closure over `x`. In the `::other-key` component, you can create functions that close over that instance of `MyObject` without having to pass it as an argument.
by
I know what Closures are but I am not able to connect the dots here. So I want to use the methods of the object `MyObject` which was instantiated as part of integrant's `init-key`. Sorry if I am dragging this too long. I will try to read up more about how "components" are supposed to work. Thanks a lot for your patience!
...