ssg  
Article Series

Jekyll in General

Jekyll Plain

Where to Discuss?

Local Group

Preface

Goal: Using generator to manage pages for each tag name.

Source Code

This article use tutor-06 theme. We will create it step by step.


Introduction

Official Documentation

The code in this page is almost a verbatim copy from:

Applying this short snippets in real life blog, turned out a wide topic, that deserve an article of its own.

Tag Names

What is this rubbish?

This means we can make pages based on tags such as:

  • http://localhost:4000/tags/soul/

  • http://localhost:4000/tags/jazz/

Jekyll cannot generate this extended URL by default, but Jekyll allow you to create this using plugin. Or to be exact, generator plugin, with the generated files result as below:

$ tree _site/tags
_site/tags
├── 2010s
│   └── index.html
├── husky
│   └── index.html
├── index.html
├── indie
│   └── index.html
├── jazz
│   └── index.html
├── pop
│   └── index.html
├── rock
│   └── index.html
├── sleepy
│   └── index.html
├── soul
│   └── index.html
├── story
│   └── index.html
└── subtitle
    └── index.html

10 directories, 11 files

Jekyll: Tags NERDTree

Caveat

Not all CI/CD support plugin.

If your CI/CD do not support jekyll plugin, then you cannot use any plugin, including this custom generator plugin

Preparation: Using Generator

All you need to care is to make a layout name, such as tag-names.html. You can choose any name, as long as it match the code in your plugin.

---
layout: page
---

1: Generator: Tag Names

The Original Code

Hmmm….

I do not really understand ruby, but the example code in official site contain this two classes.

module Jekyll
  class TagPageNameGenerator < Generator
    ...
  end

  # A Page subclass used in the `TagPageGenerator`
  class TagPageName < Page
    ...
  end
end

The TagPageNameGenerator.

  class TagPageNameGenerator < Generator
    safe true

    def generate(site)
      if site.layouts.key? 'tags'
        dir = site.config['tag_dir'] || 'tags'
        site.tags.each_key do |tag|
          site.pages << TagPageName.new(
            site, site.source, File.join(dir, tag), tag)
        end
      end
    end
  end

And TagPageName.

  # A Page subclass used in the `TagPageGenerator`
  class TagPageName < Page
    def initialize(site, base, dir, tag)
      @site = site
      @base = base
      @dir  = dir
      @name = 'index.html'

      self.process(@name)
      self.read_yaml(File.join(base, '_layouts'), 'tags.html')
      self.data['tag'] = tag

      tag_title_prefix = site.config['tag_title_prefix'] || 'Tag: '
      self.data['title'] = "#{tag_title_prefix}#{tag}"
    end
  end

This should be working with your tags.html layout.

What does it offer?

Requirement?

