"Web Development with Clojure": No such var: db/get-user
clojure, database, dependencies, localhost, web-applications
Solution
Note the first line in the stacktrace: `Exception in thread "main" java.lang.RuntimeException: No such var: db/get-user, compiling:(guestbook/routes/auth.clj:38:14)`
It's looked for and couldn't find `db/get-user` when compiling `auth.clj` and states the problem is on line 38 column 14. You aliased `db` to `guestbook.models.db`. Let's look for a `get-user` function in `guestbook/models/db.clj`. It's not there! Add the function to `db.clj` or remove all usage of that function from `auth.clj`.
Problem
I am using the "Web Development with Clojure" book to make a tutorial guestbook application and have hit a snag. It seems that some of the libraries and their functions have been updated since the release of this book causing a slightly bumpy introduction to web-development for me. I have been able to work around most of the issues by updating dependencies when I can or using older versions of libraries when I can't, but this strategy has reached its limit and I have run into a thorny issue. I get this stack trace when I try to run my app on a local-host server: ``` Exception in thread "main" java.lang.RuntimeException: No such var: db/get-user, compiling:(guestbook/routes/auth.clj:38:14) at clojure.lang.Compiler.analyze(Compiler.java:6380) at clojure.lang.Compiler.analyze(Compiler.java:6322) at clojure.lang.Compiler$InvokeExpr.parse(Compiler.java:3573) at clojure.lang.Compiler.analyzeSeq(Compiler.java:6562) at clojure.lang.Compiler.analyze(Compiler.java:6361) at clojure.lang.Compiler.access$100(Compiler.java:37) at clojure.lang.Compiler$LetExpr$Parser.parse(Compiler.java:5973) at clojure.lang.Compiler.analyzeSeq(Compiler.java:6560) at clojure.lang.Compiler.analyze(Compiler.java:6361) at clojure.lang.Compiler.analyzeSeq(Compiler.java:6548) at clojure.lang.Compiler.analyze(Compiler.java:6361) at clojure.lang.Compiler.analyze(Compiler.java:6322) at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:5708) at clojure.lang.Compiler$FnMethod.parse(Compiler.java:5139) at clojure.lang.Compiler$FnExpr.parse(Compiler.java:3751) at clojure.lang.Compiler.analyzeSeq(Compiler.java:6558) at clojure.lang.Compiler.analyze(Compiler.java:6361) at clojure.lang.Compiler.analyzeSeq(Compiler.java:6548) at clojure.lang.Compiler.analyze(Compiler.java:6361) at clojure.lang.Compiler.access$100(Compiler.java:37) at clojure.lang.Compiler$DefExpr$Parser.parse(Compiler.java:529) at clojure.lang.Compiler.analyzeSeq(Compiler.java:6560) at clojure.lang.Compiler.analyze(Compiler.java:6361) at clojure.lang.Compiler.analyze(Compiler.java:6322) at clojure.lang.Compiler.compile1(Compiler.java:7148) at clojure.lang.Compiler.compile(Compiler.java:7219) at clojure.lang.RT.compile(RT.java:398) at clojure.lang.RT.load(RT.java:438) at clojure.lang.RT.load(RT.java:411) at clojure.core$load$fn__5018.invoke(core.clj:5530) at clojure.core$load.doInvoke(core.clj:5529) at clojure.lang.RestFn.invoke(RestFn.java:408) at clojure.core$load_one.invoke(core.clj:5336) at clojure.core$load_lib$fn__4967.invoke(core.clj:5375) at clojure.core$load_lib.doInvoke(core.clj:5374) at clojure.lang.RestFn.applyTo(RestFn.java:142) at clojure.core$apply.invoke(core.clj:619) at clojure.core$load_libs.doInvoke(core.clj:5413) at clojure.lang.RestFn.applyTo(RestFn.java:137) at clojure.core$apply.invoke(core.clj:619) at clojure.core$require.doInvoke(core.clj:5496) at clojure.lang.RestFn.invoke(RestFn.java:457) at guestbook.handler$loading__4910__auto__.invoke(handler.clj:1) at clojure.lang.AFn.applyToHelper(AFn.java:159) at clojure.lang.AFn.applyTo(AFn.java:151) at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3458) at clojure.lang.Compiler.compile1(Compiler.java:7153) at clojure.lang.Compiler.compile1(Compiler.java:7143) at clojure.lang.Compiler.compile(Compiler.java:7219) at clojure.lang.RT.compile(RT.java:398) at clojure.lang.RT.load(RT.java:438) at clojure.lang.RT.load(RT.java:411) at clojure.core$load$fn__5018.invoke(core.clj:5530) at clojure.core$load.doInvoke(core.clj:5529) at clojure.lang.RestFn.invoke(RestFn.java:408) at clojure.core$load_one.invoke(core.clj:5336) at clojure.core$compile$fn__5023.invoke(core.clj:5541) at clojure.core$compile.invoke(core.clj:5540) at user$eval19.invoke(form-init6949279956252999274.clj:1) at clojure.lang.Compiler.eval(Compiler.java:6619) at clojure.lang.Compiler.eval(Compiler.java:6609) at clojure.lang.Compiler.load(Compiler.java:7064) at clojure.lang.Compiler.loadFile(Compiler.java:7020) at clojure.main$load_script.invoke(main.clj:294) at clojure.main$init_opt.invoke(main.clj:299) at clojure.main$initialize.invoke(main.clj:327) at clojure.main$null_opt.invoke(main.clj:362) at clojure.main$main.doInvoke(main.clj:440) at clojure.lang.RestFn.invoke(RestFn.java:421) at clojure.lang.Var.invoke(Var.java:419) at clojure.lang.AFn.applyToHelper(AFn.java:163) at clojure.lang.Var.applyTo(Var.java:532) at clojure.main.main(main.java:37) Caused by: java.lang.RuntimeException: No such var: db/get-user at clojure.lang.Util.runtimeException(Util.java:219) at clojure.lang.Compiler.resolveIn(Compiler.java:6848) at clojure.lang.Compiler.resolve(Compiler.java:6818) at clojure.lang.Compiler.analyzeSymbol(Compiler.java:6779) at clojure.lang.Compiler.analyze(Compiler.java:6343) ... 72 more Compilation failed: Subprocess failed ``` I think I probably have a couple of things loaded in here that I don't need, but my project.clj file looks like this: ``` (defproject guestbook "0.1.0-SNAPSHOT" :description "FIXME: write description" :url "http://example.com/FIXME" :dependencies [[org.clojure/clojure "1.5.1"] [compojure "1.1.6"] [hiccup "1.0.5"] [ring-server "0.3.1"] [lein-light-nrepl "0.0.15"] [org.clojure/clojurescript "0.0-2156"] [org.clojure/java.jdbc "0.2.3"] [org.xerial/sqlite-jdbc"3.7.2"] [lib-noir "0.8.0"]] :repl-options {:nrepl-middleware [lighttable.nrepl.handler/lighttable-ops]} :plugins [[lein-ring "0.8.7"] [lein-ancient "0.5.4"]] :ring {:handler guestbook.handler/app :init guestbook.handler/init :destroy guestbook.handler/destroy} :aot :all :profiles {:production {:ring {:open-browser? false, :stacktraces? false, :auto-reload? false}} :dev {:dependencies [[ring-mock "0.1.5"] [ring/ring-devel "1.2.1"]]}}) ``` home file: ``` (ns guestbook.routes.home (:require [compojure.core :refer :all] [guestbook.views.layout :as layout] [hiccup.form :refer :all] [guestbook.models.db :as db] [noir.session :as session])) (defn format-time [timestamp] (-> "dd/MM/yyyy" (java.text.SimpleDateFormat.) (.format timestamp))) (defn home [] (layout/common [:h1 "Guestbook"] [:p "Welcome to my guestbook"] [:hr] [:form] [:p "name"] [:input] [:p "Message"] [:textarea {:rows 10 :cols 40}])) (defn show-guests [] [:ul.guests (for [{:keys [message name timestamp]} (db/read-guests)] [:li [:blockquote message] [:p "-" [:cite name]] [:time (format-time timestamp)]])]) (defn home [& [name message error]] (layout/common [:h1 "Guestbook " (session/get :user)] [:p "Welcome to my guestbook"] [:p error] (show-guests) [:hr] (form-to [:post "/"] [:p "Name:" (text-field "name" name)] [:p "Message:" (text-area {:rows 10 :cols 40} "message" message)] (submit-button "comment")))) (defn save-message [name message] (cond (empty? name) (home name message "You forgot to leave a name, dumbass.") (empty? message) (home name message "Don't you have something to say?") :else (do (db/save-message name message) (home)))) (defroutes home-routes (GET "/" [] (home)) (POST "/" [name message] (save-message name message))) ``` handler: ``` (ns guestbook.handler (:use compojure.core ring.middleware.resource ring.middleware.file-info hiccup.middleware guestbook.routes.home) (:require [compojure.handler :as handler] [compojure.route :as route] [guestbook.models.db :as db] [guestbook.routes.auth :refer [auth-routes]])) (defn init [] (println "guestbook is starting") (if-not (.exists (java.io.File. "./db.sq3")) (db/create-guestbook-table))) (defn destroy [] (println "guestbook is shutting down")) (defroutes app-routes (route/resources "/") (route/not-found "Not Found")) (def app (-> (routes auth-routes home-routes app-routes) (handler/site) (wrap-base-url))) ``` database: ``` (ns guestbook.models.db (:require [clojure.java.jdbc :as sql]) (:import java.sql.DriverManager)) (def db {:classname "org.sqlite.JDBC", :subprotocol "sqlite", :subname "db.sq3"}) (defn create-guestbook-table [] (sql/with-connection db (sql/create-table :guestbook [:id "INTEGER PRIMARY KEY AUTOINCREMENT"] [:timestamp "TIMESTAMP DEFAULT CURRENT_TIMESTAMP"] [:name "TEXT"] [:message "TEXT"]) (sql/do-commands "CREATE INDEX timestamp_index ON guestbook (timestamp)"))) (defn read-guests [] (sql/with-connection db (sql/with-query-results res ["SELECT * FROM guestbook ORDER BY timestamp DESC"] (doall res)))) (defn save-message [name message] (sql/with-connection db (sql/insert-values :guestbook [:name :message :timestamp] [name message (new java.util.Date)]))) ``` authorization: ``` (ns guestbook.routes.auth (:require [compojure.core :refer [defroutes GET POST]] [guestbook.views.layout :as layout] [hiccup.form :refer [form-to label text-field password-field submit-button]] [noir.response :refer [redirect]] [noir.session :as session] [noir.validation :refer [rule errors? has-value? on-error]] [noir.util.crypt :as crypt] [guestbook.models.db :as db])) (defn format-error [[error]] [:p.error error]) (defn control [field name text] (list (on-error name format-error) (label name text) (field name) [:br])) (defn registration-page [] (layout/common (form-to [:post "/register"] (control text-field :id "screen name") (control password-field :pass "Password") (control password-field :pass1 "Retype Password") (submit-button "Create Account")))) (defn login-page [] (layout/common (form-to [:post "/login"] (control text-field :id "screen name") (control password-field :pass "Password") (submit-button "login")))) (defn handle-login [id pass] (let [user (db/get-user id)] (rule (has-value? id) [:id "screen name is required"]) (rule (has-value? pass) [:pass "password is required"]) (rule (and user (crypt/compare pass (:pass user))) [:pass "invalid password"]) (if (errors? :id :pass) (login-page) (do (session/put! :user id) (redirect "/"))))) (defn handle-registration [id pass pass1] (rule (= pass pass1) [:pass "password was not retyped correctly"]) (if (errors? :pass) (registration-page) (do (db/add-user-record {:id id :pass (crypt/encrypt pass)}) (redirect "/login")))) (defroutes auth-routes (GET "/register" [] (registration-page)) (POST "/register" [id pass pass1] (handle-registration id pass pass1)) (GET "/login" [] (login-page)) (POST "/login" [id pass] (handle-login id pass)) (GET "/logout" [] (layout/common (form-to [:post "/logout"] (submit-button "logout")))) (POST "/logout" [] (session/clear!) (redirect "/"))) ``` Could someone please help unstick me so I can get back into the swing of my tutorial? Also, any tips on how to efficiently update code based on deprecated libraries would be helpful. I apologize for the lengthy amounts of code, but I am still a noob and not exactly sure where the problem lies. I am suspecting that it has to do something with the database dependencies, because specifically sql/with-connection has come up as deprecated in my google searches for the [org.clojure/java.jdbc "0.2.3"] dependency, which is now on version 0.3.3.