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!
#'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"