Hello World – Build­ing a blog us­ing Boot

04 July 2015

I've been plan­ning on start­ing a blog for some time already. But as I love hack­ing with build tools and such I usu­ally spend some days try­ing a blog gen­er­ator be­fore de­cid­ing it's not good enough and start writ­ing a new one, without ever fin­ish­ing any­thing. Thus I was happy when I saw Perun which does pretty much everything I want:

  • Modular design, easy to extend
  • No forced metadata schema
  • RSS Atom feed
  • Reads markdown
  • Templating using Hiccup

Perun

While work­ing on a pro­ject I like to con­stantly see what is the res­ult of the code I'm work­ing on. On Clo­jure­Script I use Figwheel. Thus I wanted the same ex­per­i­ence for work­ing on blog, though this is not that use­ful when writ­ing it's use­ful while set­ting up the blog. To make this work­flow pos­sible with Perun a two changes were im­ple­men­ted:

Fast re­builds

Pars­ing the mark­down files and their metadata into Clo­jure data takes re­l­at­ively long. If the file has not been changed that work is un­ne­ces­sary. Boot's watch task and file­set provide an easy way to see which files changed since the last build. Per­un's markdown-task which parses the files uses this to read only changed files and merge the changed metadata to ex­ist­ing metadata from pre­vi­ous builds. This provides build times of around 100ms when post con­tent has been changed.

Clo­jure changes

Tem­plat­ing in Perun is done from Clo­jure us­ing Hic­cup. To auto­mat­ic­ally render the changes whenever the clj files change the render tasks use Boot pods to run the code in fresh en­vir­on­ment. This way the Clo­jure namespaces are re­loaded after the changes and changes are in­stantly seen. Be­cause all namespaces re­quired by the render namespaces has to be loaded when files change, it usu­ally takes around 10 seconds to build the site after clj file change.

Al­tern­at­ive ap­proach would be to use tools.namespace to re­load the changed namespaces. That should be quite a bit faster at the ex­pense of sim­pli­city. This ap­proach is used by boot-garden.

Ex­tend­ab­il­ity

As men­tioned Perun should be easy to ex­tend. This is achieved by im­ple­ment­ing Perun us­ing mul­tiple Boot tasks which can be com­posed. Be­low is a ex­ample from this blo­g's build.boot file which shows some ex­amples of the ex­tend­ab­il­ity. The tasks can be cat­egor­ized in three types:

  1. Tasks which read metadata from files, currently only markdown (1)
  2. Tasks which ma­nip­u­late the metadata (2)
  3. Tasks which render some output (3, 4)
(deftask split-keywords []
  (boot/with-pre-wrap fileset
    (->> fileset
         (perun/get-perun-meta)
         (perun/map-vals
           (fn [{:keys [keywords] :as post}]
             (if (string? keywords)
               (assoc post :keywords (->> (string/split keywords #",")
                                          (mapv string/trim)))
               post)))
         (perun/with-perun-meta fileset))))

(deftask build
  [p prod bool "Build rss, sitemap etc."]
  (comp (less :source-map true :compress prod)
        ;; 1
        (markdown)
        ;; 2
        (if prod (draft) identity)
        (slug)
        (permalink)
        (split-keywords)
        ;; 3
        (render :renderer 'blog.views.post/render)
        (collection :renderer 'blog.views.index/render :page "index.html")
        (collection :renderer 'blog.views.tags/render :page "tags/index.html")
        (collection :renderer 'blog.views.atom/render :page "atom.xml")
        ;; 4
        (if prod (sitemap :filename "sitemap.xml") identity)
        (if prod
          (rss :title "Blog"
               :description "Deraen's blog"
               :link "http://deraen.github.io")
          identity)))

The task split-keywords is a task which ma­nip­u­lates the metadata by split­ting keyword strings. Though I now see that I should in­stead just define the keywords as ar­rays in YAML metadata of the posts.

In this ex­ample there are five tasks which out­put files. A task which renders all the posts, two tasks which render a col­lec­tion view and tasks for RSS and sitem­ap. The second col­lec­tion task col­lects a list of all of tags (keywords) in the posts and cre­ates a tag cloud out of those, what is cool here is that to cre­ate a tag page no spe­cial task was needed as the render func­tion can it­self do the ne­ces­sary work (one reduce).

Render tasks do not need to ne­ces­sar­ily out­put HTML, they can pro­duce string in any way which is then writ­ten to the file. collection-task can for ex­ample pro­duce HTML us­ing Hic­cup or XML us­ing data.xml.

HTML and CSS livere­load

Fig­wheel style Clo­jure­Script de­vel­op­ment is already pos­sible with Boot us­ing boot-reload but that is heav­ily built for Clo­jure­Script use cases. The cli­ent is writ­ten in Clo­jure­Script so a built step is re­quired. Also boot-re­load does­n't handle HTML file re­loads which are the es­sen­tial for be­ing use­ful for static page de­vel­op­ment. Boot-re­load does handle HTML re­loads.

Pre­vi­ously I've been us­ing LiveReload.js with Gulp.js so I thought that should be a good fit for Boot also. Luck­ily a Clojure implementation of LiveRe­load already ex­is­ted. It re­quired some API changes to make it gen­er­ally us­able lib­rary. After the changes it was a breeze to cre­ate a boot task.

Cool fea­tures and pos­sib­il­it­ies

As it is easy to ma­nip­u­late the data between read­ing the files and ren­der­ing there is great op­por­tun­ity to move some fea­tures which are usu­ally im­ple­men­ted in the browser to build pro­cess. The value I see there is that RSS read­ers (A­FAIK) do not ex­ecute JS so those read­ers are left without e.g. code high­light­ing.

An­other pos­sib­il­ity is to hy­phen­ate the text. This is even more use­ful as the hy­phen­a­tion is re­l­at­ively ex­pens­ive op­er­a­tion so it's great if it only needs to be ex­ecuted once in­stead of each page load. For this reason I already star­ted writ­ing clj-hyphenate which im­ple­ments Frank­lin M. Li­ang's hy­phen­a­tion al­gorithm in Clo­jure. The al­gorithm is the same as used by TeX, Lib­reOf­fice and Hy­phen­at­or.js. To hy­phen­ate HTML it is pos­sible to in­sert soft-hy­phens into the text which the browser only shows if the word needs to be split into mul­tiple lines.