So I currently have this code:
(ns contact-form.core
(:gen-class))
(def foo "Hello World!")
(defn some-func [a-symbol]
(println (str a-symbol "'s value is: " (eval a-symbol))))
(defn -main [& args]
(some-func 'foo))
After I do C-c C-k in Emacs, I get the following output:
contact-form.core> (-main)
foo's value is: Hello World!
nil
But when I do lein uberjar and run the resulting jar file, I get an error:
Exception in thread "main" java.lang.RuntimeException: Unable to resolve symbol: foo in this context, compiling:(NO_SOURCE_PATH:0)
at clojure.lang.Compiler.analyze(Compiler.java:6235)
at clojure.lang.Compiler.analyze(Compiler.java:6177)
at clojure.lang.Compiler.eval(Compiler.java:6469)
at clojure.lang.Compiler.eval(Compiler.java:6431)
at clojure.core$eval.invoke(core.clj:2795)
at contact_form.core$some_func.invoke(core.clj:7)
at contact_form.core$_main.doInvoke(core.clj:10)
at clojure.lang.RestFn.invoke(RestFn.java:397)
at clojure.lang.AFn.applyToHelper(AFn.java:159)
at clojure.lang.RestFn.applyTo(RestFn.java:132)
at contact_form.core.main(Unknown Source)
Caused by: java.lang.RuntimeException: Unable to resolve symbol: foo in this context
at clojure.lang.Util.runtimeException(Util.java:156)
at clojure.lang.Compiler.resolveIn(Compiler.java:6720)
at clojure.lang.Compiler.resolve(Compiler.java:6664)
at clojure.lang.Compiler.analyzeSymbol(Compiler.java:6625)
at clojure.lang.Compiler.analyze(Compiler.java:6198)
... 10 more
So I have two questions:
Why does the uberjar not function exactly the same as the REPL?
What can I do to fix this problem?
1. Why does uberjar function differently to the REPL?
A cause of the error "NO_SOURCE_PATH" is that you are not presently in the namespace that defined 'foo.
To illustrate: if I evaluate your code in my REPL and execute it, it places me in the contact-form.core namespace as you would expect because (ns contact-form.core) is evaluated by the REPL, but if I switch to the user namespace and call -main I can produce the same error:
contact-form.core=> (-main)
foo's value is: Hello World!
nil
contact-form.core=> (ns user)
nil
user=> (contact-form.core/-main)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: foo in this context, compiling:(NO_SOURCE_PATH:120)
user=>
So, by specifying your entry point to main for standalone uberjar execution (outside the REPL), it is equivalent to calling (contact-form.core/-main) from the default namespace in your jar which is clojure.core, because (ns contact-form.core) has not been evaluated. Result: main can be executed with a fully qualified (namespaced) path to the function, but none of the symbols from contact-form.core are available in the current default namespace.
2. The fix
The solution would be to explicitly switch to your namespace first.:
(defn -main [& args]
(use 'contact-form.core)
(some-func 'foo))
Related
I am trying to get the namespace associated with the code actually running.
I created a template project using 'lein new app test':
(ns test.core
(:gen-class))
(defn -main
[& args]
(println (ns-name *ns*)))
When using the repl, *ns* evaluates to test.core:
echo "(-main)" | lein repl
nREPL server started on port 37435 on host 127.0.0.1 - nrepl://127.0.0.1:37435
REPL-y 0.4.4, nREPL 0.7.0
Clojure 1.10.1
OpenJDK 64-Bit Server VM 1.8.0_265-b01
Docs: (doc function-name-here)
(find-doc "part-of-name-here")
Source: (source function-name-here)
Javadoc: (javadoc java-object-or-class-here)
Exit: Control+D or (exit) or (quit)
Results: Stored in vars *1, *2, *3, an exception in *e
test.core=> (-main)
test.core
nil
test.core=> Bye for now!
But when using lein run, I get user namespace:
lein run
user
Why such a behavior ?
How can I get the namespace of a piece of code like in the repl ?
A few things to note:
*ns* is bound to the namespace wherein the expression is evaluated
user is the default namespace
when you call (test.core/-main) from the user namespace, *ns* is bound to user, not test.core.
If you would like to print the namespace in which -main was defined, you can use a top-level expression:
(ns test.core)
(def current-ns *ns*)
(defn -main [& args]
(println (ns-name current-ns)))
or use var metadata:
(ns test.core)
(defn -main [& args]
(println (ns-name (:ns (meta #'-main)))))
What's the proper workflow in cases when you have an error after you restart/reload your app using mount?
An example:
user> (restart)
#error {
:cause "Unable to resolve symbol: account-database in this context"
:via
[{:type clojure.lang.Compiler$CompilerException
:message "Syntax error compiling at (app/model/session.clj:55:39)."
:data #:clojure.error{:phase :compile-syntax-check, :line 55, :column 39, :source "app/model/session.clj"}
:at [clojure.lang.Compiler analyze "Compiler.java" 6808]}
{:type java.lang.RuntimeException
:message "Unable to resolve symbol: account-database in this context"
:at [clojure.lang.Util runtimeException "Util.java" 221]}]
:trace
[[clojure.lang.Util runtimeException "Util.java" 221]
Reloadable Clojure REPL
user> (restart)
Syntax error compiling at (*cider-repl Projects/pollcro:localhost:43993(clj)*:597:7).
Unable to resolve symbol: restart in this context
user> (start)
Syntax error compiling at (*cider-repl Projects/pollcro:localhost:43993(clj)*:600:7).
Unable to resolve symbol: start in this context
After fixing an error, I can not (restart) this reply anymore due to the following error:
Unable to resolve symbol: restart in this context
Is it always like this or am I doing something wrong with my REPL workflow?
As a workaround, I cider-quit my REPL session and start it again but that's very annoying.
It seems I can use mount/start after fixing an error:
user> (mount/start)
{:started
["#'app.server-components.config/config"
"#'app.model.database/pool"
"#'app.model.mock-database/conn"]}
But it completely loses my user namespace scope, REPL doesn't see any of its methods + I can't send user namespace to repl because of the following error:
Syntax error compiling at (app/server_components/pathom.clj:1:1).
namespace 'app.model.session' not found
("src/main" "src/dev" "src/test")#function[expound.alpha/printer]nil#'user/start#'user/stop#'user/restartnil
That's what my user namespace looks like:
(ns user
(:require
[clojure.tools.namespace.repl :as tools-ns :refer [set-refresh-dirs]]
[expound.alpha :as expound]
[clojure.spec.alpha :as s]
[mount.core :as mount]
;; this is the top-level dependent component...mount will find the rest via ns requires
[app.server-components.http-server :refer [http-server]]))
;; ==================== SERVER ====================
(set-refresh-dirs "src/main" "src/dev" "src/test")
;; Change the default output of spec to be more readable
(alter-var-root #'s/*explain-out* (constantly expound/printer))
(defn start
"Start the web server"
[] (mount/start))
(defn stop
"Stop the web server"
[] (mount/stop))
(defn restart
"Stop, reload code, and restart the server. If there is a compile error, use:
(tools-ns/refresh)
to recompile, and then use `start` once things are good."
[]
(stop)
(tools-ns/refresh :after 'user/start))
Here's the workaround that seems to work in case of an error:
the content of a fixed file need to be sent to the REPL
switch back to user namespace
send the content of your user namespace to the REPL
there is no 4 - you just got your REPL back.
At this point you can use your regular (start), (stop), (restart).
In our Clojure codebase we have a protocol:
(ns project.repository)
(defprotocol Repository
(index [this fields unique]))
A type
(ns project.mongo (:require
[monger.collection :as mc]
[monger.core :as mg]
[project.repository :refer :all]))
(deftype MongoRepository [db collection-name]
Repository
(index [this fields unique]
(mc/ensure-index db collection-name fields {:unique unique})))
(defn mongo-repository [db coll] (MongoRepository. db coll))
(def mongo-db ((mg/connect-via-uri "mongodb://127.0.0.1/bots") :db))
And an instantiation
(ns project.users (:require
[lp-bots.storages.repository :refer :all]
[lp-bots.storages.mongo :refer [mongo-repository mongo-db]]))
(def users-storage (mongo-repository mongo-db "users"))
(index users-storage [:key1 :key2] true)
This works fine when used interactively from REPL or launched with lein run, but lein uberjar invariably throws an exception:
Exception in thread "main" java.lang.ExceptionInInitializerError, compiling:(/tmp/form-init118199196859405970.clj:1:72)
...
Caused by: java.lang.ExceptionInInitializerError
...
Caused by: java.lang.IllegalArgumentException: No implementation of method: :index of protocol: #'project.repository/Repository found for class: project.mongo.MongoRepository
at clojure.core$_cache_protocol_fn.invokeStatic(core_deftype.clj:568)
at clojure.core$_cache_protocol_fn.invoke(core_deftype.clj:560)
at project.repository$fn__557$G__514__566.invoke(repository.clj)
at project.users__init.load(Unknown Source)
at project.users__init.<clinit>(Unknown Source)
The weirdest part is that the problem goes away when (index) is called in a (let):
(def users-storage
(let [u (mongo-repository mongo-db "leads")]
(index u [:key1 :key2] true)
u))
Any thoughts on what might be causing the difference?
I would suspect that it's something to do with trying to connect to MongoDB when compiling the project.mongo ns, or the project.users ns. These lines should probably be in a function call that's called at startup, rather than as the code is loaded:
(def mongo-db ((mg/connect-via-uri "mongodb://127.0.0.1/bots") :db))
and
(index users-storage [:key1 :key2] true)
I have this code to get data from sumo logic and other services.
core.clj has this, which parses the arguments and routes it to the right function in route.clj
(def cli-options
[
["-a" "--app APPNAME" "set app. app can be:
sumologic or jira"]
["-?" "--help"]
])
(defn -main
[& args]
(let [{:keys [options summary errors arguments]} (parse-opts args cli-options)]
(cond
(:app options) (route/to (:app options) options arguments)
:else (print_usage summary))))
route.clj has this:
(defn to
[app options arguments]
(case app
"jira" (jira/respond options arguments)
"sumologic" (sumo/respond)))
And then sumo.clj has this. there are other functions, of course, but showing just the relevant parts.
(defn get-env-var
[var]
(let [result (System/getenv var)]
(if (nil? result)
(throw (Exception. (str "Environment variable: " var " not set. Aborting")))
result)))
(def access_key
(let [user (get-env-var "SUMO_ID")
pass (get-env-var "SUMO_KEY")]
[user pass]))
(defn respond
[]
(let [{:keys [status body error] :as response} (http/get endpoint rest-options)]
(if error
(println error)
(print-response body))))
When I run the program using leiningen as lein run -- -? or even just lein run, I get this error, even though I haven't explicitly called the sumologic function. What am I doing wrong and what are things that I can do differently?
Caused by: java.lang.Exception: Environment variable: SUMO_KEY not set. Aborting
at clarion.sumo$get_env_var.invoke(sumo.clj:14)
at clarion.sumo$fn__3765.invoke(sumo.clj:19)
at clojure.lang.AFn.applyToHelper(AFn.java:152)
at clojure.lang.AFn.applyTo(AFn.java:144)
at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3553)
You have def'd access_key so it is being evaluated when you load the application. You probably want to make it a function instead.
I've referenced ClojureScript clojure.set? and can only get the docstring to come up, but actually running any of the clojure.set function doesn't work. Using ClojureScript 0.0-3126 on Opera 28.0 and/or Chrome 41.0.2272.101 m on Windows 7 x64.
Any ideas on how to correct this?
thanks
ClojureScript:cljs.user> (require '[clojure.set])
nil
ClojureScript:cljs.user> (doc clojure.set/union)
-------------------------
clojure.set/union
([] [s1] [s1 s2] [s1 s2 & sets])
Return a set that is the union of the input sets
nil
ClojureScript:cljs.user> (clojure.set/union #{:a} #{:b})
TypeError: Cannot read property 'union' of undefined
TypeError: Cannot read property 'union' of undefined
at eval (eval at <anonymous> (http://localhost:8888/js/coreweb.js:45250:260), <anonymous>:1:100)
at eval (eval at <anonymous> (http://localhost:8888/js/coreweb.js:45250:260), <anonymous>:9:3)
at eval (ev al at <anonymous> (http://localhost:8888/js/coreweb.js:45250:260), <anonymous>:14:4)
at http://localhost:8888/js/coreweb.js:45250:255
at clojure$browser$repl$evaluate_javascript (http://localhost:8888/js/coreweb.js:45256:4)
at Object.callback (http://localhost:8888/js/coreweb.js:45421:181)
at goog.messaging.AbstractChannel.deliver (http://localhost:8888/js/coreweb.js:42499:13)
at goog.net.xpc.CrossPageChannel.xpcDeliver (http://localhost:8888/js/coreweb.js:43463:14)
at Function.goog.net.xpc.NativeMessagingTransport.messageReceived_ (http://localhost:8888/js/coreweb.js:42859:13)
at Object.goog.events.fireListener (http://localhost:8888/js/coreweb.js:39835:21)
That's more personal research than an answer. Still, I did reproduce your problem in Chrome (on MAC) taking the ClojureScript Quick Start.
If the cljs server repl file is as follow (i.e. there is no require on clojure.set):
(ns hello-world.core
(:require [clojure.browser.repl :as repl]))
(defonce conn
(repl/connect "http://localhost:9000/repl"))
(enable-console-print!)
(println "Hello world!")
and the repl.clj is:
(require 'cljs.repl)
(require 'cljs.closure)
(require 'cljs.repl.browser)
(cljs.closure/build "src"
{:main 'hello-world.core
:output-to "out/main.js"
:verbose true})
(cljs.repl/repl (cljs.repl.browser/repl-env)
:watch "src"
:output-dir "out")
Then starting the repl in a terminal with:
rlwrap java -cp cljs.jar:src clojure.main repl.clj
And connecting the browser on http://localhost:9000, then this works:
ClojureScript:cljs.user> (require '[clojure.set])
nil
ClojureScript:cljs.user> (clojure.set/union #{6} #{9} #{7})
#{7 6 9}
Now, if you reload the server page http://localhost:9000, and try again, I had the problem (doc is available on the symbol, but the var is not there anymore):
ClojureScript:cljs.user> (doc clojure.set/union)
-------------------------
clojure.set/union
([] [s1] [s1 s2] [s1 s2 & sets])
Return a set that is the union of the input sets
nil
ClojureScript:cljs.user> (clojure.set/union #{6} #{9} #{7})
TypeError: Cannot read property 'union' of undefined
TypeError: Cannot read property 'union' of undefined
at eval (eval at <anonymous> (http://localhost:9000/out/clojure/browser/repl.js:42:272), <anonymous>:1:100)
If you modify the cljs server file to require clojure.set, i.e
(ns hello-world.core
(:require [clojure.browser.repl :as repl]
[clojure.set]))
Then, it looks like you still need to require clojure.set from the repl in order to use its functions, but then, you can reload the server page and it still works in the repl.