Blog Improvements

by Jeremy D. Frens on August 8, 2016


When I moved my blog from Octopress to Nanoc, I was able to put together a tagging system in about an hour, complete with an index of tags with tagged articles. I had a plan to do a similar thing for series of articles, and I finally got around to putting something together.

Upgrade all the things

I first decided it was time to update Nanoc and the other gems that I’m using to generate this blog. Of the gems that got updated, three of them stood out:

  • nanoc went from 4.2.0 to 4.2.4.
  • rack went from 1.6.4 to 2.0.1.
  • rouge wanted to go from 1.11.0 to 2.0.5.

Upgrading nanoc was only a couple patch levels, but it is the core of this blog. Fortunately, it turned out to be no big deal. Upgrading rack was also no big deal, even though it was a major version upgrade. I use rack only to serve up my blog locally on my laptop, so it really wasn’t a big deal if it didn’t upgrade well.

rouge is a different story. As its website says, rouge is “an elegant, extendable code highlighter written in pure Ruby.” It’s one of the acceptible syntax highlighters for kramdown, and both rouge and kramdown have worked well for me. Until now.

I upgraded them all, and all of my fenced code blocks were rendered as plain text! When I looked at the generated HTML, the code itself had been color coded, but there was no pre or code tag around them. The new version of rouge had changed their formatters, so I tried a few of them that made the most sense. Then I found a formatter for legacy compatibility. It worked! Yay! On only the first code block. Boo!!!

Turns out that formatting only the first code block is a known issue. It doesn’t look like it’s going to be fixed any time soon. So I locked rouge down to ~>1.8 and moved on.

Series

I got frustrated each week writing my fractals in Elixir series. I wanted to make sure someone who jumped into an article in the middle of the series could at least see the larger context and could go back to the beginning if they wanted to. By the fourth article, I started to include a list of the previous articles. It was getting tiresome repeating that every time, so four articles later I sent the user to the GitHub project where I kept the list of articles in the README.md.

I still had to maintain the list of articles by hand. It was frustrating because my blog had close to everything it needed to manage series of articles. I finally had enough this past week, so I wrote some code and a new data file and ended up with this:

I keep track of the series titles and descriptions in a YAML file:

# series.yml
bindings_in_elixir:
  title: Ruby, Elixir, Variable Bindings, and Concurrency
  description: ...
fractals_in_elixir:
  title: Fractals in Elixir
  description: ...

The order of the series in this file determines their order on the series page.

The keys at the top level are identifiers that I can use elsewhere, like in an article’s front matter:

title: Switching Back to ExUnit
posted_at: 2016-08-01T19:43:28-05:00
created_at: 2016-08-01T19:43:28-05:00
kind: article
series: :fractals_in_elixir
description: I love testing.  I love RSpec.  I've tried to love ESpec.  It didn't take, and I'm back to ExUnit.
tags: elixir testing

I set :series to the identifier of its series. If I leave out :series, then the article won’t be in any series. The created_at timestamp is used to order the articles in a series on the series page.

Nuts and bolts

My “series database” from the YAML file is easy to build and keep available:

def all_series
  @series ||= YAML.load_file('data/series.yml').symbolize_keys
end

Nanoc::Helpers::Blogging provides a sorted_articles method which returns the published articles sorted by created_at, descending. I have a method to filter out articles for a particular series:1

def series?(post, series_key)
  post[:series] == series_key.to_sym
end

Those are useful for generating the list of articles for a series:2

<ul>
  <% sorted_articles.reverse.select { |a| series?(a, series_key) }.each do |post| %>
    <li>
      <%= link_to post[:title], post.path %>
    </li>
  <% end %>
</ul>

series_key was set in an outer loop:

<% all_series.each do |series_identifier, series_data| %>
  <h2 id="<%= series_identifier.to_s %>">
    <%= series_data['title'] %>
  </h2>
  <p><%= series_data['description'] %></p>
  <ul>
    <!-- article loop -->
  </ul>
<% end %>

I create an HTML id from the series identifier, and then pull out the title for the header. The description goes directly into its own paragraph, and the list of articles is generated after that.

Evaluation

I had a good deal of frustration trying to debug the problem with kramdown and an updated rouge. It doesn’t help that I need to provide configuration for rouge through kramdown through rouge. And it’s really hard debugging Ruby code through a DSL. Getting gems to play well together is still a bit frustrating, but I don’t think it’s a hard with Nanoc as it was with Octopress and Jekyll.

Building out the new series feature was pretty straightforward and even fun. I hadn’t originally planned on adding a series description, but it was really easy to add once the infrastructure and basic features were in place.

I’m thinking about adding a GitHub feature. It would be nice to have a special section in an article (or in the gutter on the right) which listed the GitHub repos (and tags) that I refer to in the article. I may also want to have a list of libraries (Ruby gems, hex packages, etc.) that I refer to as well. It would be even more awesome if I could build the list dynamically from the article itself.

One step at a time…

Footnotes

  1. I tend to write procedural code for Nanoc. It may be because it’s the simple way out, but I have several methods like #series? which take a post as an argument when they’d make more sense as a method on a post. The next time I need to add more code (or when I get bored), I’ll probably wrap posts in my own Post class for better OOD.

  2. In these code snippets, I’ve stripped formatting and structural tags and even removed a little bit of content so that the Ruby code is easy to see and read.

blogging nanoc