3

I want to get a list of public functions from another namespace so they can be exposed as commands.

A similar question for Clojure seemed close, but does not seem to work on ClojureScript.

Another question has answers for Clojurescript, but they either only show how to print them to the REPL, or return all members instead of only the publicly exposed ones.

shader
  • 801
  • 1
  • 7
  • 25

1 Answers1

3

ClojureScript does not have a resolve function. It is possible to mimic the behavior using hacks, e.g., this one:

(defn ->js [var-name]
  (-> var-name
      (clojure.string/replace #"/" ".")
      (clojure.string/replace #"-" "_")))

(defn invoke [function-name & args]
  (let [f (js/eval (->js function-name))]
    (apply f args)))

The second question you linked has an answer that refers to the clojure.repl/dir function which prints

a sorted directory of public vars in a namespace.

If you can print them, you can turn them into a string with with-out-str.

Now let's assume we have a namespace called demo.core with one public function called add:

(ns demo.core)

(defn add [a b]
  (println "hello from add")
  (+ a b))

We can retrieve the public fns from demo.core as strings as follows:

(defn public-fns
  "Returns coll of names of public fns in demo.core"
  []
  (as-> (with-out-str (clojure.repl/dir demo.core)) public-fns
    (clojure.string/split public-fns #"\n")
    (map (fn [s] (str "demo.core/" s)) public-fns)))

So with with-out-str we turn them into a list of strings, then split on newline, then prepend the names of public functions with "demo.core".

Then, using our earlier created invoke function, we can obtain add and invoke it with the arguments 1 and 2:

(invoke (first (public-fns)) 1 2)
;; "hello from add"
;; => 3

This is all very hacky, and it might break in advanced compilation, but it works.

user2609980
  • 10,264
  • 15
  • 74
  • 143
  • It looks like `clojure.repl/dir` prints _all_ of the public vars in the namespace. How would you further filter down to just the functions? – shader Jul 12 '17 at 20:47
  • 1
    Take a look at the code of [`cljs.test/function?`](https://github.com/clojure/clojurescript/blob/r1.9.671-52-g3e775a1/src/main/cljs/cljs/test.cljc#L19-L23) and [`cljs.analyzer/named-public-vars`](https://github.com/clojure/clojurescript/blob/d2711a2f222e56bdfa698610a59167de4124789d/src/main/clojure/cljs/repl.cljc#L1222). Maybe that will help you. I cannot get it to work now. – user2609980 Jul 13 '17 at 16:24