Code Reloading in Clojure

I like quick feed back while I’m coding. Instead of ‘playing’ computer in my head, let the code code run and see if it does what I expect it to do. It’s one of the reasons enjoy working in a test first workflow and having a test friendly code base.

Just changing curtains color

Just changing curtains color

Anyway, some things are just easier to ‘tweak’ visually and experiment. For example when working on a GUI code or some REST output. So, I want that changes are immediately available. In Clojure there are different, ready to use solutions to do code reloading. Like Figweel for Clojurescript and web front-ends.

Here I am not going into Figweel and many other solutions. Instead I’m using Clojure facilities, without any special tools. I also take a look at on code reloader: test-referesh

Basics: Reload with use

Assuming we have some piece Clojure code. Like this:

(ns my-friendly-emailer
  (:require [clojure.string :as str]))

(defn create-text
  "Creates a super friendly email text"
    "Hello " (:fullname user) "\n"
    "You like " (str/join "," (:likes user))))

(defn create-email
  "Creates an email, ready to send"
  {:to   (:email user)
   :body (create-text user)})

(def example-user
   :fullname "Jill Awesome"
   :username "jill"
   :email    "jill@test.local"
   :likes    ["Sports", "Movies", "Cars"]

Now, we want to ‘tune’ the email and make it actually a friendly email. So you include it into your REPL and evaluate the result:

Now you did some changes. But when you use the `create-emails` function again, it still prints out the old result. You don’t want to restart, but just reload it and keep going. Well, the ‘use’ macro already has reloading built in. Load the namespace of the file with the :reload flag:

That’s it. Clojure will reload the code in the namespace for you. Nothing else to do.

Reload more, :reload-all

Maybe the change you made is actually in some dependency. Assume we have this function in another namespace, which refers the the friendly email code:

 (ns uses-emails
   (:require [my-friendly-emailer :as mail]))

 (defn preview-email-example []
   (doseq [email (mail/create-email mail/example-user)]
     (println (:body email))))

When you use the ‘preview-email-examples’ function, you might notice that reloading with `(use ‘uses-emails :reload)`, it will not include changes made to the my-friendly-emailer.clj file. Use the :reload-all flag. It also reloads any dependencies:

Yes, please just change the curtains.

Yes, please just change the curtains.

Automate Reloading, Example REST Server

You can use the ‘use’ primitive easily to create your own reload mechanism. This is useful, when you use Clojure in some embedded scenario or existing of the shelf reloaders do not work.. Let’s extend the given email example to a web service:

(ns our-web-server
       [my-friendly-emailer :as mail]
       [ring.adapter.jetty :as jetty]
       [ :as json]))

  "included dependencies, like in lein:"
  [[org.clojure/data.json "0.2.6"]
   [ring/ring-core "1.5.0"]
   [ring/ring-jetty-adapter "1.5.0"]]

(defn handler
  "Most boring handler. Just returns our example emails as JSON"
  {:status  200
   :headers {"Content-Type" "application/json"}
   :body    (json/write-str (mail/create-email mail/example-user))})

(defn start-server []
  (jetty/run-jetty handler {:port 3000 :join? false})

(def running-server (start-server))

This web server is pretty annoying. If we change anything in the email template code, we have to restart it. Wouldn’t it be nice if changes are visible on the next browser refresh. Well, we can hack that our self. We add a developer flag. When that is set, we just go and reload our app’s main namespace.

Then we enable it. In this example just in via the REPL. In a real app the toggle mechanism can be more advanced

Of course, for Ring there is already a reload library (probably even more than one). For example the reload wrapper from ring-devel library.

Using test-refresh

So far we used core Clojure functionality. Based on that you certainly can go deeper and create more elaborate reloading schemes. One of such is test-refresh, a Leiningen plugin. I just add it to my ~/.lein/profiles.clj, so that it is available in any project I touch:

; In  ~/.lein/profiles.clj
{:user {:plugins [[com.jakemccrary/lein-test-refresh "0.16.0"]]}}

This gives the `lein test-refresh` command. That command will run the test suite. The cool thing now is, that it will rerun tests which are affected by a change. So you get immediate feed back if your edit just broke something. Even better, you just can experiment around, while test-refresh reloads the code, giving you always feed back.

Immediate feedback.

Immediate feedback.

Here a screen series to illustrate that. Or if you have time, checkout this video: Don`t settle for a REPL.

When started, the tests are executed first:
First your tests are executed.
When editing, as soon as you save a file, all tests which depend on that change are executed again

Now, you also just can try code out right there. The results are shown immediatly. Like a free edit REPL:
That also allows you to monitor some high level end result, while changing the lower level functions:

Use test-refresh or your Test Server

Well, you can also use test refresh to reload your server when files change. What I usually do put the server in a separate namespace, with no dependency to my core app. When the file and its dependencies does not change, test-refresh won`t reload it. This means that test refresh does not reload the actual server. Note that the test server exposes an atom for the actual request handler:

(ns test-server
  ; Only depends on Jetty / Ring.
  ; Since that won't change, it isn't reloaded by test refresh
  (:require [ring.adapter.jetty :as jetty]))

; This will be swapped out by the test setup
; That setup will have the actual app in the dependency list and therefore be reloaded by test-refresh
(def test-handler (atom (fn [request] {:status 503})))

(defn- call-latest-handler [request]
  (@test-handler request))

(println "Starting test server")
(jetty/run-jetty call-latest-handler {:port 3000 :join? false})

Then we add a ‘test setup’ file. That one depends on our application. Since our application changes, and this file depends on it, it will be reloaded as soon as we change anything in our applicaiton. When reloaded, it will swap out the handler of the test server. So, if the app changes, test-reload will do it`s reload logic and we also will have the latest code running in the web server:

This can be done for other server types and setups. Is neat for places where there no pre made plugin or you need a very specific setup.

Keep on reloading

That`s it for today =). Happy reloading. ^.^

Tagged on: ,