5
votes

I'm trying to load two namespaces from the library "spurious-aws-sdk-helper" (which by the way I've installed locally - this is me testing it before deploying to Clojars). And I'm loading the namespaces from inside an if statement.

Once the namespaces are loaded I call a function which is provided by one of the loaded namespaces.

The problem is that when executing the code via lein ring server I get a Java exception informing me that the namespace I'm trying to access isn't available.

But if I run lein repl and then (use 'spurious-clojure-example.routes.home) the relevant top level namespace; then (require '[spurious-aws-sdk-helper.core :as core]) the namespace - much like in the code I'll linked to in a moment demonstrates - then the namespace WILL be available and subsequently the call to the function won't error?

I'm not sure if it's one of those errors which are misleading and in fact it's not the namespace I'm trying to require that's the problem, but something inside of it that's the issue? But if that was true then why would it work when called manually by myself within lein repl?

The code I'm referring to is: 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)))

It's the (core/configure ...) call that triggers a Java exception saying "core" namespace isn't available. But running the following code from lein repl works fine...

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

UPDATE 1:

Just to clarify I've updated the code as follows...

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

...and when running it within the REPL it works fine.

The problem is when running it via lein ring server.

I've started reading about (ns-resolve) here: http://technomancy.us/143

But the solution it suggests: (ns-resolve 'core 'configure) didn't work; it just threw an Unable to resolve symbol: core in this context error.

2
Are you sure you have ~/.lein/profiles.clj set in order to have (env :debug) return true ? - T.Gounelle
Also note you're using cred in content. It won't be available unless the debug profile is on and could result in an error. - deadghost
@deadghost very good point about cred that'll be a separate issue I need to consider fixing afterwards (very likely the util namespace I'll just load explicitly; as I would need that functionality regardless of profile). - Integralist

2 Answers

2
votes

I created a app with lein new compojure-app and when :debug value is truthy, require clojure.string :as str and then also printing something to shell.

The code below works via lein ring server. I tested it with :debug values true and false. I see in your example, you use environ so, I put {:debug true} or {:debug false} in .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)))

2
votes

Tried it with:

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

=> No such namespace: str

I'm a tiny bit surprised as well since if and when should only evaluate the body of their expressions for the appropriate branch and not touch the other expressions. I did not expect a namespace error on false.

More surprising is I did not expect a namespace error on true as well. I'd guess it's some java compilation thing trying to resolve the namespace even before code evaluation. I don't know the specifics of why.

As for what you should do, this code is funky and I've never thought of or seen anyone doing anything similar. Unless there is some specific reason for doing this, the solution is simple: shove your requires to the very top in ns. There's no need to change anything else.