June 5, 2015

My first time using boot over leiningen

Using clojurescript doesn't suck anymore!

When boot first appeared in my usual rotation of clojure news, I must confess that I didn’t really see what the fuss was about. It seemed like someone had taken leiningen and broken out all its parts into functions, which you could then use to get your dependencies, build your project, and so forth. Don’t you see! Leiningen is just a build tool, but boot could be anything. It could even be a build tool!

As far as I was concerned, the effort of enumerating all the current and future tasks your project might need and constructing the subset of leiningen that you needed to re-create wasn’t worth the gain in flexibility. The boot website doesn’t help either, instead falling into the trap of making vague claims about boot’s capabilities without really selling me on why I should use it.

It wasn’t until I stumbled across this blog post detailing how to integrate Middleman with boot that I gave it any serious thought. I use Middleman in just about every project – it’s a really solid, flexible and useful piece of tooling with extensive plugin support, and handles all my front-end tasks like a champ. But, when it comes to the clojure-middleman interface, that’s always just been me building the static site using the middleman command line before running or restarting the clojure server. For clojurescript, I just resigned myself to the fact that none of the javascript could run while I was developing the design in middleman – that would have to wait until I did a build so I could serve it up with figwheel, and any iteration I had on the design while I had the scripty bits running would have to go through the middleman build process between changes.

So, for the first time, I installed boot to give it a college try.

Baby build steps

My first impression when writing my first build.boot was that I had been entirely too pessemistic estimating the difficulty of writing my own build scripts. Most of the examples of boot scripts I had seen were someone showing off their whiz-bang build job that really did a bunch of stuff at once, but actually using boot for basic stuff is way, way simpler.

To test out boot, I made a new directory for my project. In it, a directory I called html that held my middleman project, and my build.boot, which started out with just the middleman task:

(set-env!
 :source-paths   #{"src"}
 :dependencies '[[com.joshuadavey/boot-middleman "0.0.3" :scope "test"]])

(require '[com.joshuadavey.boot-middleman :refer [middleman]])

(deftask dev []
 (middleman :dir "html"))

Here, the dev task just wraps the middleman task and tells it that the middleman project lives in the html directory. I ran the task ($ boot dev), and the compiled middleman site appeared in the target directory of my project, almost like magic.

Besides my first step into boot-land not being nearly the build-script-wrangling hell I was expecting, I was also rightly impressed with how easy it was to import and use a non-packaged build step. Just pop in a dependency and use it in a task, easy as that.

Going deeper

Emboldened by the success of my first experiment, I started adding bits and pieces. First, I updated my dev task to serve the files as well:

(set-env!
 :source-paths   #{"src"}
 :dependencies '[[com.joshuadavey/boot-middleman "0.0.3" :scope "test"]
                 [pandeiro/boot-http    "0.3.0"      :scope "test"]])

(require '[com.joshuadavey.boot-middleman :refer [middleman]]
         '[pandeiro.http :refer [serve]])

(deftask dev []
 (comp
  (serve :dir "target/")
  (middleman :dir "html")))

Then, I added a clojurescript file to the mix, and had the build task compile it, too:

(set-env!
 :source-paths   #{"src"}
 :dependencies '[[adzerk/boot-cljs      "0.0-2814-3" :scope "test"]
                 [com.joshuadavey/boot-middleman "0.0.3" :scope "test"]
                 [pandeiro/boot-http    "0.3.0"      :scope "test"]])

(require '[com.joshuadavey.boot-middleman :refer [middleman]]
         '[pandeiro.http :refer [serve]]
         '[adzerk.boot-cljs      :refer [cljs]])

(deftask dev []
 (comp
  (serve :dir "target/")
  (middleman :dir "html")
  (cljs :source-map true)
))

And so on with the watch and reload tasks, and the clojurescript repl. Before long at all, my dev task would:

  • Build my middleman site
  • Compile clojurescript
  • Watch the files from the latter and re-run those tasks if anything changed
  • Automatically reload the page when anything changed (And call a clojurescript function of my choosing)
  • Fire up a node-js repl that I could connect to with fireplace

And all that with just existing plugins that I put together in the most basic way. The only catch is that certain tasks must be in a certain order – I cribbed my order from this and this boot script. My final product:

(set-env!
 :source-paths   #{"src"}
 :dependencies '[[adzerk/boot-reload    "0.2.6"      :scope "test"]
                 [adzerk/boot-cljs      "0.0-2814-3" :scope "test"]
                 [com.joshuadavey/boot-middleman "0.0.3" :scope "test"]
                 [pandeiro/boot-http    "0.3.0"      :scope "test"]
                 [adzerk/boot-cljs-repl "0.1.10-SNAPSHOT" :scope "test"]])

(require '[com.joshuadavey.boot-middleman :refer [middleman]]
         '[pandeiro.http :refer [serve]]
         '[adzerk.boot-reload    :refer [reload]]
         '[adzerk.boot-cljs      :refer [cljs]]
         '[adzerk.boot-cljs-repl :refer [cljs-repl start-repl]])

(deftask dev []
 (comp
  (serve :dir "target/")
  (watch)
  (middleman :dir "html")
  (reload :on-jsload 'bootcljs.core/on-reload)
  (cljs-repl)
  (cljs :source-map true)
))

All that in about a half-hour, the first time I ever used boot.

I’ll still probably stick or start with leiningen for straightforward clojure projects, but I’ll definitely keep boot in mind when my project.clj file starts expanding beyond dependencies. I think boot might be the killer product for clojurescript, or maybe vice-versa. clojurescript and leiningen never really got along perfectly, but boot, being more agnostic about what your project looks like, handles it just fine. This has always been the major barrier for me when it comes to using clojurescript in my projects, so I’m really excited about that development.

If you’re reading this, and you haven’t given boot a try, just give it a try – I can’t guarantee you’ll love it, but I guarantee that you’re not getting the full picture just reading about it. And that’s really all I have to say – I think I’m going to go convert all my projects that have even thought about clojurescript to use boot now.