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
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?
-
Source Layout Name:
tag-names.html
-
Destination files:
tags/*/index.html
-
Content:
tag
name andpost
.
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 %}
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 %}
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
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 %}
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 %}
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 %}
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>
{% 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.