How about an adjustment, to suit my needs?

  1. Source Layout Name: tag-names.html

  2. Destination files: tags/*/index.html

  3. Content: tag name and post.

Changing source name:

The first one is easy, just change the name

The TagPageNameGenerator.

    def generate(site)
      if site.layouts.key? 'tag-names'
        dir = site.config['tag_dir'] || 'tags'
        ...
      end
    end

And TagPageName.

    def initialize(site, base, dir, tag)
      ...

      self.process(@name)
      self.read_yaml(File.join(base, '_layouts'), 'tag-names.html')
      self.data['tag'] = tag

      ...
    end

The Need of Content

We do not want empty pages right?

As a preparation, our page can simply contain badge of tags as usual.

---
layout: page
aside_message : This line will be ignored
---

{% assign terms = site.tags %}
{% assign term_array = terms | term_array | sort %}
{% include index/terms-badge.html %}

Jekyll: Tags: Badges Only

Of course this is note enough, we need something more in each tag page, such as showing all post related to that tag.

---
layout: page
aside_message : This line will be ignored
---

{% assign terms = site.tags %}
{% assign term_array = terms | term_array | sort %}
{% include index/terms-badge.html %}

{% assign posts = page.posts %}
{% include index/blog-list.html %}

Jekyll: Tags: Blog List

The Riddle?

How do I get this page.posts?

Is is more like a question of where, rather than how to, because al you need to do is just adding this one line.

    def initialize(site, base, dir, tag)
      ...

      self.process(@name)
      self.read_yaml(File.join(base, '_layouts'), 'tag-names.html')
      self.data['tag'] = tag
      self.data['posts'] = site.tags[tag]

      ...
    end

Notice this bad boy: self.data['posts'] = site.tags[tag].

Add Decoration

You can also add a slight change to make the tag name capitalized.

    def initialize(site, base, dir, tag)
      ...

      tag_title_prefix = site.config['tag_title_prefix'] || 'Tag: '
      self.data['title'] = "#{tag_title_prefix}#{tag.capitalize}"
    end

Complete Plugin Code

Now we can summarized the ruby generator plugin code.

module Jekyll
  class TagPageNameGenerator < Generator
    safe true

    def generate(site)
      if site.layouts.key? 'tag-names'
        dir = site.config['tag_dir'] || 'tags'
        site.tags.each_key do |tag|
          site.pages << TagPageName.new(
            site, site.source, File.join(dir, tag), tag)
        end
      end
    end
  end

  # A Page subclass used in the `TagPageGenerator`
  class TagPageName < Page
    def initialize(site, base, dir, tag)
      @site = site
      @base = base
      @dir  = dir
      @name = 'index.html'

      self.process(@name)
      self.read_yaml(File.join(base, '_layouts'), 'tag-names.html')
      self.data['tag'] = tag
      self.data['posts'] = site.tags[tag]

      tag_title_prefix = site.config['tag_title_prefix'] || 'Tag: '
      self.data['title'] = "#{tag_title_prefix}#{tag.capitalize}"
    end
  end
end

Jekyll: Generator Plugin: Tag Names

We are finished with the generator plugin. But we are not done yet with this cool topic.


2: Layout: Changing Include View

Alternate View

If you looks closely in my repository, there is also a few lines in commented code:

---
layout: page
aside_message : This line will be ignored
---

{% assign terms = site.tags %}
{% assign term_array = terms | term_array | sort %}
{% include index/terms-badge.html %}

{% assign posts = page.posts %}
{% include index/blog-list.html %}

{% comment %}
  Alternative view
  {% include index/by-month.html %}
  {% include index/by-year.html %}
  {% include index/blog-list.html %}
{% endcomment %}

Jekyll: Tag Names: as Blog List

Tags: by Year

Consider see how that tag name pages looks, in archive by-year list fashioned.

{% assign posts = page.posts %}
{% include index/by-year.html %}

Jekyll: Tag Names: as Archive by Year

Tags: by Month

And how that tag name pages looks, in archive by-month list fashioned.

{% assign posts = page.posts %}
{% include index/by-month.html %}

Jekyll: Tag Names: as Archive by Month

I hope that you got the point, why I separate the reusable code in _includes, instead of just put them in layouts.


3: Adjustment: URL Changes

We are still, not done yet. Changes might impact URLs. So we need adjustment in a few places. In this simple blog site, only two places.

Partial Liquid: Terms Badge

Adjust the href to change URL from

  <a href="#{{ this_word | slugify }}">

To

  <a href="{{ site.baseurl }}/tags/{{ this_word | slugify }}/">
  <p>Tag Badges:
  {% for item in (0..terms.size) %}{% unless forloop.last %}
    {% assign this_word = term_array[item] | strip_newlines %}
    <span style="white-space: nowrap;">[
      <a href="{{ site.baseurl }}/tags/{{ this_word | slugify }}/">
        {{ this_word }}
      </a> 
      <small>({{ terms[this_word].size }})</small>
    ]</span>
    &nbsp;
  {% endunless %}{% endfor %}
  </p>

Partial Liquid: Blog List

Adjust the href to change URL from

  [ <a href="{{ site.baseurl }}/tags/#{{ tag | slugify }}"
      >{{ tag }}</a> ]

To

  [ <a href="{{ site.baseurl }}/tags/{{ tag | slugify }}"
      >{{ tag }}</a> ]
  <ul>
  {% for post in posts %}
    <li>
      <a href="{{ site.baseurl }}{{ post.url }}">
        {{ post.title }}
      </a><br/>
      Tag: 
      {% for tag in post.tags %}
        [ <a href="{{ site.baseurl }}/tags/{{ tag | slugify }}"
            >{{ tag }}</a> ]
      {% endfor %}
    </li>
  {% endfor %}
  </ul>

And that is all.

In the next chapter with CSS frameworks with a lot of content, we are going to need more adjustment. Just be aware of URL changes, when you need apply something new.


What is Next ?

Consider continue reading [ Jekyll Plain - Theme - Bundling ].

Thank you for reading.