Nanoc

April 30th, 2015 · · ruby, blogging

The current iteration of this blog runs on an "engine" called Nanoc. Nanoc, you say?

nanoc is a static site generator, fit for building anything from a small personal blog to a large corporate web site.

The keywords here is "static site generator". This means that I build/generate the site locally on my machine, and then push it to a server on the internets for all you to see. There's no dynamic site running, built in PHP or ruby or whatever. Just plain HTML files, some CSS, a bit of Javascript and some images or videos.

Why nanoc?

The previous iteration of my blog was built on Octopress. The principle was the same: Octopress is built on Jekyll, and Jekyll is also a static site generator (there are many others, by the way). But I felt constrained by using Octopress. I don't know why exactly, but I think it started when I wanted to put a new theme on my blog; the current one at the time was a quick-n-dirty job to get started, and after a year on the blog I felt it was time to put some more personalized paint on it. I did not have to time build a full new theme from scratch so I was looking for an "almost there" theme which I could then modify.

I guess I was put off by the (seeming) complexity of the Octopress themes. I did not directly find my way around them, and installing/maintaining them felt like too much work for what it was worth. But even apart from that, due to the out-of-the-box nature of Octopress, trying to change it to fit your own needs was not straightforward. Might be good for starting off quickly, but not for more advanced use.

So started putting off revamping the blog, either until I found the courage to get the work done, or until I found something new.

And then I saw Daniel Kennett doing a post about switching to Nanoc and I knew I was "in love" immediately. This was what I needed. Raw but malleable, ruby, and at first sight: not too complicated.

Start over

The next steps were:

sudo gem install nanoc
nanoc create-site blog.inferis.org

And off I was. To be honest: the bare-bones site that create-site creates is really bare-bones. But I felt this was good, because there's not to much to worry about. You can learn from your site's source and the generated html how things work. Now don't understand me wrong: this took some time and wasn't an easy task. But it wasn't complex either, and the most important part: it was fun exploring how everything worked. You take baby steps here, moving from one issue to another, building up gradually.

Sometimes I hit some brick walls, taking wrong approaches, being stuck for no apparent reason. But it felt like programming, not like configuring. Those brick walls became challenges, and I was eager to find a solution for them. Given that, the Nanoc documentation is really good, but as usual the guides only cover the basic things. Once you go deeper it becomes harder to find good information. There's a few other people tinkering with Nanoc, and blogging about it, and those blogposts were helpful (I'll probably do some more technical posts on my nanoc ventures later on). Also, since everything is open source, you can just consult the source code to see what's going on and work with that.

NSConference

So I was almost done when NSConference 7 came. I wanted to blog about my move to Apple during NSConference, so I started a final sprint to finish the blog so I could switch to nanoc and ditch Octopress. Turned out it was more work than expected (e.g. the code blocks issue -- see below -- took quite a bit of messing around to fix). But by the end of the first day of the conf I felt I was finished and published the site.

I've been happy with it ever since, fine-tuning things here and there, adding initial non-necessary features back in (categories, for example). I feel I have a robust and easy to augment codebase now, and that makes the developer in me very happy.

Current setup

Some details about my current nanoc setup?

Markdown

My posts are all written in Markdown and parsed using the redcarpet gem, using the following configuration:

filter :redcarpet,
       :options => { :fenced_code_blocks => true, :smartypants => true, :tables => true }

I love using Markdown to write content. It's great for embedding code (especially with the fenced code blocks), and you don't get distracted about the layout too much. And if I'm every inclined to do so, it's easy to generate a paper version of this blog; there's enough tools out there to generate PDFs from Markdown.

Theme

I'm using a variation of the Clean Blog Theme (thanks, Ash Furrow for the inspiration). I modified it slighly to personalise it a bit. There's more I want to do but that's for another time.

The theme is made up from a bunch of static files compromising the base of the theme; these are stored in a separate folder which is merged in when generating the site:

-
  type: static
  items_root: /
  allow_periods_in_identifiers: true

