0

I am writing a Ring / Compojure app with Clojure that fetches content for pages from database. To be able to create tests for how the content is displayed, I created prod and dev environments and when using dev environment, a mock database is used instead of the production database. I achieve this by reading the database from another file and giving it as a parameter to my routes. Here's a simplified version:

(defn www-routes [db]
    (defroutes www-routes
        (GET "/" [] ...)))

(def config (delay (load-file (.getFile (resource "config.clj")))))

(defn db []
    (if (= "dev" (:database @(force config)))
        'kipsu.db-mock
        'kipsu.database))

(def app (routes (www-routes (db)))

The setup is largely taken from the example here, with the addition of setting the database as a parameter.

This setup works great with running the tests with the mock database and displaying real content on prod environment. Things run fine when I start a lein server locally, run tests or any of the functions in lein repl. My problem comes when I'd like to create an uberjar for deploying the changes on my server.

This is where I get a NullPointerException when compiling, starting from (db) function call inside the def app. I've tried debugging with poor success and am not even 100% sure where the actual error is. All I know is the db function is never even called. Here's the stack trace:

Compiling kipsu.jdbc.json
Compiling kipsu.database
Compiling kipsu.api_converter
Compiling kipsu.web
java.lang.NullPointerException, compiling:(web.clj:124:29)
    at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3628)
    at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3622)
    at clojure.lang.Compiler$BodyExpr.eval(Compiler.java:5879)
    at clojure.lang.Compiler$DefExpr.eval(Compiler.java:439)
    at clojure.lang.Compiler.compile1(Compiler.java:7323)
    at clojure.lang.Compiler.compile(Compiler.java:7390)
    at clojure.lang.RT.compile(RT.java:399)
    at clojure.lang.RT.load(RT.java:444)
    at clojure.lang.RT.load(RT.java:412)
    at clojure.core$load$fn__5448.invoke(core.clj:5866)
    at clojure.core$load.doInvoke(core.clj:5865)
    at clojure.lang.RestFn.invoke(RestFn.java:408)
    at clojure.core$load_one.invoke(core.clj:5671)
    at clojure.core$compile$fn__5453.invoke(core.clj:5877)
    at clojure.core$compile.invoke(core.clj:5876)
    at user$eval9$fn__16.invoke(form-init1768231915654429312.clj:1)
    at user$eval9.invoke(form-init1768231915654429312.clj:1)
    at clojure.lang.Compiler.eval(Compiler.java:6782)
    at clojure.lang.Compiler.eval(Compiler.java:6772)
    at clojure.lang.Compiler.load(Compiler.java:7227)
    at clojure.lang.Compiler.loadFile(Compiler.java:7165)
    at clojure.main$load_script.invoke(main.clj:275)
    at clojure.main$init_opt.invoke(main.clj:280)
    at clojure.main$initialize.invoke(main.clj:308)
    at clojure.main$null_opt.invoke(main.clj:343)
    at clojure.main$main.doInvoke(main.clj:421)
    at clojure.lang.RestFn.invoke(RestFn.java:421)
    at clojure.lang.Var.invoke(Var.java:383)
    at clojure.lang.AFn.applyToHelper(AFn.java:156)
    at clojure.lang.Var.applyTo(Var.java:700)
    at clojure.main.main(main.java:37)
Caused by: java.lang.NullPointerException
    at kipsu.web$fn__4568.invoke(web.clj:115)
    at clojure.lang.Delay.deref(Delay.java:37)
    at clojure.lang.Delay.force(Delay.java:27)
    at clojure.core$force.invoke(core.clj:730)
    at kipsu.web$db.invoke(web.clj:118)
    at clojure.lang.AFn.applyToHelper(AFn.java:152)
    at clojure.lang.AFn.applyTo(AFn.java:144)
    at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3623)
    ... 30 more
Exception in thread "main" java.lang.NullPointerException, compiling (web.clj:124:29)
    at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3628)
    at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3622)
    at clojure.lang.Compiler$BodyExpr.eval(Compiler.java:5879)
    at clojure.lang.Compiler$DefExpr.eval(Compiler.java:439)
    at clojure.lang.Compiler.compile1(Compiler.java:7323)
    at clojure.lang.Compiler.compile(Compiler.java:7390)
    at clojure.lang.RT.compile(RT.java:399)
    at clojure.lang.RT.load(RT.java:444)
    at clojure.lang.RT.load(RT.java:412)
    at clojure.core$load$fn__5448.invoke(core.clj:5866)
    at clojure.core$load.doInvoke(core.clj:5865)
    at clojure.lang.RestFn.invoke(RestFn.java:408)
    at clojure.core$load_one.invoke(core.clj:5671)
    at clojure.core$compile$fn__5453.invoke(core.clj:5877)
    at clojure.core$compile.invoke(core.clj:5876)
    at user$eval9$fn__16.invoke(form-init1768231915654429312.clj:1)
    at user$eval9.invoke(form-init1768231915654429312.clj:1)
    at clojure.lang.Compiler.eval(Compiler.java:6782)
    at clojure.lang.Compiler.eval(Compiler.java:6772)
    at clojure.lang.Compiler.load(Compiler.java:7227)
    at clojure.lang.Compiler.loadFile(Compiler.java:7165)
    at clojure.main$load_script.invoke(main.clj:275)
    at clojure.main$init_opt.invoke(main.clj:280)
    at clojure.main$initialize.invoke(main.clj:308)
    at clojure.main$null_opt.invoke(main.clj:343)
    at clojure.main$main.doInvoke(main.clj:421)
    at clojure.lang.RestFn.invoke(RestFn.java:421)
    at clojure.lang.Var.invoke(Var.java:383)
    at clojure.lang.AFn.applyToHelper(AFn.java:156)
    at clojure.lang.Var.applyTo(Var.java:700)
    at clojure.main.main(main.java:37)
Caused by: java.lang.NullPointerException
    at kipsu.web$fn__4568.invoke(web.clj:115)
    at clojure.lang.Delay.deref(Delay.java:37)
    at clojure.lang.Delay.force(Delay.java:27)
    at clojure.core$force.invoke(core.clj:730)
    at kipsu.web$db.invoke(web.clj:118)
    at clojure.lang.AFn.applyToHelper(AFn.java:152)
    at clojure.lang.AFn.applyTo(AFn.java:144)
    at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3623)
    ... 30 more
Compilation failed: Subprocess failed

I'm not the most fluent with Clojure and am working with this app to learn more. Any help for steering me at the right direction from here is greatly appreciated!

4

1 に答える 1

0

あなたの問題はdef、コンパイル時に実行されるためだと思います。とで正しいことを(def config)行いまし(defn db)たが(def app)、ファイルが見つからない場合、コンパイル時にエラーが発生します。その理由を理解するために、 を見てみましょうdef

(def hello (println "hello"))

このコードをコンパイルしようとすると、コンパイル時に「hello」がコンソールに出力され、実行時に varhelloの値がnil.

(def hello (delay (println "hello"))

(def world @hello)

varhelloはコンパイル時に評価されなくなりましたが、varworldを導入することでまったく同じ問題が発生します。

ここで、特定の問題に戻ります。コンパイル時に構成を読み取らせたくないし、構成が必要なたびにディスクからファイルを読み取らなければならないようにしたくありません。

コンパイル時に構成を読み取らないと、おそらく関数であるべきだと思います。memoizeそれがその関数である場合は、その関数を呼び出すたびにディスクから読み取られないようにするために単純に使用できます。

于 2015-08-21T00:53:40.840 に答える