integrant

2018-09-25T13:06:27.000100Z

Hello integrant folks! I’m struggling trying to use a different impl of a “component” depending of env… Simple use case: I have a mailer component for production environment and a dev mailer component that only print the message … This mailer component acts as a dependency for other components … in https://github.com/stuartsierra/component library this is really easy to achieve, but I don’t know which is the preferred/existent approach using integrant I’ve looked to the docs with no success 😞 Thanks in advance!

2018-09-25T13:48:55.000100Z

@tangrammer I have this exact scenario! I’m using Integrant with Duct, but what Duct does is have a dev.edn config that’s included/merged in when in development mode. So I override the component and override any dependent keys references.

2018-09-25T13:50:48.000100Z

Another way of doing it is have a component that reads in the environment, and then have the mailer component depend on that one, and switch dev/prod impl based on the environment component dependency.

grierson 2018-09-25T13:59:47.000100Z

Guessing here. I think you create your config.edn {:component {:mailer #ig/ref :mailer}} then (derive :mailer :prod/mailer) (derive :mailer :test/mailer) then merge your test.edn config to overwrite you config.edn.

2018-09-25T15:19:53.000100Z

@dadair could you expand that approach “So I override the component and override any dependent keys references.” ?

2018-09-25T15:21:33.000100Z

@grierson I’ll give a try to that approach too 🙂

2018-09-25T15:28:17.000100Z

Sure, so I have a config.edn with:

:mandala.lib.email/email-fn {}

 :mandala.lib.email/postman
 {:ssl      true
  :host   ...
  ...
  :email-fn #ig/ref :mandala.lib.email/email-fn}
and I have a dev.edn with:
:mandala.lib.email/email-fn-dev {}
 :mandala.lib.email/postman
 {:email-fn #ig/ref :mandala.lib.email/email-fn-dev}
The merging of the config files is handled by Duct via :duct.core/include ["mandala/config"] in dev.edn and by the following in a dev.clj namespace:
(defn read-config []
  (duct/read-config (io/resource "dev.edn")))

(integrant.repl/set-prep! (comp duct/prep read-config))
So in development mode the :email-fn key is referencing a dev-mode console printer, while in production (via standard configuration in config.edn that isn’t overridden in prod) the :email-fn key referencing a true mailer.

2018-09-25T15:31:00.000100Z

Without using Duct, you could have a standard integrant component that returns the environment (something like (defmethod ig/init-key ::env [k opts] (System/getenv "..")) then you can pass that component into the mailer (defmethod ig/init-key ::mailer [k {:keys [env]}] (if (dev? env) dev-mailer prod-mailer)).

2018-09-25T15:32:38.000100Z

thanks for the example! I’ll try it right now 🙂

2018-09-25T15:43:00.000100Z

@dadair in production environment will I have email-fn-dev and email-fn in the system ??

2018-09-25T15:43:27.000100Z

so both will be initialised too?

2018-09-25T15:44:13.000100Z

In production you would only read the “config.edn” file so you would only have “email-fn” (if you set it up that way)

2018-09-25T15:48:42.000100Z

good point 🙂 but in development, will i have both?

2018-09-25T15:50:30.000100Z

Yes, though the production key wont be referenced/used. That’s why I extracted the function that performs the side effect, it might be created but wont be called

2018-09-25T15:51:09.000100Z

If email-fn performed some side effect at init-time, you would have a potential problem

2018-09-25T15:52:25.000100Z

@grierson besides the derivation stuff …

(derive :app.emailer/dev-emailer :app.emailer/emailer )(derive :app.emailer/prod-emailer :app.emailer/emailer )
,what do i need to add to dev.edn and/or config.edn? I’m getting IllegalArgumentException No method in multimethod 'init-key' for dispatch value: :app.emailer/emailer clojure.lang.MultiFn.getFn (MultiFn.java:156) 😬

2018-09-25T15:54:07.000100Z

yep :thinking_face: I think it should exist a clear and straight forward approach …

2018-09-25T15:55:15.000100Z

The easiest/simplest approach is passing the env into the single component definition and use that to configure the mailer appropriately for that env

2018-09-25T15:55:46.000100Z

Fairly straightforward

grierson 2018-09-25T15:55:50.000100Z

I was wrong you don't need to have the :app.emailer/emailer key in your config.

2018-09-25T15:56:31.000100Z

2018-09-25T15:56:53.000100Z

but in this case i can’t keep my dev things aside my prod things … don’t i ? :thinking_face:

grierson 2018-09-25T15:58:15.000200Z

{:component {:mailer #ig/ref :mailer}} (derive :mailer :app.mailer/mailer)

grierson 2018-09-25T15:59:27.000100Z

or (derive :mailer :app.mailer/test-mailer) for test

grierson 2018-09-25T15:59:54.000100Z

the :mailer just acts as indirection so you can swap out the live and test version of your mailer.

2018-09-25T16:00:43.000100Z

Thanks again! (I’ll try in a few 🙂 )

2018-09-25T16:01:06.000100Z

@dadair thanks a lot for your help!! I have to leave now 🙂

2018-09-25T16:01:22.000100Z

In this case you are mixing, sure, but it’s still functional/transparent as it’s being passed the environment.