This works because nanoc has support for multiple data sources, and nanoc combines the "dynamic" source (the posts) and the static source together to generate the final result. This also means that you could have your posts in an Oracle database if you wanted. 😜

Custom filters

Filters are nanoc's way of transforming your source content to content that you view in the site. As mentioned before, I'm using the redcarpet filter to transform the Markdown posts into HTML.

Nanoc comes with a bunch of filters, and there are another set of them out on the internet. You can use plain erb if you wanted, or use a different Markdown parser (e.g. Kramdown). But it's very easy to come up with your own filters too.

I wrote a couple for my blog specifically:

  • fixcodeblocks.rb fixes the redcarpet generated codeblocks (using Nokogiri) to conform to the HTML that the code highlighter filter expected (took me some time to find out why that was, by the way).
  • octopress_img_tags.rb is a filter I ported from the Octopress install which supports the {% img %} syntax from Jekyll.
  • tweet_tag.rb is another Octopress style filter that filters {% tweet %} tags into an embedded tweet

There's probably more to come as I expand the blog, but that's it for now.

Niceties

  • I added categories/tags because that's what you do.
  • I added a published metadata item for posts, which you can set to false so that the post is excluded when generating the site. Very handy for WIP posts.
  • I also added a preview metadata item for posts; setting this to true does generate the page with the article itself, but it omits the same article in the rest of the site. It won't appear on the homepage, in the archives or category browsers or in the atom feed. I did this so I could publish posts for proofreading (just pass the link to proofreaders) but without alerting the Rest-Of-The-World.

Pros/cons

Cons

  • steep learning curve
  • you need a Mac to deploy your site (generation + cli tools)
  • requires knowledge of Ruby

You can also be thrown off by the not-to-obvious error messages when something goes wrong:

$> nanoc
Loading site data… done
Compiling site…

Captain! We’ve been hit!

Message:

NoMethodError: undefined method `encoding' for nil:NilClass

Compilation stack:

  - [item]   /posts/2015-04-30-nanoc/ (rep default)

Stack trace:

  0. /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/cgi/util.rb:7:in `escape'
  1. lib/filters/tweet_tag.rb:65:in `block in url_params_for'
  2. lib/filters/tweet_tag.rb:64:in `map'
  3. lib/filters/tweet_tag.rb:64:in `url_params_for'
  4. lib/filters/tweet_tag.rb:70:in `cache_file_for'
  5. lib/filters/tweet_tag.rb:59:in `cached_response'
  6. lib/filters/tweet_tag.rb:44:in `html_output_for'
  7. lib/filters/tweet_tag.rb:39:in `run_octopress'
  8. lib/filters/OctopressFilter.rb:11:in `block in run'
  9. lib/filters/OctopressFilter.rb:9:in `gsub'
  ... 36 more lines omitted. See full crash log for details.

If you believe this is a bug in nanoc, please do report it at
-> https://github.com/nanoc/nanoc/issues/new <-

A detailed crash log has been written to ./crash.log.

But then again, it's just parsing the throw error correctly, find the culprit and continue.

Pros

  • very, very flexible
  • static: no fear of performance problems or server breakdowns - it's just files
  • well documented

But above all: once you get the hang of it, it's so much fun to hack on it. If you know how it works, it's pretty easy to set it to your hand and modify it as you see fit. It almost feels like an app/program you're working on.

I'm currently using Nanoc 3.7.5, and they're working on 4.0. Judging from the upgrade guide a lot of the peculiarities I found a bit daunting at first are smoothed out. This will probably make it easier to start with Nanoc.

Open Source

So check it out if you're looking for something new. It's open source, so there's no excuse not to dive in or to contribute. 😊

Speaking of contribute: the complete source of this blog is open source (thanks again to Ash Furrow for the inspiration). You can see all the code I wrote or changed at http://github.com/inferis/inferis.github.io (the nanoc branch contains the source code, the master branch is the generated site). The cool thing about this is that people send pull requests to fix typos: it hasn't happened often, but when it happens, I love it. 😘

This post was proofread by: @tmussche, @pjaspers, @dantoml. Many thanks!