Smartening up haml in nanoc
I’ve been tidying up around the place. Do you like it? It feels a lot cleaner to me.
(RSS subscribers are encouraged at this point to follow the link and read the article on the website proper.)
For a while I’ve wanted to swap out my technically quite nice but aesthetically rather sterile sans-serif body font for a more friendly serif with a bit more readability, so with this slight redesign I’ve now gone and done that. However, I quickly got annoyed with markdown’s generic straight quotes, and started working out how to fix this.
The default answer for turning boring straight quotes into nice typographical quote marks, especially in a markdown-heavy environment, is to pull out John Gruber’s SmartyPants. I’m using Redcarpet to render markdown on my site, so it’s not too hard to build in SmartyPants support.
Markdown + SmartyPants
Redcarpet’s documentation shows you how to include smartypants: you just include
Redcarpet::Render::SmartyPants in your renderer and everything works fine. Since I already have a custom renderer in my nanoc implementation, I simply added SmartyPants to that1:
1 2 3 4 5 6 7
module Redcarpet::Render class CustomRenderer include SmartyPants #... end end
To use this renderer in nanoc, you just need to modify your Rules file slightly. Let’s say I want to render all my blog posts in markdown with SmartyPants:
1 2 3 4 5
compile "/posts/*" do if !item.binary? filter :redcarpet, renderer: Redcarpet::Render::CustomRenderer end end
Haml + SmartyPants
There is a problem, and the problem looks like this:
1 2 3 4 5 6 7 8
# sample_document.haml %h1 Haml page %p This is a haml page, with a haml paragraph. :markdown Now I'm filtering a section through markdown, but "these quotes" won't run through SmartyPants.
In cases like this, Haml has its own little markdown-rendering party and doesn’t invite Redcarpet or its custom renderer along for the ride. Worse, Haml ends up using Tilt as a templating engine, adding a further layer between us the metal. How can we get into the guts of the rendering process, telling Haml to tell Tilt to tell whatever it’s using to render this markdown inside the filter that it should be using SmartyPants?
1 2 3 4 5 6 7
class Tilt::HamlTemplate module ::Haml::Filters::Markdown def render(text) RDiscount.new(text, :smart).to_html end end end
We can fix this up for nanoc and Redcarpet nice and easy. My code looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
require "tilt" class Tilt::HamlTemplate module ::Haml::Filters::Markdown def render(text) @renderer ||= Redcarpet::Markdown.new( Redcarpet::Render::CustomRenderer, fenced_code_blocks: true, footnotes: true ) @renderer.render(text) end end end
I don’t even have to tell Redcarpet to use SmartyPants here - its
CustomRenderer knows to use it, and Redcarpet is perfectly happy letting the renderer do whatever it wants. This little code snippet goes into the
lib/ folder along with the custom renderer, and you don’t need to do anything else. Haml will now route all its markdown-filtered blocks through Redcarpet, and the rest is magic.
This file is stored in
lib/, in the root of your nanoc site. Files in
lib/are auto-loaded, making this the perfect place to screw around with your nanoc install. ↩