This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-09-23
Channels
- # 100-days-of-code (5)
- # announcements (3)
- # beginners (68)
- # cider (2)
- # cljdoc (2)
- # cljs-dev (10)
- # cljsjs (2)
- # clojure (40)
- # clojure-austin (2)
- # clojure-dev (17)
- # clojure-italy (8)
- # clojure-spec (4)
- # clojure-uk (9)
- # clojurebridge (1)
- # clojurescript (48)
- # datomic (4)
- # emacs (4)
- # figwheel-main (93)
- # fulcro (8)
- # hyperfiddle (33)
- # jobs-discuss (3)
- # luminus (60)
- # off-topic (66)
- # onyx (42)
- # pedestal (11)
- # re-frame (35)
- # reagent (1)
- # reitit (39)
- # shadow-cljs (30)
- # specter (27)
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
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...
>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")
that is in db.core
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)))
where do I check that?
that's right
I could run it in cider and get a stack
just a minute
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
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)))
that code comes from mount
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! @U0J6U23FW
sorry, I thought I said...
if you're running the app using lein run
, and then connecting to nrepl that should cause start-app
to run
works
brilliant, thanks.
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
Thanks Dmitri, The book is very helpful.