🏆 I provide private lessons on Emacs, Linux, and Life in general: https://protesilaos.com/coach/. Lessons continue throughout the year.

How I coded my new book

This post is archived. Opinions expressed herein may no longer represent my current views. Links, images and other media might not work as intended. Information may be out of date. For further questions contact me.

Today I published a new book: Little Guide to the European Union. In this Codelog entry I want to document the coding practices used to deliver it in the way I did.

Filtering subdirectories of Jekyll collections

As far as I know, Jekyll (the static site generator I use) does not recognise subdirectories in a collection. I have a _books folder where I wanted to add my two books on the European Union. These books are, in essence, folders with a bunch of markdown files, one for each chapter. So when I try to call a for loop for site.books.[subdirectory] I get content from both books instead of the subdirectory I wish to target.

The way I thought to filter the loop and limit it to each book is to include a custom YAML object for each book’s contents. All files that make up my Little Guide have the key flag with value euguide. Similarly, my Handbook has flag with euhandbook. These are the variables that help me sort files based on the book they belong to.

Thanks to this technique, I can create two separate loops, one for each book’s index. For the Little Guide I use this:

{% assign euguides = site.books | where: "flag", "euguide" %}

<div class="measure">
  <section class="home-loop">
    <ul>
      {% for euguide in euguides %}
        <li class="clearfix">
          <a href="{{ euguide.url | relative_url }}">{{ euguide.title }}</a>
          <span class="post-meta">Part {{ forloop.index }}</span>
        </li>
      {% endfor %}
    </ul>
  </section>
</div>

Notice how I apply the filter using where: "flag", "euguide". Also note the liquid variable forloop.index which adds a number to each item. Without the filter it would not be able to add the proper sequence as it would include content from the entire _books folder, i.e. also cover my other book.

The print-optimised version

For my new book I deliver each chapter as a single webpage. It has its own URL and meta data, which I think is the best approach for content on the web. I do, nonetheless, recognise that people may want to keep a physical copy of my book. It is a laborious task to print several pages and then to figure out in what order they are supposed to be read. It is much easier to print one webpage that includes all of the book’s contents.

When I began thinking about how to approach this, I first considered the non-engineer’s method: to copy/paste each chapter into a single document. That is rather inefficient and contrary to my intention of updating my book when and where necessary. If I were to maintain two separate sets of files, I could end up in a dependency mess of trying to track down whatever changes I made in one part to see if they were replicated in the other.

Whereas the developer’s solution I implement works automagically. I use the above-mentioned loop in a slightly different way. I created a new markdown file and included in it two for loops, one for the table of contents and the other with the actual text. Here is the code:

{% assign euguids = site.books | where: "flag, "euguide" %}

<div class="contents">
  <h2>Book contents</h2>
  <ol>
  {% for euguid in euguids %}
    <li>
      {{ euguid.title }}
    </li>
  {% endfor %}
  </ol>
</div>

{% assign euguides = site.books | where: "flag", "euguide" %}

<div>
  {% for euguide in euguides %}
    <h2 class="h1 print-section">
      {{ forloop.index }}. {{ euguide.title }}
    </h2>
    {{ euguide.content }}
  {% endfor %}
</div>

Just to be sure that I do not get confused, I differentiate those two loops by a single letter. The first is euigids. The second euguides.

Notice the euguide.content in the second loop. It displays the entirety of the file’s content. Also worth noting is the print-section CSS class. It corresponds to this little snippet which separates chapters with a page break when printed:

.print-section {
  page-break-before: always;
}

Also note the semantically correct use of the h2 tag in conjunction with an h1 class. The heading will look as a big title when printed, but will be parsed as a secondary title in the webpage. We need that because it is incorrect to have more than one h1 on each page.

My book also comes with a versioning system. I may be making updates and need to document them accordingly. The way to display the book’s version, which is set as a global variable in my _config.yml file, is to call it on the file of the print-optimised page using a liquid tag: {{ site.data.text.euguide.version }}. This way I do not need to manually update the document every time the version changes (I employ this technique for a number of issues).

More than a book

Okay I admit this subheading is a bit too much, but you get the idea. This book is not only about the content. The underlying code is equally important. Perhaps more so. Without it the book would not be published. I would have to find some other medium to deliver it or I would end up publishing it in a largely suboptimal fashion.

The reader does not have to know any of this. The code is not meant to call for attention. Like all good implements, it works seamlessly.

Finally, I think I am beginning to use Jekyll in some advanced ways. I first thought of it as some simple tool for keeping blog posts in one place. It is much more powerful than that. In fact, it seems to me like a CMS without the handholding (not that this is necessarily a good thing for every user). I am loving it and am super excited about what use case I will come up with next.