(defn start-server []
(reset! server
(jetty/run-jetty #'handler {:port 3001 :join? false})))
Does the #' alone make this hot re-loadable or is something in the run-jetty func that makes this re-loadable because it is a var? I noticed this makes it so I can reload the handler function which is great but I want to be able to reload all the handlers under it. Say handler is:
(def handler
(ring/ring-handler (ring/router (routes))))
Do I need to put #' on each route: ["/" {:get #'home}]] like this? Can this be left like this for production?
It would be easy enough to have a different run-jetty call for dev/prod but not for all the routes.When you put #' in front of some symbol, the value bound to the var named by that symbol becomes reloadable. So you can only redefine the var itself and all the usages will take on the new value, unless the old value was closed over (the value itself, not the var).
So putting #' in front of every handler means that you can redefine a particular handler without touching anything else. If you put it only in front of the root handler that includes the routes and all the other handlers, only that root handler will be reloadable. So if you change some leaf handler, you'd still have to re-evaluate it all the way up the chain to the nearest #'.
It's not a big deal to leave #' in production unless you used it in some hot loop where a plain var deref would result in a significant performance loss.
FWIW I myself am not a fan of that approach at all - I don't like to keep track of things that I've changed and of things that I've re-evaluated, I don't like to scatter #' all over the code base, I don't like finding out that something needed #' and I forgot about it. So I just use the "reloaded" workflow with Integrant, where I can change however much or little, in any place, and then reload it all at once, but only what actually needs reloading.
It depends on how you define your routes
Too complicated to get into via a phone keyboard, but it is a variant of the things that trip people up with (def f (partial g :foo)) and then re-defining g
ok, thank you! I think I get it. I initially went through this tutorial: https://caveman.mccue.dev/tutorial/clojure/7_pick_a_handler_based_on_route and was trying to create just a simple server with a few routes from scratch without looking at that tutorial but wanted the hot reload. I see there now they scatter #' on all the handlers too. I'll look into integrant at some point 🙂
And you can use any other library similar to Integrant, like e.g. Component. Mount is another popular choice but I don't like it due to its global state.
It looks like that tutorial uses reitit and writes things like (partial #'hello-handler system). If you instead write (fn [r] (hello-handler r system)) then you won't need to use #' everywhere. Alternatively you could use something like middleware to inject the system into the request? I wouldn't be surprised something like that gets introduced later in the tutorial.
@marcrosoft I follow caveman too and I am pretty sure that if you have ENVIRONMENT=development bit and are therefore calling the binary overload of (root-handler) then the #' in e.g. ["/goodbye" {:get {:handler (partial #'goodbye-handler system)}}] is unnecessary.
> It would be easy enough to have a different run-jetty call for dev/prod but not for all the routes.
The caveman approach spares you having to do this.
(defn start-server
[{::keys [env] :as system}]
(let [handler (if (= (Dotenv/.get env "ENVIRONMENT") "development")
(partial #'routes/root-handler system)
(routes/root-handler system))]
(jetty/run-jetty
handler
{:port (Long/parseLong (Dotenv/.get env "PORT"))
:join? false})))
is all you need.nope
if you leave the #' out in the actual route definitions the value of the the vars of the handlers is only taken once when root-handler is called at system start up
I am wrong, I misread the handler construction there, didn't think you would rebuild the routes on every request, which is what that partial of #'routes/root-handler does