Clojure: How to restore REPL after exception? - clojure

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

Related

next.jdbc - unable to find valid certification path to requested target

The error
; Execution error (SunCertPathBuilderException) at
sun.security.provider.certpath.SunCertPathBuilder/build
(SunCertPathBuilder.java:141). ; unable to find valid certification
path to requested target
The code (Clojure)
(ns backend.core
(:require [next.jdbc :as jdbc]))
(defn -main
"I don't do a whole lot ... yet."
[& args]
(println "Hello, World!"))
(def db {:dbtype "sqlserver" :dbname "dbname" :user "MY_USER" :password "MY_PASSWORD" :host "MY_HOST" :port 1234})
(def datasource (jdbc/get-datasource db))
(defn create-product
"Create a product."
[name ds]
(jdbc/execute! ds [(str "insert into dbo.product (name) value('" name "')")]))
(comment
(create-product "my-product" datasource))
I'm playing around with clojure/sql server/next.jdbc and trying to work with my distant SQL Server 2017, but this error appear...
It look like I need some certificate. Is it the case? How can I generate it? How can I install it on my dev PC? Should it be install in a specific place?
I fixed my issue by changing the content of the (def db ...). Here is the updated code working.
(ns backend.core
(:require [next.jdbc :as jdbc]))
(defn -main
"I don't do a whole lot ... yet."
[& args]
(println "Hello, World!"))
(def db {:jdbcUrl "jdbc:sqlserver://MY_DISTANT_HOST:12345;databaseName=DB_NAME;user=USERNAME;password=MY_PASSWORD;encrypt=true;trustServerCertificate=true"})
(def datasource (jdbc/get-datasource db))
(defn create-product
"Create a product."
[name ds]
(with-open [connection (jdbc/get-connection ds)]
(jdbc/execute! connection ["insert into dbo.product (name) values (?)" name] {:return-keys true})))
(comment
(create-product "my-product" datasource))
You can find more information about jdbc connection string here: https://learn.microsoft.com/en-us/sql/connect/jdbc/building-the-connection-url?view=sql-server-ver15
I too ran into a second issue after updating the data-source configuration. The error was:
no mssql-jdbc_auth-8.2.1.x64 in java.library.path
I only needed to download zip file here https://learn.microsoft.com/en-us/sql/connect/jdbc/download-microsoft-jdbc-driver-for-sql-server?view=sql-server-ver15, unzip it and follow instructions inside install.txt and finally restart my IDE (VS Code).

lein uberjar cannot find an implementation defined in deftype

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)

Slamhound fails to reconstruct my ns forms

I have created a project with two namespaces, familja-moderne.core and familja-moderne.visualization.svg.
src/familja_moderne/core.clj
(ns familja-moderne.core
(:require [clojure.set :as set])
(:import (java.io ByteArrayInputStream)))
(def try-out
[(set/map-invert
{:1 :2
:3 :4})
(ByteArrayInputStream. (.getBytes "myBytes"))
(familja-moderne.visualization.svg/points heists)])
src/familja_moderne/visualization/svg.clj
(ns familja-moderne.visualization.svg
(:refer-clojure :exclude [max min]))
(def some-map {:this :that
:foo :bar})
(def many-dependencies
{:something (ByteArrayInputStream. (.getBytes "something"))
:another-map (set/map-invert some-map)})
When I run
lein slamhound code/familja-moderne/src/familja_moderne/visualization/svg.clj
I get
WARNING: ex-info already refers to: #'clojure.core/ex-info in namespace: slingshot.ex-info, being replaced by: #'slingshot.ex-info/ex-info
WARNING: ex-data already refers to: #'clojure.core/ex-data in namespace: slingshot.ex-info, being replaced by: #'slingshot.ex-info/ex-data
which I don't understand but it works and my ns form is reconstructed as
(ns familja-moderne.visualization.svg
(:require [clojure.set :as set])
(:import (java.io ByteArrayInputStream))
(:refer-clojure :exclude [max min]))
Running
lein slamhound code/familja-moderne/src/familja_moderne/core.clj
fails and I get the following messages
WARNING: ex-info already refers to: #'clojure.core/ex-info in namespace: slingshot.ex-info, being replaced by: #'slingshot.ex-info/ex-info
WARNING: ex-data already refers to: #'clojure.core/ex-data in namespace: slingshot.ex-info, being replaced by: #'slingshot.ex-info/ex-data
Failed to reconstruct: #<File code/familja-moderne/src/familja_moderne/core.clj>
java.lang.ClassNotFoundException: familja-moderne.visualization.svg, compiling:(NO_SOURCE_PATH:0:0)
Running
lein slamhound code/familja-moderne/src/familja_moderne/
which is supposed to reconstruct the ns forms in both namespaces results in something different
WARNING: ex-info already refers to: #'clojure.core/ex-info in namespace: slingshot.ex-info, being replaced by: #'slingshot.ex-info/ex-info
WARNING: ex-data already refers to: #'clojure.core/ex-data in namespace: slingshot.ex-info, being replaced by: #'slingshot.ex-info/ex-data
Failed to reconstruct: #<File code/familja-moderne/src/familja_moderne/core.clj>
Couldn't resolve familja-moderne.visualization.svg, got as far as {:import #{java.io.ByteArrayInputStream}, :alias {clojure.set set}, :old {:load nil, :exclude {}, :xrefer #{}, :require #{}, :refer-all #{}, :verbose #{}, :rename {}, :alias {clojure.set set}, :reload #{}, :reload-all #{}, :gen-class nil, :import #{java.io.ByteArrayInputStream}, :refer {}}, :meta nil, :name familja-moderne.core}
If I remove any reference to familja-moderne.visualization.svg from familja-moderne.core it works OK.
I was having the same issue and opened a PR to Slamhound with a fix: https://github.com/technomancy/slamhound/pull/87.
You can try it by doing a lein install from the dotted-alias branch in my fork.
My theory is that this issue happens because of the clojure bug CLJ-1403 which makes the exception to be thrown when Slamhound is trying to "regrow" the ns form.
Hope that helps!

Clojurescript clojure.set not working

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.

Clojure symbol evaluation error

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