luminus

manandearth 2018-09-23T12:25:20.000100Z

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

manandearth 2018-09-23T12:27:34.000100Z

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?

yogthos 2018-09-23T13:00:24.000100Z

@adamgefen the error means that the db connection state hasn't been started

yogthos 2018-09-23T13:01:17.000100Z

mount resolves components using the namespace hierarchy, so if a namespace isn't referenced anywhere the resources defined using defstate will not be started

yogthos 2018-09-23T13:02:01.000100Z

ah hang on, just reread the error looks like a different problem

yogthos 2018-09-23T13:02:18.000100Z

what does your *db* definition look like?

manandearth 2018-09-23T13:03:14.000100Z

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.

manandearth 2018-09-23T13:03:20.000100Z

hold on if you can...

yogthos 2018-09-23T13:12:58.000100Z

sure thing

manandearth 2018-09-23T13:23:15.000100Z

>ClassCastException mount.core.DerefableState cannot be cast to clojure.lang.IFn reporting-example.db.core/eval14346/fn--14347 (core.clj:20)

manandearth 2018-09-23T13:23:23.000100Z

that's the original issue

manandearth 2018-09-23T13:24:07.000100Z

(conman/bind-connection *db* "sql/queries.sql")

yogthos 2018-09-23T13:24:26.000100Z

yeah that error typically occurs when the state isn't started

yogthos 2018-09-23T13:24:36.000100Z

where are you running the above command?

manandearth 2018-09-23T13:24:58.000100Z

that is in db.core

yogthos 2018-09-23T13:25:50.000100Z

can you just paste db.core here

yogthos 2018-09-23T13:26:09.000200Z

one problem could be that you have some code that's trying to use the connection at compile time

manandearth 2018-09-23T13:26:39.000100Z

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

yogthos 2018-09-23T13:27:26.000100Z

that looks right

yogthos 2018-09-23T13:28:57.000100Z

where does the trace for the error point to in the project?

manandearth 2018-09-23T13:30:24.000100Z

where do I check that?

yogthos 2018-09-23T13:30:54.000100Z

where do you see the exception?

yogthos 2018-09-23T13:31:16.000100Z

if you're running the app using lein run that should show the error in the terminal

manandearth 2018-09-23T13:31:23.000100Z

yes

manandearth 2018-09-23T13:31:31.000200Z

that's right

yogthos 2018-09-23T13:31:40.000100Z

is there a stacktrace somewhere?

manandearth 2018-09-23T13:31:57.000100Z

I could run it in cider and get a stack

manandearth 2018-09-23T13:32:03.000100Z

just a minute

manandearth 2018-09-23T13:35:23.000100Z

yogthos 2018-09-23T13:37:11.000100Z

and what does the function around line 94 look like in core.cljc

yogthos 2018-09-23T13:37:38.000100Z

might be easier if you put the project up on github or something so I can take a look locally

manandearth 2018-09-23T13:39:22.000100Z

manandearth 2018-09-23T13:39:45.000100Z

that's the let statement

yogthos 2018-09-23T13:43:09.000100Z

are you starting mount states manually there?

manandearth 2018-09-23T13:45:28.000100Z

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.

yogthos 2018-09-23T13:46:58.000100Z

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

yogthos 2018-09-23T13:47:55.000100Z

the *db* resource relies on the env resource from the config.clj namespace

yogthos 2018-09-23T13:48:06.000100Z

and that resource must be started first

manandearth 2018-09-23T13:48:08.000100Z

that code comes from mount

yogthos 2018-09-23T13:48:27.000100Z

ah ok, so where does the stack trace to in your project

yogthos 2018-09-23T13:49:09.000100Z

how are you getting the error

yogthos 2018-09-23T13:49:20.000100Z

is it happening when the app starts, or when you run a command in the repl?

manandearth 2018-09-23T13:49:20.000200Z

manandearth 2018-09-23T13:50:22.000100Z

it happens when I run (mount.core/start #'reporting-example.db.core/*db*)

2018-09-24T08:02:55.000100Z

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.

manandearth 2018-09-25T10:52:17.000100Z

Thanks! works perfectly! @jmayaalv

manandearth 2018-09-23T13:51:05.000200Z

sorry, I thought I said...

yogthos 2018-09-23T13:51:07.000100Z

and has the start-app function from the core namespace been run before then?

yogthos 2018-09-23T13:52:05.000100Z

if you're running the app using lein run, and then connecting to nrepl that should cause start-app to run

manandearth 2018-09-23T13:53:31.000100Z

works

manandearth 2018-09-23T13:53:37.000100Z

brilliant, thanks.

yogthos 2018-09-23T13:53:43.000100Z

so what was it? 🙂

manandearth 2018-09-23T13:54:09.000100Z

I have no idea... I have to retrace what I did wrong.

manandearth 2018-09-23T13:54:41.000100Z

Because I did try with lein run and without.

yogthos 2018-09-23T13:55:01.000100Z

my recommendation is to always start the app using lein run, and then connect to the nrepl after

manandearth 2018-09-23T13:55:25.000100Z

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

yogthos 2018-09-23T13:55:26.000100Z

this way it's closest to the way it would run standalone during deployment

yogthos 2018-09-23T13:55:55.000100Z

and it ensures that all the resources are started correctly

manandearth 2018-09-23T13:56:11.000100Z

Thanks Dmitri, The book is very helpful.

yogthos 2018-09-23T13:56:24.000100Z

thanks, glad to hear it

👍 1