0
votes

Good day..I'm using vertx and it's very enjoyable but for event bus communication it requires a lot of boilerplate and even worst, if I need change some function name I need change it in 4 or 5 places differents..

just know, I'm using an abstraction for transform the vertx callbacks to channels..like this

;;helper
(defn send [add msg]
   (let [ch (chan 1)]
      (eb/send add msg #(put! ch %))))

;;I wrap the event bus sender in a function for make a bit cleaner the code
(defn eb-get-cached-view [name id]
   (send "render:get-cached-view" [name id])) ;;return chan[response]

(eb-get-cached-view "something" "here");;and finally I use it

;;in other verticle, I write the funcion
(defn get-cached-view [name id]
   (...))

;;and enable an event bus listener pointing to that function
;; basically I receive a vector, pass it to my function, which return a channel, then I reply to the message with the response
(eb/on-message "render:get-cache-view" #(go (eb/reply (<! (apply get-cached-view %)))))

as you can see it's a lot of boilerplate and I'm afraid every time than I change the function name I could miss change it in some point and my code fails, the on-message argument I follow the convention of use my namespace follow by ":" and the function name

I was thinking than probably would be good something like this

(defbus blah
        [name]
         (str "hi! " name))

this macro build the function blah and create a bus listener for messages with the "render" namespace (can I access to the namespace inside a macro? is good approach?)

(eb/on-message "namespace:blah" #(go (eb/reply (<!(apply get-cached-view %))))) ;;apply is necessary because the messages are received inside a vctor

and a macro for send the message

(<eb namespace:blah "john Smith") ;; could be a key like :namespace:blah too

than translate to something like this

(defn namespace:blah [message]
   (let [ch (chan 1)]
      (eb/send "namespace:blah" message #(put! ch %)))) 

(namespace:blah "john Smith")

with these macros I would avoid boilerplate,change callbacks for promise or avoid inconsistencies in the function names...

is it a good approach? is possible write these macros or I'm ignoring some point?..I'm a bit newbie with clojure and I don't know if it's possible and why it doesnt exist yet...

I appreciate any help and maybe correction of my code (I wrote the code by heart)

----------EDIT 1----------------------------------------------------------

I wrote this macro (thanks to Arthur)...it's a bit simpler than the final macro only for demo

(defmacro defbus [bus-name args code]
   `(let [fun# (fn ~args ~code)]
       (println (str *ns* ":" ~bus-name))
       (eb/on-message (str *ns* ":" ~bus-name) (fn [arg#] (apply fun# arg#)))))

I think than the macro-expansion is pretty well...but when I try use it I get

(defbus blah [a b] (str "just " a " " b))

java.lang.RuntimeException: Unable to resolve symbol: blah in this context

seems than I can use blah directly but a simbol or string...Am I missing something? (I'm a newbie with macros and all the examples are very different to what I need) thanks!...

1

1 Answers

0
votes

You can access the namespace form anywhere in under the name *ns* In this case it would be convenient if your macro defined an eventbus using the namespace where the call is being made rather than the namespace where you define the helper function or macro.

user> (defmacro defbus [bus-name]
        `(eb/on-message (str *ns* ":" ~bus-name) :dostuff))
#'user/defbus

user> (macroexpand-1 '(defbus foo))
(eb/on-message (clojure.core/str clojure.core/*ns* ":" foo) :dostuff)

This macro will return the code for a call to on-message that will then be evaluated in the callers namespace where ns is bound to that namespace, which I'm presuming is what you want.