In my previous three blog posts about Boot – Rebooting Clojure, Building On Boot, and Testing With Boot – I looked at why World Singles decided to switch from Leiningen to Boot, as well discussing one of the missing pieces for us (testing). Once I had boot-expectations written, I was casting around for other missing pieces in the ecosystem and one glaring one was the lack of something to generate new projects from templates.
Leiningen has long-supported the generation of new projects from templates and it’s pretty slick. Want to get a new Framework One application up and running?
lein new fw1 myapp cd myapp PORT=8123 lein run
That’s all it takes. No directories and files to create, no editing. Just tell Leiningen to create a new
fw1 project called
myapp, drop into that newly created directory and run the generated skeleton application.
Behind the scenes, Leiningen looks for the most recent release of the
fw1/lein-template artifact on Clojars (or Maven Central), downloads it and adds it to the classpath, then it
leiningen.new.fw1 namespace (assumed to be in that artifact) and calls the
fw1 function within that namespace. That
fw1 template project in turn relies on the
leiningen.new.templates namespace to provide a number of functions to
render new project files from mustache-style templates, using the Stencil library. Leiningen templates may also depend on Leiningen’s core code, as well as a few libraries that Leiningen always makes available (such as Slingshot).
I figured that in order to kickstart any
boot new functionality, it would make sense for it to be able to render existing Leiningen templates, as well as Boot-specific templates. Since Boot deliberately includes source code directly from other projects so as to minimize the number of dependencies it brings in, and it already had several pieces of Leiningen copied into it, I reached out to Leiningen’s current maintainer, Jean Niklas L'orange and asked permission to include parts of Leiningen’s new/template support. He graciously said yes – thank you! – so I created a raw first cut of a
new task for Boot, based directly on Leiningen’s code, which in turn depended on Bultitude and
leiningen-core (and a few other bits and pieces). Because Boot tasks are “just Clojure”, it was fairly straightforward to get to a working 0.1.0 version that had basic parity with
Since Boot already provided ways to manage dependencies and the classpath, my next goal was an 0.2.0 version that didn’t rely on Leiningen’s core. This version provided the same functionality (well, almost, it had compatibility bugs that took another version to iron out) but no longer needed to bring in
leiningen-core as a dependency (unless you were generating a Leiningen template which might itself rely on that).
At this point, I was able to implement built-in templates to match Leiningen’s
template, which would produce Boot-specific versions. The 0.2.1 version included built-in templates for
task (the Boot equivalent of a plugin), and
new had both
leiningen.new.templates (adapted to run inside Boot) and
boot.new.templates to support Boot templates. A Boot template is just like a Leiningen template, with a couple of important exceptions: the artifact name is of the form
foo/boot-template (instead of
foo/lein-template) and the template’s main namespace is
boot.new.foo (instead of
leiningen.new.foo). In addition, a Boot template is expected to rely on Boot’s internals or explicitly specify its own dependencies – instead of depending on Leiningen’s core library. As a final piece of clean up for this “initial” version of Boot
new, I removed the dependency on Bultitude (which actually hadn’t been needed for a while) and deferred the addition of the
slingshot dependencies so they wouldn’t be pulled in if you were generating a project from a Boot template.
At this point, I felt Boot
new was ready to announce to the world! I could do:
boot -d seancorfield/boot-new new -t fw1 -n myapp
and get a freshly generated Framework One app, even if that still used Leiningen to actually run the new app. I could do:
boot -d seancorfield/boot-new new -t app -n myapp
to get a skeleton for an application that was “powered by Boot”: the generated
build.boot file provides tasks for
building the application uber-JAR and
runing the application itself.
Alan Dipert and Paulus Esterhazy were the first two people to uncover compatibility bugs with existing Leiningen templates (for Hoplon and Chestnut, respectively). Thank you! And that brought me to Release 0.3.1 of boot-new.
The next thing on my roadmap is to add some sort of “generator” function (not unlike James Reeves' lein-generate) which will allow you to add new pieces to your existing projects, much Rails/Grails have built-in commands to let you add new models, controllers, and so on. That will be part of 0.4.0 and probably go through a couple of revisions. At some point, I’ll feel comfortable declaring a 1.0.0 release and then we’ll see about getting Boot
new merged into the core of Boot itself.
Boot originally started life as a part of Hoplon and for a long time, you needed Leiningen in order to generate a new Hoplon project, even tho' Hoplon itself was powered-by-Boot. It’s nice to see Hoplon’s Getting Started using
boot-new as the recommended way to generate a new Hoplon project (it’s still a Leiningen template!).