5

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.

Integralist
  • 5,899
  • 5
  • 25
  • 42
  • Are you sure you have `~/.lein/profiles.clj` set in order to have `(env :debug)` return `true` ? – T.Gounelle Feb 14 '15 at 23:46
  • 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 Feb 15 '15 at 05:56
  • @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 Feb 15 '15 at 10:47

2 Answers2

2

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.

deadghost
  • 5,017
  • 3
  • 34
  • 46
  • if i put the str/split expr outside when expr, it works. (do (when true ; also tried with false) (require '[clojure.string :as str])) (str/split "Clojure is awesome!" #" ")). Maybe it's because the ns resolution happens per expression. – mavbozo Feb 15 '15 at 08:17
  • @deadghost thanks for the feedback. Just to clarify: I wanted to avoid `require`'ing the helper library if the application was in production - i.e. the helper library is only to be used in development. – Integralist Feb 15 '15 at 10:10
  • @deadghost I've added an update to the bottom of my question that clarifies some changes I've made and also attempting to use `ns-resolve` (which didn't work) – Integralist Feb 15 '15 at 10:48
  • @Integralist What do you gain by only `require`ing the helper library in development? – deadghost Feb 15 '15 at 11:47
  • @deadghost I assumed it would be slightly more performant, or is that just nonsense? Because Clojure is effectively a compiled language (e.g. compiled into a deployable jar) I need to think about things like this differently (compared to a scripted language like Ruby). But I still would think it's better to be explicit and to have the code highlight the fact a specific library is only used in development, rather than just load it outright with no explanation (but that's just my own personal opinion). – Integralist Feb 16 '15 at 08:59
2

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

mavbozo
  • 1,161
  • 8
  • 10
  • 1
    Thanks @mavbozo - it seems that the problem is due to how Clojure treats `(do)` blocks and all expressions within it are compiled at the same time. Where as separating them out into two different blocks means the compiler doesn't hit the error. – Integralist Feb 16 '15 at 09:00
  • Update: this works when running `lein ring server` normally. But once this app was packaged up into a jar and the jar was run from a Docker container the problem of the require call and the reference to the namespace came back. I decided in that instance to just specify the dependency within the ns declaration (because I realised it didn't really matter; because the app was compiled as a whole and so there was no performance issues to avoid as such) – Integralist Feb 19 '15 at 12:29
  • @Integralist does the jar run well locally in your dev machine? i packaged the app containing code above to a jar and it runs in my dev machine. – mavbozo Feb 19 '15 at 14:37
  • I've about to open a separate question for this discussion as I feel it's important to understand what I'm missing – Integralist Feb 20 '15 at 13:21
  • http://stackoverflow.com/questions/28630412/how-get-a-clojure-compojure-app-running-within-docker – Integralist Feb 20 '15 at 13:44