weavejester / integrant

Micro-framework for data-driven architecture

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

#ig/ref should allow for deep references

ieure opened this issue · comments

Say that I have some global configuration which I want to reference later:

{:my.core/config {:my.core/threads 8
                  :my.core/retries 3
                  :my.core/whatever :bar}
 :my.http/config {:timeout           5
                  :threads           ...
                  :default-per-route ...}}

I need the global threads value inside the :my.http/config map, but there's no way to express this, because #ig/ref only allows referencing top-level keys. This seems to leave two options, both of which seem unnecessarily complex:

Break up my.core/config into separate top-level keys. For example:

{:my.core/threads 8
 :my.core/retries 3
 :my.core/whatever :bar
 :my.http/config {:timeout           5
                  :threads           #ig/ref :my.core/threads
                  :default-per-route #ig/ref :my.core/threads}}

This is irksome, because I now have to define a bunch of ig/init-key methods for these keys, instead of one, which handles a map.

Compute the config in ig/init-key. This requires overly complicated implementation of the :my.http/config (and anything else needing global config into) ig/init-key method. For example (roughly):

{:my.core/config {:my.core/threads 8
                  :my.core/retries 3
                  :my.core/whatever :bar}
 :my.http/config {:core #ig/ref :my.core/config
                  :timeout           5}}

(defmethod ig/init-key :my.http/config [_ {{:keys [:my.core/threads]} :my.http/core :as config}]
  (-> config
      (assoc :threads threads)
      (assoc :default-per-route threads)
      (dissoc :my.http/core)))

Aero is able to ref arbitrary paths inside the EDN, but it can't be used with Integrant, since both libs have different reader macros. If the EDN contains #ig/ref, Aero fails to read it. The function-call form (ig/ref ...) isn't evaluated, so lands in the config verbatim. Leaning solely on Aero's #ref breaks Integrant's resolution -- the literal referenced config lands in the output, rather than the initialized output.

The first approach seems like the least bad option. But I think that the real solution is for Integrant to support Aero-style deep references in #ig/ref.

Integrant has a fairly narrow scope; it's not intended to be a generic configuration library, or to solve problems outside of managing dependencies.

You can use Aero with Integrant by adding an additional reader. I'm not too familiar with Aero, but something like this may work:

(defmethod aero/reader 'ig/ref    [_ k v] (ig/ref k v))
(defmethod aero/reader 'ig/refset [_ k v] (ig/refset k v))