Logo

mobileteashop

Dennis Poon's blog and ersatz portfolio.

Partial Templates in Jekyll (or not)

As I was applying the Prettify markup to our Developer Center for each code example, I started to get annoyed around the 10th copy-paste of the following:

<pre class="prettyprint linenums">
  <!-- Code Example Content -->
</pre>

Given the 30+ code examples in the doc base with more to come, it seemed to make sense to pull the markup into a partial template. I envisioned something like a regular Jekyll template, except that it could be included at-will on any page.

Unfortunately, fighting with the template mechanism yielded nothing, so I turned to the include tag.

(the TL;DR is that partial templates can only be approximated in a very hacky fashion in vanilla Jekyll, so feel free to skip this post if you’re looking for an elegant solution.)

Using the Jekyll include Tag

The Jekyll include tag allows you to reuse page fragments in multiple locations of your website. You can even pass parameter values into an included fragment to run stuff like conditional logic.

This works well for components that will not necessarily appear on every page, like sub-menus. include also works well in theory to aid in separating layout from content, especially in very design-heavy pages.

We started by putting each code example into a separate include file. The doc files would pull in the required example files via include:

<div class="java language-example">
  <pre class="prettyprint linenums">
    {% include java/single_purchase_example.html %}
  </pre>
</div>
<div class="dotnet language-example">
  <pre class="prettyprint linenums">
    {% include dotnet/single_purchase_example.html %}
  </pre>
</div>
<div class="php language-example">
  <pre class="prettyprint linenums">
    {% include php/single_purchase_example.html %}
  </pre>
</div>
<div class="ruby language-example">
  <pre class="prettyprint linenums">
    {% include ruby/single_purchase_example.html %}
  </pre>
</div>

This way, the devs could create/edit the code examples as pure code without mucking around with surrounding markup.

While having content and format separated was nice, it left something to be desired in the amount of repetitive markup. Considering that the page had ten-or-so code examples in four programming languages, this meant about 40 example blocks and a lot of copy-paste.

It seemed like a good time to do some optimization.

Envisioned Partial Template Solution

Ideally, we would have liked to be able to procedurally generate a complete code example ‘block’ (formatting and example content, for each programming language) in a single call.

So starting with common formatting for any general code sample:

_includes/code_example.html

# require parameter 'example_file' 

<pre class="prettyprint linenums">
  {% include include.example_file %}
</pre>

We’d build upon that with a language-based builder:

_includes/code_example_all_langs.html

# languages = ['java', 'dotnet', 'php', 'ruby']
# require parameter 'example_file' 
{% for lang in languages %}
  {% capture filename %}{{ lang }}/{{ include.example_file }}{% endcapture %}
  <div class="{{ lang }} language-example">
    {% include code_example.html example_file=filename %}
  </div>
{% endfor %}

Then whenever we need to spit out a code example, we could do it in a single line:

{% include code_example_all_langs.html example_file='example-file' %}

There was only one small problem… it turns out that the include file argument only takes a string literal and variables are not allowed.

Googling found some people that managed to accomplish this via plugins/source modification, but from the official Jekyll point-of-view this mechanic will NOT be supported (see issue #176).

At this point, there wasn’t much to be gained by the envisioned outer for loop - it would end up as a for loop with an ugly if-else block. The last salvageable part of the solution (if even salvageable) would be the general code example markup case.

A Partial Template Approximation

As mentioned before, include in Jekyll can take parameter values. Although we cannot dynamically set the filename argument, we can approximate the net effect by abusing parameter values. Where we wanted to use a variable include, instead a parameter is used to pass the content.

_includes/code_example.html

# require parameter 'code_content' 

<pre class="prettyprint linenums">
  {{ include.code_content }}
</pre>

Usage:

{% capture example_content %}
  {% include single_purchase_example.html %}
{% endcapture %}
{% include code_example.html code_content=example_content %}

This actually works! But it’s not a great solution.

  • It’s ugly.
  • It relies on a global-variable-esque passing mechanic.
  • It seems to work fine in Jekyll, but not on GitHub Pages’ flavour of Jekyll.

Aftermath

We decided not to go with the hacky partial templates and went back to our original setup. At the very least, we retained the separation of content and formatting.

I wouldn’t exactly call this exercise a ‘failure’ as working with any new technology or platform involves figuring out what you can/cannot do and getting used to the tool’s conventions.

Once you start finding that you’re spending an awful lot of time looking-up/trying weird workarounds, you’ve likely hit the tool’s functionality boundary :).