Component decorator
darkleaf opened this issue · comments
For example I have following system:
(def config
{::logger {}
::sms-sender {}
::component-1 {:sms-sender (ig/ref ::sms-sender)}
;; ...
::component-1000 {:sms-sender (ig/ref ::sms-sender)}})
(defmethod ig/init-key ::logger [_ _]
#(prn :log %))
(defmethod ig/init-key ::sms-sender [_ _]
#(prn :sms %))
(defmethod ig/init-key ::component-1 [_ {:keys [sms-sender]}]
(fn []
(sms-sender "Hi! I'm 1")))
;; ...
(defmethod ig/init-key ::component-1000 [_ {:keys [sms-sender]}]
(fn []
(sms-sender "Hi! I'm 1000")))
And I want to add logged-sms-sender
component that logs every sms. I can replace (ig/ref ::sms-sender)
with (ig/ref ::logged-sms-sender)
1000 times but I don't want to do this.
Can I decorate a component without changing reference to it?
I'm not sure what you mean by "decorate" in this case, but if you want to replace ::sms-sender
with ::logged-sms-sender
, then all you need to do is:
(derive ::logged-sms-sender ::sms-sender)
Then ensure that ::logged-sms-sender
is the only key that derives from ::sms-sender
in your map.
"decorate" means to use decorator pattern.
I have defined sms-sender
and logger
components.
And I want to create a new logged-sms-sender
.
May be like this:
;; I guess this isn't work with integrant
(defmethod ig/init-key ::logged-sms-sender [_ {:keys [logger sms-sender]}]
(fn [msg]
(logger msg)
(sms-sender msg)))
Or I can use ugly get-method
:
(derive ::logged-sms-sender ::sms-sender)
(defmethod ig/init-key ::logged-sms-sender [_ {:keys [logger], :as params}]
(fn [msg]
(let [sms-sender ((get-method ig/init-key ::sms-sender) params)]
(logger msg)
(sms-sender msg))))
You don't need to use get-method
. You can just write:
(defmethod ig/init-key ::logged-sms-sender [_ {:keys [logger] :as opts}]
(let [sms-sender (ig/init-key ::sms-sender opts)]
(fn [msg]
(logger msg)
(sms-sender msg))))
If you want a generic logging "decorator" then you could write something like:
(defmethod ig/init-key ::logged [_ {:keys [logger fn]}]
(fn [& args]
(logger args)
(apply fn args)))
And then in your configuration you could write:
{::logger {}
::sms-sender {}
[::logged ::logged-sms-sender] {:logger #ig/ref ::logger, :fn #ig/ref ::sms-sender}}
I liked the first example. Thanks for idea.
But I don't understand second example.
{::logger {}
::sms-sender {}
::component-1 {:sms-sender #ig/ref ::sms-sender}
[::logged ::logged-sms-sender] {:logger #ig/ref ::logger, :fn #ig/ref ::sms-sender}}
::component-1
must get [::logged ::logged-sms-sender]
as dependency.
You'd need to rename the keys in my last example; either by changing the references or renaming ::sms-sender
key.