We have some boiler-plate Cljs code that does something like this:
(defn mui->reagent [mui-name]
(r/adapt-react-class (gobj/get js/MaterialUI mui-name)))
(def app-bar (mui->reagent "Button"))
(def card (mui->reagent "Card"))
....
Basically it introduces a def
for every js/MaterialUI
component and there are lots of them. I thought that could be cleaned up using a macro and wrote something like this in a .cljc file:
(defn react-import [tag]
`(def ~(symbol (str "my-"(->kebab-case tag)))
(reagent.core/adapt-react-class (goog.object/get js/MaterialUI ~tag))))
(defmacro import-mui [tags]
`(do ~@(map react-import tags)))
And it works if called like this:
(import-mui ["Button" "Card" "Checkbox"])
That will introduce 3 defs: my-button
, my-card
, my-checkbox
. So, you can see that it works but only half way. I thought: “What if I retrieve the list of the components from MaterialUI object”? Something like:
(require '[my-mui.components :refer-macros [import-ui]])
(import-mui (filterv capitalized? (js/Object.keys js/MaterialUI))
;; you need to use only capitalized keys,
;; how `capitalized?` function works it's not important here
But that doesn’t work for some reason, it simply doesn’t want to feed the list in there. If I take the same list produced by (filterv capitalized? (js/Object.keys js/MaterialUI))
, put it in a vector and feed to the macro - it works. But it doesn’t work when done like above.
So I thought what if I do it inside the macro, but I can’t make that work either.
For the sake of simplicity, let’s forget about MaterialUI and React and simplify things a bit:
Let’s create two files, my_ns.cljc
and my_ns.cljs
, in .cljc file:
(defn new-def [t]
`(def ~(symbol (str "my-" t))
~t))
(defmacro try-me [tags]
`(do ~@(map new-def tags)))
in .cljs file:
(requre '[my-ns :refer-macros [try-me]])
(set! js/window.GlobalList #js {"One" {} "Two" {} "Three" {}})
Now, if I eval (try-me ["One" "Two" "Three"])
- it works. It creates 3 defs. But If I try:
(try-me (js/Object.keys js/GlobalList))
It wouldn’t. It throws an exception with the following message:
Can't def ns-qualified name in namespace my-(js at line 1 <cljs repl>
So why is that? Why this doesn’t work?
What’s the fundamental difference between passing a literal vector vs. passing a computed vector into a macro? What am I missing?
Can someone help me out? Thanks!