following the Database Access chapter in Web Development with Clojure, I manage to recreate all the functions in the project. Up until the report generation section: lein new luminus reporting-example +postgres
works, the migration works, then recreating the db.core namespace. when I start the repl and call for (mount.core/start #'reporting-example.db.core/*db*)
I get an exception: >Caused by java.lang.ClassCastException
mount.core.DerefableState cannot be cast to clojure.lang.IFn
the project copied from the Pragmatic Programmer website works. I understand that it has to do with the actual template that Luminus generates. I tried replacing some of the files with the ones from the finished project but I get into new issues. Is there a simple way to make this work with the current template @yogthos?
@adamgefen the error means that the db connection state hasn't been started
mount resolves components using the namespace hierarchy, so if a namespace isn't referenced anywhere the resources defined using defstate
will not be started
ah hang on, just reread the error looks like a different problem
what does your *db*
definition look like?
I'll rebuild it as I have all my steps documented and I'll post it in a few minutes, as by now I have replaced every bit of code.
hold on if you can...
sure thing
>ClassCastException mount.core.DerefableState cannot be cast to clojure.lang.IFn reporting-example.db.core/eval14346/fn--14347 (core.clj:20)
that's the original issue
(conman/bind-connection *db* "sql/queries.sql")
yeah that error typically occurs when the state isn't started
where are you running the above command?
that is in db.core
can you just paste db.core here
one problem could be that you have some code that's trying to use the connection at compile time
>(ns reporting-example.db.core (:require [cheshire.core :refer [generate-string parse-string]] [clj-time.jdbc] [clojure.java.jdbc :as jdbc] [clojure.tools.logging :as log] [conman.core :as conman] [reporting-example.config :refer [env]] [mount.core :refer [defstate]]) (:import org.postgresql.util.PGobject java.sql.Array clojure.lang.IPersistentMap clojure.lang.IPersistentVector [java.sql BatchUpdateException PreparedStatement Date Timestamp])) (defstate ^:dynamic db :start (if-let [jdbc-url (env :database-url)] (conman/connect! {:jdbc-url jdbc-url}) (do (log/warn "database connection URL was not found, please set :database-url in your config, e.g: dev-config.edn") db)) :stop (conman/disconnect! db)) (conman/bind-connection db "sql/queries.sql") (defn to-date [sql-date] (-> sql-date (.getTime) (java.util.Date))) (extend-protocol jdbc/IResultSetReadColumn Array (result-set-read-column [v ] (vec (.getArray v))) Date (result-set-read-column [v ] (to-date v)) Timestamp (result-set-read-column [v ] (to-date v)) PGobject (result-set-read-column [pgobj metadata index] (let [type (.getType pgobj) value (.getValue pgobj)] (case type "json" (parse-string value true) "jsonb" (parse-string value true) "citext" (str value) value)))) (extend-type java.util.Date jdbc/ISQLParameter (set-parameter [v ^PreparedStatement stmt idx] (.setTimestamp stmt idx (Timestamp. (.getTime v))))) (defn to-pg-json [value] (doto (PGobject.) (.setType "jsonb") (.setValue (generate-string value)))) (extend-type clojure.lang.IPersistentVector jdbc/ISQLParameter (set-parameter [v ^java.sql.PreparedStatement stmt ^long idx] (let [conn (.getConnection stmt) meta (.getParameterMetaData stmt) type-name (.getParameterTypeName meta idx)] (if-let [elem-type (when (= (first type-name) \_) (apply str (rest type-name)))] (.setObject stmt idx (.createArrayOf conn elem-type (to-array v))) (.setObject stmt idx (to-pg-json v)))))) (extend-protocol jdbc/ISQLValue IPersistentMap (sql-value [value] (to-pg-json value)) IPersistentVector (sql-value [value] (to-pg-json value)))
that looks right
where does the trace for the error point to in the project?
where do I check that?
where do you see the exception?
if you're running the app using lein run
that should show the error in the terminal
yes
that's right
is there a stacktrace somewhere?
I could run it in cider and get a stack
just a minute
and what does the function around line 94 look like in core.cljc
might be easier if you put the project up on github or something so I can take a look locally
that's the let
statement
are you starting mount states manually there?
There was nowhere that I defined start and stop, So whatever comes in the box with a new luminus +postgres. plus the protocol extension as in the tutorial.
but default luminus template doesn't generate the function above, the resources are started in core.clj
using the following function:
(defn start-app [args]
(doseq [component (-> args
(parse-opts cli-options)
mount/start-with-args
:started)]
(log/info component "started"))
(.addShutdownHook (Runtime/getRuntime) (Thread. stop-app)))
the *db*
resource relies on the env
resource from the config.clj
namespace
and that resource must be started first
that code comes from mount
ah ok, so where does the stack trace to in your project
how are you getting the error
is it happening when the app starts, or when you run a command in the repl?
it happens when I run (mount.core/start #'reporting-example.db.core/*db*)
if you want to start the app from the repl, u just need to run (start)
. The fn is on env/dev/user.clj
. mount.core/start
is not working probably because the config state is not started and is a dependency for the db state.
Thanks! works perfectly! @jmayaalv
sorry, I thought I said...
and has the start-app
function from the core
namespace been run before then?
if you're running the app using lein run
, and then connecting to nrepl that should cause start-app
to run
works
brilliant, thanks.
so what was it? 🙂
I have no idea... I have to retrace what I did wrong.
Because I did try with lein run and without.
my recommendation is to always start the app using lein run
, and then connect to the nrepl after
I guess I had in the back of my mind the idea that it can't work with all the different dependencies to when the book was written
this way it's closest to the way it would run standalone during deployment
and it ensures that all the resources are started correctly
Thanks Dmitri, The book is very helpful.
thanks, glad to hear it