3
votes

In my main namespace, I have a top level var named "settings" which is initialized as an empty {}.

My -main fn sets the contents of settings using def and conj based on some command line args (different database hosts for production/development, etc).

I'm trying to access the contents of this map from another namespace to pull out some of the settings. When I try to compile with lein into an uberjar, I get a traceback saying "No such var: lb/settings".

What am I missing? Is there a more idiomatic way to handle app wide settings such as these? Is it safe to use "def" inside of -main like I am, or should I be use an atom or ref to make this threadsafe?

Thanks!

(ns com.domain.main
  (:use com.domain.some-other-namespace.core)
  (:gen-class))

(def settings {})

(defn -main [& args]
  (with-command-line-args... ;set devel? based on args
    (if (true? devel?)
    (def settings (conj settings {:mongodb {:host "127.0.0.1"}
                      :memcached {:host "127.0.0.1"}}))
    (def settings (conj settings {:mongodb {:host "PRODUCTION_IP"}
                      :memcached {:host "PRODUCTION_IP"}})))


;file2.clj
(ns com.domain.some-other-namespace.core
  (:require [main :as lb]
  ...)

;configure MongoDB
(congo/mongo!
  :db "dbname" :host (:host (mongodb lb/settings))))
...
2

2 Answers

4
votes

Ok, I found the problem. It looks like it was a circular reference. I was ":require"ing com.domain.some-other-namespace.core from com.domain.main. Since the "require" is called before (def settings {}) in com.domain.main, the var does not yet exist when the other namespace is compiled...

I moved the settings map into a separate namespace (named settings naturally) and changed it from a Var to an Atom just to be safe. Seems to work great now!

0
votes

A couple things to check:

typically clojure namespaces have at least one . in them project.main I think leiningen may depend on this.

check the classes folder to make sure the main and some-other-namespace class files are being compiled.