String to Symbol in ClojureScript - clojurescript

Metadata of a variable such as the docstring can be read like so: Here the function map is used as an example:
(-> cljs.core/map var meta :doc)
;= "Returns a lazy sequence consisting of the result of appl....
What would I have to do to get the same result when only having the function name and its namespace as a string (as "cljs.core/map")?
Wrapping the string with the function symbol yielded an error.

Here is one way to do it:
* Clojure file *
(ns myproject.macros
(:require [clojure.string :as str]))
(defmacro doc1 [s]
(let [sym (apply symbol (str/split s #"/"))]
`(-> ~sym (var) (meta) :doc)))
* ClojureScript file *
(ns myproject.main
(:require-macros [myproject.macros :as m]))
(prn (m/doc1 "cljs.core/inc"))
=>
"Returns a number one greater than num."

Related

Clojurescript namespace as argument

Say I have the following Clojurescript code:
(ns one)
(defn foo [] 1)
(ns two)
(defn foo [] 2)
(ns other)
(defn thing [the-ns] (the-ns/foo))
; now I want to see 1
(other/thing one)
; now I want to see 2
(other/thing two)
How can I achieve this with Clojurescript?
one and two has the same "interface".
PS I know I can pass a function as an argument, but that doesn't answer the question. (e.g. the namespace might have many functions, and I don't want to pass them all)
 Tried ns-resolve
boot.user=> (ns one)
nil
one=> (defn foo [] 1)
#'one/foo
one=> (ns two)
nil
two=> (defn foo [] 2)
#'two/foo
two=> (ns other (:require [cljs.analyzer.api :as api]))
nil
other=> (defn thing [the-ns] (let [f (api/ns-resolve the-ns 'foo)] (f)))
#'other/thing
other=> (other/thing 'one)
java.lang.NullPointerException:
other=> (one/foo)
1
other=> (two/foo)
2
(Yes, there's no trace after java.lang.NullPointerException:, and I go on to show the initial namespaces resolve in the REPL session,)
If I move away from the contrived example, and try this in my Clojurescript project, I get this trace:
#object[Error Error: No protocol method IDeref.-deref defined for type null: ]
Error: No protocol method IDeref.-deref defined for type null:
at Object.cljs$core$missing_protocol [as missing_protocol] (http://0.0.0.0:8000/index.html.out/cljs/core.js:311:9)
at Object.cljs$core$_deref [as _deref] (http://0.0.0.0:8000/index.html.out/cljs/core.js:2164:17)
at cljs$core$deref (http://0.0.0.0:8000/index.html.out/cljs/core.js:4945:18)
at Function.cljs.analyzer.api.ns_resolve.cljs$core$IFn$_invoke$arity$3 (http://0.0.0.0:8000/index.html.out/cljs/analyzer/api.js:346:51)
at cljs$analyzer$api$ns_resolve (http://0.0.0.0:8000/index.html.out/cljs/analyzer/api.js:322:37)
at Function.cljs.analyzer.api.ns_resolve.cljs$core$IFn$_invoke$arity$2 (http://0.0.0.0:8000/index.html.out/cljs/analyzer/api.js:332:37)
at cljs$analyzer$api$ns_resolve (http://0.0.0.0:8000/index.html.out/cljs/analyzer/api.js:318:37)
at eval (eval at <anonymous> (http://0.0.0.0:8000/index.html.out/weasel/repl.js:30:495), <anonymous>:1:108)
at eval (eval at <anonymous> (http://0.0.0.0:8000/index.html.out/weasel/repl.js:30:495), <anonymous>:9:3)
at eval (eval at <anonymous> (http://0.0.0.0:8000/index.html.out/weasel/repl.js:30:495), <anonymous>:14:4)
You can use ns-resolve function to find a var in a namespace.
(ns one)
(defn foo [] 1)
(ns two)
(defn foo [] 2)
(ns other)
(defn thing [the-ns]
(let [f (ns-resolve the-ns 'foo)]
(f)))
(demo.other/thing 'one) ;; returns 1
(demo.other/thing 'two) ;; returns 2
But for this kind of polymorphic behavior, using protocols or multi-methods is more suitable.
UPDATE
The above code works only in Clojure, because ns-resolve does not exists in ClojureScript. In fact, ClojureScript does not have Vars.
But we can manually get the function from namespace object. We also need to mark functions with export metadata flag to prevent function names get "munged":
(ns demo.one)
(defn ^:export foo [] 1)
(ns demo.two)
(defn ^:export foo [] 2)
(ns demo.other)
(defn thing [the-ns]
(let [f (aget the-ns "foo")]
(f)))
(other/thing demo.one)
(other/thing demo.two)

Reflect on Clojure Function Name

How do I use reflection in Clojure to extract the function name of a symbol that evaluates to a function?
(require '[clojure.reflect :as r])
(defn foo [x] x)
(r/reflect foo)
=> {#clojure.reflect.Field ... 'gobbledygook}
Something about (map :name (:members (r/reflect foo)))?
No, please do not use str because it behaves differently. In my example, here is what I've got:
user=> (defn foo [x] x)
#'user/foo
user=> (str foo)
"user$foo#7cb20059"
Instead, use combination of meta and var calls:
(-> foo var meta)
This code returns a map of variable's metadata. The key :name holds the function name as a string:
(-> foo var meta :name)
foo
If you also want to have an ns's name, do the following:
user=> (-> foo var meta :ns ns-name)
user
It gets the namespace object and returns its name with the ns-name function as a string.
Those methods do not depend on Clojure or REPL version and thus are more maintainable.
Looks like I can use str:
(str foo)
=> "#'my-project/foo"

How do I get a list of public functions in a ClojureScript namespace?

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.
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.

loading configuration file in clojure as data structure

Is there a reader function in clojure to parse clojure data structure? My use case is to read configuration properties files and one value for a property should be a list. I'd like to be able to write this as:
file.properties:
property1 = ["value1" "value2"]
and in clojure:
(load-props "file.properties")
and get a map with value {property1, ["value1" "value2"]
Right now,m I'm doing the following, with the same input file "file.properties":
(defn load-props [filename]
(let [io (java.io.FileInputStream. filename)
prop (java.util.Properties.)]
(.load prop io)
(into {} prop)))
;; returns:
;; {"property1" "[\"valu1\", \"valu2\"]"}
(load-props "file.properties")
But I cannot get a way to parse the result to a clojure's vector. I'm basically looking for something like Erlang's file:consult/1 function. Any idea how to do this?
If you want to read java-style properties files, look at Dave Ray's answer - though properties files have many limitations.
If you are using Clojure 1.5 or later, I suggest you use edn, the extensible data notation used in Datomic - it's basically clojure data structures, with no arbitrary code execution, and the ability to add tags for things like instances or arbitrary types.
The simplest way to use it is via read-string and slurp:
(require 'clojure.edn)
(clojure.edn/read-string (slurp "filename.edn"))
That's it. Note that read-string only reads a single variable, so you should set up your configuration as a map:
{ :property1 ["value1" "value2"] }
Then:
(require 'clojure.edn)
(def config (clojure.edn/read-string (slurp "config.edn")))
(println (:property1 config))
returns
["value1" "value2"]
java.util.Properties implements Map so this can be done very easily without manually parsing properties files:
(require 'clojure.java.io)
(defn load-props
[file-name]
(with-open [^java.io.Reader reader (clojure.java.io/reader file-name)]
(let [props (java.util.Properties.)]
(.load props reader)
(into {} (for [[k v] props] [(keyword k) (read-string v)])))))
(load-props "test.properties")
;=> {:property3 {:foo 100, :bar :test}, :property2 99.9, :property1 ["foo" "bar"]}
In particular, properties files are more complicated than you think (comments, escaping, etc, etc) and java.util.Properties is very good at loading them.
Is there a reader function in clojure to parse clojure data structure?
Yes. It's called read. You can also use it to read configuration data.
A file props.clj containing
{:property1 ["value1" 2]
:property2 {:some "key"}}
can be read like this:
(ns somens.core
(:require [clojure.java.io :as io])
(:import [java.io PushbackReader]))
(def conf (with-open [r (io/reader "props.clj")]
(read (PushbackReader. r))))
When reading untrusted sources it might be a good idea to turn of *read-eval*:
(def conf (binding [*read-eval* false]
(with-open [r (io/reader "props.clj")]
(read (PushbackReader. r)))))
For writing configuration data back to a file you should look at print functions such as pr and friends.
contrib has functions for reading writing properties,
http://richhickey.github.com/clojure-contrib/java-utils-api.html#clojure.contrib.java-utils/as-properties
If this is for your own consumption then I would suggest reading/writing clojure data structures you can just print them to disk and read them.
(use '[clojure.contrib.duck-streams :only (read-lines)])
(import '(java.io StringReader PushbackReader))
(defn propline->map [line] ;;property1 = ["value1" "value2"] -> { :property1 ["value1" "value2"] }
(let [[key-str value-str] (seq (.split line "="))
key (keyword (.trim key-str))
value (read (PushbackReader. (StringReader. value-str)))]
{ key value } ))
(defn load-props [filename]
(reduce into (map propline->map (read-lines filename))))
DEMO
user=> (def prop (load-props "file.properties"))
#'user/prop
user=> (prop :property1)
["value1" "value2"]
user=> ((prop :property1) 1)
"value2"
UPDATE
(defn non-blank? [line] (if (re-find #"\S" line) true false))
(defn non-comment? [line] (if (re-find #"^\s*\#" line) false true))
(defn load-props [filename]
(reduce into (map propline->map (filter #(and (non-blank? %)(non-comment? %)) (read-lines filename)))))

Can I refer another namespace and expose its functions as public for the current ns?

I thought use would do it but it seems the mapping created in the current namespace is not public. Here is an example of what I'd like to achieve:
(ns my-ns
(:use [another-ns :only (another-fct)]))
(defn my-fct
[]
(another-fct 123)) ; this works fine
Then I have another namespace like this:
(ns my-ns-2
(:require [my-ns :as my]))
(defn my-fct-2
[]
(my/another-fct 456)) ; this doesn't work
I would like to do that because another-ns is a library to access a database. I would like to isolate all the calls to this library in a single namespace (my-ns), this way all the DB dependent functions would be isolated in a single namespace and it becomes easier to switch to another DB if needed.
Some of the functions of this library are just fine for me but I'd like to augment others. Let's say the read functions are fine but I'd like to augment the write functions with some validation.
The only way I see so far is to hand-code all the mapping into my-ns even for the functions I don't augment.
One way to do this selectively (specifying each function explicitly) is to use something like Zach Tellman's Potemkin library. An example of it's use is found in the lamina.core namespace which serves as the public entry point for Lamina, importing the key public functions from all other internal namespaces.
You can also use clojure.contrib.def/defalias:
(use 'clojure.contrib.def/defalias)
(defalias foo clojure.string/blank?)
(foo "")
Does this help?
(defmacro pull [ns vlist]
`(do ~#(for [i vlist]
`(def ~i ~(symbol (str ns "/" i))))))
Here's an example:
(ns my-ns)
(defmacro pull [ns vlist]
`(do ~#(for [i vlist]
`(def ~i ~(symbol (str ns "/" i))))))
(pull clojure.string (reverse replace))
(defn my-reverse
[]
(reverse "abc"))
(ns my-ns-2)
(defn my-fct-2 []
(list (my-ns/my-reverse)
(my-ns/reverse "abc")))
(my-fct-2)
If you want to just pull in everything, then:
(defmacro pullall [ns]
`(do ~#(for [i (map first (ns-publics ns))]
`(def ~i ~(symbol (str ns "/" i))))))
(pullall clojure.string)
To pull everything from namespace that may have macros defined within use this
(defmacro pullall [ns]
`(do ~#(for [[sym var] (ns-publics ns)]
`(def ~sym ~var))))