Search code examples
clojureread-eval-print-loopringcompojure

Dynamic handler update in Clojure Ring/Compojure REPL


I've created a new Compojure Leiningen project using lein new compojure test. Web server is run by lein repl and then

user=> (use 'ring.adapter.jetty)
user=> (run-jetty test.handler/app {:port 3000})

Routes and app handler specification is trivial:

(defroutes app-routes
  (GET "/*.do" [] "Dynamic page")
  (route/not-found "Not Found"))

(def app
  (wrap-defaults app-routes site-defaults))

Now, after changing anything in app-routes definition (e.g. changing "Dynamic page" text to anything else, or modifying URI matching string), i do not get the updated text/routes in the browser. But, when changing app-routes definition slightly to

(defn dynfn [] "Dynamic page fn")
(defroutes app-routes
  (GET "/*.do" [] (dynfn))
  (route/not-found "Not Found"))

i do get dynamic updates when changing the return value of dynfn. Also, following the advice from this article and modifying the app definition to

(def app
  (wrap-defaults #'app-routes site-defaults))

(note the #' that transparently creates a var for app-routes) also helps!

Why is that so? Is there any other way one could get a truly dynamic behaviour in defroutes?

Thanks!


Solution

  • #'app-routes is a reader macro that expands to (var app-routes). When a var is used as if it were a function, it is dereferenced anew on each invocation, and then the value returned by that deref is called.

    If you were to supply app-routes as the argument, the compiler would give the dereferenced value to wrap-defaults, and when the var is updated, the previous value is not changed, so changing the var does not change the behavior of app.

    The following repl transcript might be instructive:

    user=> (defn foo [] "original")
    #'user/foo
    user=> (defn caller [f] #(f))
    #'user/caller
    user=> (def call-foo-value (caller foo))
    #'user/call-foo-value
    user=> (call-foo-value)
    "original"
    user=> (def call-foo-var (caller #'foo))
    #'user/call-foo-var
    user=> (call-foo-var)
    "original"
    user=> (defn foo [] "changed")
    #'user/foo
    user=> (call-foo-value)
    "original"
    user=> (call-foo-var)
    "changed"