4

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!

siphiuel
  • 3,480
  • 4
  • 31
  • 34

1 Answers1

11

#'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"
noisesmith
  • 20,076
  • 2
  • 41
  • 49
  • 1
    Thanks for a perfect explanation. I'd actually wish it were included in more Ring/Compojure tutorials. Thanks so much! – siphiuel Mar 09 '15 at 08:20