Clojure命名空间不可访问。

5

我正在尝试从库“spurious-aws-sdk-helper”中加载两个命名空间(顺便说一下,我已经在本地安装了它 - 这是我在部署到Clojars之前进行测试)。我在if语句内加载命名空间。

一旦命名空间被加载,我调用一个由其中一个已加载的命名空间提供的函数。

问题是,当通过lein ring server执行代码时,我会收到Java异常,告诉我我要访问的命名空间不可用。

但是,如果我运行lein repl,然后运行(use 'spurious-clojure-example.routes.home)这个相关的顶级命名空间;然后(require '[spurious-aws-sdk-helper.core :as core])这个命名空间 - 就像我稍后会展示的代码中所演示的那样 - 然后命名空间将可用,随后对函数的调用就不会出错了?

我不确定它是否是一种误导性错误,实际上并不是我尝试要求的命名空间有问题,而是它内部的某些内容有问题?但如果是真的,为什么手动在lein repl中调用它时可以正常工作呢?

我所指的代码是:https://github.com/Integralist/spurious-clojure-example/blob/baseline/src/spurious_clojure_example/routes/home.clj#L9-L10

(ns spurious-clojure-example.routes.home
  (:use [amazonica.aws.s3])
  (:require [compojure.core :refer :all]
            [environ.core :refer [env]]
            [spurious-clojure-example.views.layout :as layout]))

(if (env :debug)
  (do
    (require '[spurious-aws-sdk-helper.core :as core])
    (require '[spurious-aws-sdk-helper.utils :refer [endpoint cred]])
    (core/configure {:s3  "test-bucket4"
                     :sqs "test-queue4"
                     :ddb (slurp "./resources/config/schema.yaml")})))

(def bucket-path "news-archive/dev/election2014-council_title")

(def content
  (apply str (line-seq
               (clojure.java.io/reader
                 (:object-content
                   (get-object (cred (endpoint :spurious-s3)) :bucket-name "shared" :key bucket-path))))))

(defn home []
  (layout/common [:h1 content]))

(defroutes home-routes
  (GET "/" [] (home)))

(core/configure ...) 调用会触发一个Java异常,指出“core”命名空间不可用。但在 lein repl 中运行以下代码可以正常工作...

(use 'spurious-clojure-example.routes.home)
(require '[spurious-aws-sdk-helper.core :as core])
(core/configure ...rest of code...)

更新 1:

为了澄清,我已经按照以下方式更新了代码...

(when (env :debug)
  (require '[spurious-aws-sdk-helper.core :as core])
  (require '[spurious-aws-sdk-helper.utils :refer [endpoint cred]])
  (core/configure
   {:s3 "test-bucket7"
    :sqs "test-queue9"
    :ddb (slurp "./resources/config/schema.yaml")}))

当你在REPL中运行代码时,它可以正常工作。

问题是当你通过lein ring server运行它时。

我已经开始阅读关于(ns-resolve)的解释: http://technomancy.us/143

但是它提到的解决方案:(ns-resolve 'core 'configure)没有起作用,它只是抛出了一个Unable to resolve symbol: core in this context的错误信息。


你确定已经设置了 ~/.lein/profiles.clj 以便 (env :debug) 返回 true 吗? - T.Gounelle
还要注意,在content中使用了cred。除非打开了debug配置文件,否则它将不可用,并可能导致错误。 - deadghost
@deadghost提出了一个非常好的观点,关于cred,这将是我需要考虑修复的另一个问题(很可能是util命名空间,我只需显式加载;因为无论如何我都需要那个功能)。 - Integralist
2个回答

2

尝试过以下方法:

(when true ; also tried with false
  (require '[clojure.string :as str])
  (str/split "Clojure is awesome!" #" "))

=> No such namespace: str

我也感到有点惊讶,因为ifwhen只应该评估其表达式的正确分支并不触及其他表达式。我没想到false会出现命名空间错误。
更令人惊讶的是,我没想到true也会出现命名空间错误。我猜这可能是某个Java编译问题,在代码评估之前尝试解析命名空间。我不知道具体原因。
至于你应该怎么做,这段代码很奇怪,我从来没有想过或见过任何人做类似的事情。除非有特殊原因需要这样做,否则解决方案很简单:将你的requires推到ns的顶部即可。不需要改变其他任何东西。

如果我将str/split表达式放在when表达式外面,它就可以工作了。(做 (当真时;也尝试过false) (要求'[clojure.string :as str])) (str/split“Clojure is awesome!”#“ ”)。也许这是因为ns解析发生在每个表达式中。 - mavbozo
@deadghost 感谢您的反馈。只是想澄清一下:如果应用程序在生产中,我想避免 require 辅助库 - 也就是说,辅助库仅在开发中使用。 - Integralist
@deadghost,我在问题底部添加了一个更新,澄清了一些我所做的更改,并尝试使用ns-resolve(但没有成功)。 - Integralist
@Integralist 在开发中仅使用 require 辅助库,你能获得什么好处? - deadghost
@deadghost 我认为它应该会更高效一些,不过这是胡说八道吗?因为Clojure实际上是一种编译语言(例如编译成可部署的jar文件),所以我需要以不同的方式思考这些问题(与像Ruby这样的脚本语言相比)。但我仍然认为最好明确地指出代码只在开发中使用特定库,而不是没有任何解释地直接加载它(但这只是我的个人意见)。 - Integralist

2

我使用 lein new compojure-app 创建了一个应用程序,当 :debug 值为真时,需要 clojure.string :as str 并在 shell 中打印一些内容。

以下代码通过 lein ring server 工作。我测试了 :debug 值为 truefalse。我看到你的示例中使用了 environ,因此我将 {:debug true}{:debug false} 放入 .lein-env

    (ns integralist.handler
      (:require [compojure.core :refer [defroutes routes]]
                [ring.middleware.resource :refer [wrap-resource]]
                [ring.middleware.file-info :refer [wrap-file-info]]
                [hiccup.middleware :refer [wrap-base-url]]
                [compojure.handler :as handler]
                [compojure.route :as route]
                [integralist.routes.home :refer [home-routes]]
                [environ.core :refer [env]]))
(when (env :debug) (require '[clojure.string :as str]))
(when (env :debug) (defn it-works! [] (println "It works!:" (str/split "Clojure is Awesome" #" "))))
(defn init [] (println "integralist is starting") (when (env :debug) (it-works!)))
(defn destroy [] (println "integralist is shutting down"))
(defroutes app-routes (route/resources "/") (route/not-found "Not Found"))
(def app (-> (routes home-routes app-routes) (handler/site) (wrap-base-url)))

1
感谢 @mavbozo - 看起来问题是由于Clojure如何处理(do)块,其中所有表达式都同时编译。而将它们分开成两个不同的块意味着编译器不会出错。 - Integralist
更新:在正常运行lein ring server时,这个方法是有效的。但是一旦将此应用程序打包成一个jar文件,并从Docker容器中运行该jar文件,就会出现require调用和对命名空间的引用问题。在那种情况下,我决定只在ns声明中指定依赖项(因为我意识到这并不重要;因为整个应用程序已经编译完成,所以没有需要避免的性能问题)。 - Integralist
@Integralist,您的开发机器上本地运行这个jar文件好吗?我将包含以上代码的应用程序打包成一个jar文件,并在我的开发机器上运行了它。 - mavbozo
我即将开一个单独的问题进行讨论,因为我觉得了解我所缺少的内容非常重要。 - Integralist
http://stackoverflow.com/questions/28630412/how-get-a-clojure-compojure-app-running-within-docker - Integralist

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接