0

我正在用 Clojure 编写一个 Ring / Compojure 应用程序,它从数据库中获取页面的内容。为了能够为内容的显示方式创建测试,我创建了 prod 和 dev 环境,并且在使用 dev 环境时,使用模拟数据库而不是生产数据库。我通过从另一个文件中读取数据库并将其作为参数提供给我的路线来实现这一点。这是一个简化版本:

(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)))

设置主要取自此处的示例,并添加了将数据库设置为参数。

此设置非常适合使用模拟数据库运行测试并在 prod 环境中显示真实内容。当我在本地启动 lein 服务器、运行测试或 lein repl 中的任何功能时,一切运行良好。当我想创建一个用于在我的服务器上部署更改的 uberjar 时,我的问题就出现了。

这是我在编译时得到 NullPointerException 的地方,从 def 应用程序内的 (db) 函数调用开始。我尝试过调试,但效果不佳,甚至不能 100% 确定实际错误在哪里。我所知道的是 db 函数甚至从未被调用过。这是堆栈跟踪:

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

我对 Clojure 不是最流利的,我正在使用这个应用程序来了解更多信息。非常感谢从这里引导我朝着正确方向的任何帮助!

4

1 回答 1

0

我认为您的问题是因为def将在编译时执行。你在 and 中做了正确的事情(def config)(defn db)但是(def app)如果它找不到你的文件会导致编译时错误。要了解为什么让我们看一下def.

(def hello (println "hello"))

如果您尝试编译此代码,您将在编译时看到“hello”打印到您的控制台,并且在运行时 varhello将具有 value nil

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

(def world @hello)

varhello现在不会在编译时被评估,但是通过引入 varworld我们得到了完全相同的问题。

现在回到你的具体问题。您不希望在编译时读取您的配置,也不希望您的配置每次需要时都从磁盘读取文件。

在编译时不阅读您的配置让我认为它可能应该是一个函数。如果它是一个函数,您可以简单地使用memoize它来确保每次调用该函数时它都不会从磁盘读取。

于 2015-08-21T00:53:40.840 回答