Leinigen and Environment Variables

02 Apr 2014

As I've been delving deeper and deeper into Clojure, I've been running into a lot of little problems. Not with the language, but with the libraries and tools. Much of this comes down to documentation, really.

For example, on a small project of mine I recently switched my database migrations from lobos to clj-sql-up (for one of the other annoyances I have with clojure libraries: finding one that stays up-to-date with the changing landscape, and respond to issues/pull-requests - perhaps more on that in a later post). I wanted to have my database configuraton come from an environment variable, with a fallback for development. Sounds simple enough, really, and it is in the code. clj-sql-up requires that you put the DB spec in the project file as well, for use with its leiningen plugin. So I put the following into my project.clj:

:clj-sql-up {:database (or (System/getenv "DATABASE_URL")
                           {:subprotocol "hsqldb"
                            :subname "file:data/my-dev.db"})
                            ...

Looks fine, but when I ran lein clj-sql-up migrate, I would see the following cryptic exception:

java.lang.IllegalArgumentException: No value supplied for key: {:subprotocol "hsqldb", :subname "file:data/daily-metric-dev.db"}
        at clojure.lang.PersistentHashMap.create(PersistentHashMap.java:77)
        at clojure.java.jdbc$get_connection.invoke(jdbc.clj:172)
        ...

It turns out, the entire form of (or ... was being used as the db spec. This isn't what I wanted at all. It took a surprising amount of digging but I finally found the answer on Stack Overflow (no surprise there, but asking Google the right question took some tuning). I changed my project file to

:clj-sql-up {:database ~(or (System/getenv "DATABASE_URL")
                            {:subprotocol "hsqldb"
                             :subname "file:data/my-dev.db"})
                             ...

See the difference? It's the use of ~ before the or. Leiningen allows execution of arbitrary code by the use of the unquote syntax. This is no where to be found in the documentation, and given that it isn't entirely obvious how the project file is used (execution vs configuration), it wasn't as obvious that it would work either.

I'm hoping that the documentation situation will improve as the tools, library and langauges mature. In the meantime I - and hopefully everyone in the clojure community - will continue to write about the problems the encounter along the way.

comments powered by Disqus

Home