7

I have boot-clj installed and want to be able to edit a .clj file in an external editor and separately have a command line REPL running from which I can call the functions that I change in the .clj file. No special reloading commands should be required.

Another thing is I don't want to have to manually type commands to include namespaces - I would like to just run a script that brings me into the namespace, so I can call existing functions right away.

Name of the file:

C:\dev\my-project\src\my_project\utils.clj

Something of what is inside the file:

(ns my-project.utils
  (:require
    [clojure.string :as s]))

(defn my-range [start end]
  (take (- end start) (iterate inc start)))

I would like to go straight into a REPL and go (my-range 0 3) and see if it produces the result I want.

What's the setup for this? What would the script file I need to run look like?

My current understanding is that the answer will look something like this:

(deftask dev-repl
  (set-env! …)
  (repl))
Chris Murphy
  • 6,411
  • 1
  • 24
  • 42

1 Answers1

9

at the command line

You can achieve this to some degree at the command line, without creating a build.boot file:

In C:\dev\my_project:

boot -r src repl -n my-project.utils
  • boot -r src: starts boot with src on the "resource paths", which is the set of directories that will be accessible within the JVM.
  • repl -n my-project.utils starts a REPL, requires your namespace, and enters it.

While the REPL is running, and after you have edited C:\dev\my_project\src\my_project\utils.clj, you can reload it at the REPL like this:

my-project.utils=> (require 'my-project.utils :reload)
nil

minimal build.boot

Alternatively, you could create the file C:\dev\my_project\build.boot with these contents:

(set-env! :resource-paths #{"src"})

(deftask dev 
  "Run a development REPL"
  []
  (repl :init-ns 'my-project.utils))

Then, in C:\dev\my_project:

boot dev

Which will also start a REPL in your namespace, but requires less command-line configuration as we've performed the configuration in build.boot, which boot will automatically evaluate.

Note: from a Clojure REPL, regardless of build tool, you can require any namespace (as long as it's on the JVM's class path) with the require function and enter it with the in-ns function.

build.boot with automatic reloading

Finally, it's possible to combine features of Boot to achieve a development workflow oriented around automatically reloading code.

In C:\dev\my_project\build.boot:

(set-env! :resource-paths #{"src"})

(require '[boot.core :as core]
         '[boot.pod  :as pod])

(deftask load-ns
  "Loads the my-project.utils namespace in a fresh pod."
  []
  (let [pods (pod/pod-pool (core/get-env))]
    (core/with-pre-wrap [fileset]
      (pod/with-eval-in (pods :refresh)
        ;; We require indirectly here so that errors from my-project.utils have
        ;; proper line and column information.
        (require 'my-project.load-impl))
      fileset)))

(deftask dev
  "Watches source code and loads my-project/utils every time code changes."
  []
  (comp (watch)
        (load-ns)))

In C:\dev\my_project\src\my_project\load_impl.clj:

(ns my-project.load-impl)

(require 'my-project.utils)

In C:\dev\my_project\src\my_project\utils.clj:

(ns my-project.utils
  (:require
    [clojure.string :as s]))

(defn my-range [start end]
  (take (- end start) (iterate inc start)))

(println "In the code!")
(println "(my-range 0 10) = " (my-range 10 20))

Back at the command prompt, type boot dev. You should see some println output, and every time you edit and save the file you should see it again, reflecting any changes you made.

Alan Dipert
  • 206
  • 2
  • 4
  • That's great thanks - will allow me to run code without restarting a new JVM every time. I wonder if the `(require ...)` can somehow be put first in the history so it is only a few up keystrokes away, or turned into a command like `r`?? – Chris Murphy Dec 17 '15 at 05:19
  • You can type Ctrl-r at the REPL to search command history. You could also add a function to your namespace and call it from the REPL with (r): (defn r [] (require ...)) – Alan Dipert Dec 17 '15 at 12:20
  • I like the `(r)` trick (even though it is a special reloading command). Interesting that if I change the name of a function the old function is still in the namespace. – Chris Murphy Dec 17 '15 at 14:09
  • I'm new to SO and am not sure if you were notified, but I made a major edit to my answer. It now includes instructions for an auto-reloading setup I hope you'll enjoy. – Alan Dipert Dec 17 '15 at 14:19
  • Yes I saw that thanks, meant you answered the question perfectly. I'm sticking with the `(r)` trick for now as it basically does what I need and has less moving parts than the real answer. – Chris Murphy Dec 17 '15 at 14:30
  • For a disciplined approach when building software, you may want to adopt the reloaded workflow by Stuart Sierra. A boot task for automatic reloading is available at https://github.com/danielsz/system. – Daniel Szmulewicz Dec 18 '15 at 00:19