3

I'm writing a clojure app for internal use, and I want the config file to be in clojure too. I have defined a few macros to make writing the config file easier, but when I try to eval the data from the config file, it cant find my macros. This works fine from the REPL however. For example, I'm using

(load-string "/path/to/config")

I get this error:

Exception in thread "main" java.lang.RuntimeException: Unable to resolve symbol: defcmd in this context, compiling:(null:1)
at clojure.lang.Compiler.analyze(Compiler.java:6235)
at clojure.lang.Compiler.analyze(Compiler.java:6177)
at clojure.lang.Compiler$InvokeExpr.parse(Compiler.java:3452)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6411)
at clojure.lang.Compiler.analyze(Compiler.java:6216)
at clojure.lang.Compiler.analyze(Compiler.java:6177)
at clojure.lang.Compiler.eval(Compiler.java:6469)
at clojure.lang.Compiler.load(Compiler.java:6902)
at clojure.lang.Compiler.load(Compiler.java:6872)
at clojure.core$load_reader.invoke(core.clj:3625)
at clojure.core$load_string.invoke(core.clj:3635)
at serverStats.core$load_config.invoke(core.clj:67)
at serverStats.core$_main.doInvoke(core.clj:78)
at clojure.lang.RestFn.invoke(RestFn.java:397)
at clojure.lang.Var.invoke(Var.java:397)
at user$eval109.invoke(NO_SOURCE_FILE:1)
at clojure.lang.Compiler.eval(Compiler.java:6465)
at clojure.lang.Compiler.eval(Compiler.java:6455)
at clojure.lang.Compiler.eval(Compiler.java:6431)
at clojure.core$eval.invoke(core.clj:2795)
at clojure.main$eval_opt.invoke(main.clj:296)
at clojure.main$initialize.invoke(main.clj:315)
at clojure.main$null_opt.invoke(main.clj:348)
at clojure.main$main.doInvoke(main.clj:426)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at clojure.lang.Var.invoke(Var.java:405)
at clojure.lang.AFn.applyToHelper(AFn.java:163)
at clojure.lang.Var.applyTo(Var.java:518)
at clojure.main.main(main.java:37)
Caused by: java.lang.RuntimeException: Unable to resolve symbol: defcmd in this context
at clojure.lang.Util.runtimeException(Util.java:156)
at clojure.lang.Compiler.resolveIn(Compiler.java:6720)
at clojure.lang.Compiler.resolve(Compiler.java:6664)
at clojure.lang.Compiler.analyzeSymbol(Compiler.java:6625)
at clojure.lang.Compiler.analyze(Compiler.java:6198)
... 28 more

However, running that same command from the REPL in my namespace works fine.

2
  • I found a related thread, but no solution: stackoverflow.com/questions/1307567/… Commented Dec 25, 2011 at 2:14
  • 2
    I'm going to guess that "defcmd" is somewhere in the path string considering that load-string doesn't do what you think it does. Try load-file. Commented Dec 25, 2011 at 8:04

1 Answer 1

5

You probably want some more sophisticated loading scheme. I assume you want to put the configuration into a dedicated configuration namespace. It will only contain the configuration. Helper functions are held in a separate namespace use'd in the configuration namespace.

(defn setup-config-space
  []
  (binding [*ns* *ns*]
    (in-ns 'config.namespace)
    (refer-clojure)
    (use 'config.helpers)))

(defn load-config
  [path]
  (binding [*ns* *ns*]
    (in-ns 'config.namespace)
    (load-file path)))

See the example use:

..ojure/1.4.0-alpha3% cat config/helpers.clj                             
(ns config.helpers)

(defmacro defcmd
  [x]
  `(defn ~x [] "Hello"))
..ojure/1.4.0-alpha3% cat x.clj
(defcmd foo)
..ojure/1.4.0-alpha3% java -cp .:clojure-1.4.0-alpha3.jar clojure.main -r
Clojure 1.4.0-alpha3
user=> ; Paste above functions
#'user/setup-config-space
#'user/load-config
user=> (setup-config-space)
nil
user=> (load-config "x.clj")
#'config.namespace/foo
user=> (config.namespace/foo)
"Hello"
Sign up to request clarification or add additional context in comments.

3 Comments

Can you explain, why evaluating in REPL: (binding [ns (find-ns 'config.namespace)] (defn tst [] 1)) results in #'user/tst and not #'config.namespace/tst?
Because the Var creation part of the def (from the defn) is done while compiling the command, but the binding only happens during execution of the body. Hence the namespace is still "user". def.* in non-toplevel position is most of the times wrong. (Exception: closure over a value from a let binding, (let [x ...] (defn foo [] x)))
Nice tip. Using it for real. A small example can be seen here on this gist: gist.github.com/paulosuzart/4727858#file-init-clj

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.