Skip to content

Keeping init-key args around for a later halt-key! should be an implementation detail #25

@aroemers

Description

@aroemers

Hi @weavejester,

As discussed on Slack, we talked about how integrant's halt-key! method could have access to the original init-key arguments. It was the following example that spurred the discussion:

(defmethod ig/init-key ::datasource
  [_ {:keys [logger]}]
  (log logger :info ::starting.datasource)
  (hikari/make-datasource ...))

(defmethod ig/halt-key! ::datasource
  [_ datasource]
  (log ??? :info ::closing.datasource)
  (.close datasource))

How to access the logger at the ??? in halt-key? A solution would be to have the following:

(defmethod ig/init-key ::datasource
  [_ {:keys [logger]}]
  (log logger :info ::starting.datasource)
  {:datasource (hikari/make-datasource ...)
   :logger logger})

(defmethod ig/halt-key! ::datasource
  [_ {:keys [datasource logger]}]
  (log logger :info ::closing.datasource)
  (.close datasource))

However, in my opinion this exposes an implementation detail (::datasource wanting to access the logger, so the actual datasource is inside a map) to the "consumers" of this component.

We discussed three possible solutions.

  1. Adapting halt-key's arguments to something like [_ pre-init-value post-init-value]. Upside is, that this is fairly straightforward. Downside is, it's not clear how to do this without breaking backward compatibility. The example however would be changed to the following:
(defmethod ig/halt-key! ::datasource
  [_ {:keys [logger]} datasource]
  (log logger :info ::closing.datasource)
  (.close datasource))
  1. Adding an Unref protocol having an unref function, together with some helper functions. Integrant will call unref if init-key's return value satisfies the Unref protocol, before passing it to other components. For example:
(defrecord Datasource [datasource logger]
  Unref
  (unref [_] datasource))

(defmethod ig/init-key ::datasource
  [_ {:keys [logger]}]
  (log logger :info ::starting.datasource)
  (map->Datasource
    {:datasource (hikari/make-datasource ...)
     :logger logger}))

Or, with some helper function:

(defmethod ig/init-key ::datasource
  [_ {:keys [logger]}]
  (log logger :info ::starting.datasource)
  (ig/expose :datasource
    {:datasource (hikari/make-datasource ...)
     :logger logger}))

Benefits of this approach are backward compatibility and the developer can keep even more values than just the pre-init value.

  1. A workaround for now without changing integrant, by using the ::build metadata on a system.

As I also stated on Slack, I am just getting started with Integrant, so we agreed we would both ponder if this is an actual problem, and if so, what the best approach would be to solve it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions