Preface
Goal: Move complexity from liquid code to ruby plugin.
Source Code
This article use tutor-06 theme. We will create it step by step.
1: Introduction
Why Plugin?
Having some fun!
I always want my code to be clean, especially my template. Instead of putting code in template, I prefer put the code somewhere else, such as in ruby plugin.
Example Plugin
Although plugin can do something that liquid cannot do. I haven’t explore plugin that far.
Most of the example provided here, is a direct port from liquid. My code building, always start from liquid. If the liquid code tends to be spaghetti, I moved the code to ruby plugin.
Of course we need better plugin example and tutorial. But all I’ve got is based on my limited experience. I hope this introduction is enough for beginner.
All plugin files placed in _plugins
directory.
$ tree _plugins
_plugins
├── hello-world.rb
├── is-show-adjacent.rb
├── pagination-links.rb
├── tag-names.rb
├── term-array.rb
├── term-keywords.rb
└── text-year.rb
0 directories, 7 files
Caveat
Not always supported
Plugin is not always supported, depends on the CI/CD. If what you have is github pages, you’d better use liquid script, and stay away from ruby plugin.
Bundling Theme
Is it just me?
I have no luck to bundle plugin with theme in Gem. So I guess, jekyll theme does not support plugin neither.
Performance
I have done testing.
AFAIK, there is no significance performance, between plugin and liquid code.
However, I still a fan of plugin anyway.
Development
You have to restart the server, for every plugin changes, to get the effect.
This is why coding in liquid is easier for beginner.
Official Documentation
Plugin Flags
I have read the documentation. I have asked my friend. But I still don’t understand what these two below, useful for for my blog.
safe true
priority :low
1: Tag: First Plugin
My first plugin is, of course, copy paste from someone else.
Hello World Example by Jesse Clark
module Jekyll
class HelloWorld < Liquid::Tag
def initialize(tag_name, text, tokens)
super
@text = text
end
def render(context)
"Hello World, #{@text}!"
end
end
end
Liquid::Template.register_tag('hello', Jekyll::HelloWorld)
Content: Home
---
layout : home
---
{% hello Folks %}
Official Documentation
2: Filter: Simple Text Year
Remember our archive page?
Liquid: Year Text
We can creatively change the text to show nicer name such as This year’s posts (2020) instead just 2020.
{% capture spaceless %}
{% assign current_year = 'now' | date: '%Y' %}
{% assign year_text = nil %}
{% if year.name == current_year %}
{% assign year_text = year.name
| prepend: "This year's posts (" | append: ')' %}
{% else %}
{% assign year_text = year.name %}
{% endif %}
{% endcapture %}
Ruby: Year Text
With plugin, this could be rewritten as:
def text_year(post_year)
localtime = Time.now
current_year = localtime.strftime("%Y")
if post_year == current_year
year_text = "This year's posts (#{post_year})"
else
year_text = post_year
end
year_text
end
Or you can make the code to be as short and cryptic. Then we have our final ruby code below:
module Jekyll
module TextYear
def text_year(post_year)
(post_year == Time.now.strftime("%Y")) ?
"This year's posts (#{post_year})" : post_year
end
end
end
Liquid::Template.register_filter(Jekyll::TextYear)
Includes: By Year
Now you can use the filter in liquid code:
{% assign postsByYear = posts
| group_by_exp: "post", "post.date | date: '%Y'" %}
<div id="archive">
{% for year in postsByYear %}
<p>{{ year.name | text_year }}</p>
{% endfor %}
{% assign postsByYear = posts
| group_by_exp: "post", "post.date | date: '%Y'" %}
<div id="archive">
{% for year in postsByYear %}
<section>
<p class ="anchor-target"
id="{{ year.name }}"
>{{ year.name | text_year }}</p>
<ul>
{% for post in year.items %}
<li><a href="{{ site.baseurl }}{{ post.url }}">
{{ post.title }}
</a></li>
{% endfor %}
</ul>
</section>
{% endfor %}
</div>
The liquid code looks tidy now.
3: Filter: Content: Term Array
On most SSG, dealing with tags and categories, need understanding of its data structures.
Liquid: Building Array of Terms
On our early articles, we have already built, this liquid code to represent our tags and categories.
{% capture spaceless %}
{% assign term_array = "" | split: "|" %}
{% for tag in terms %}
{% assign term_first = tag | first %}
{% assign term_array = term_array | push: term_first %}
{% endfor %}
{% assign term_array = term_array | sort %}
{% endcapture %}
Ruby: Array of Terms
Sorry for my Ruby. I’m not a native speaker.
With plugin, this could be rewritten as:
def term_array_alternate_two(terms)
a = []
terms.each { |term| a << term.at(0) }
a
end
Or shorter, just return array [].
def term_array_alternate_one(terms)
terms.map { |term| term.at(0) }
end
Or even more shorter. Then we have our final ruby code below:
module Jekyll
module TermArray
def term_array(terms)
terms.keys
end
end
end
Layout: Tag List
Now you can use the filter in liquid code:
---
layout: page
---
{% assign terms = site.tags %}
{% assign term_array = terms | term_array | sort %}
{% include index/terms-badge.html %}
{% include index/terms-tree.html %}
Layout: Category List
And also apply this filter to show categories.
---
layout: page
---
{% assign terms = site.categories %}
{% assign term_array = terms | term_array | sort %}
{% include index/terms-badge.html %}
{% include index/terms-tree.html %}
4: Filter: SEO: Term Keywords
again, remember our SEO in html head?
Liquid: Merge Keywords
We require long lines, just to merge array. The problem comes, when we have to whether the array empty, or not.
{% capture spaceless %}
<!-- keywords -->
{% assign cats = '' | split: '' %}
{% assign tags = '' | split: '' %}
{% assign words = '' | split: '' %}
{% assign terms = '' | split: '' %}
{% if page.categories %}
{% assign cats = page.categories %}
{% endif %}
{% if page.tags %}
{% assign tags = page.tags %}
{% endif %}
{% if page.keywords %}
{% assign words = page.keywords %}
{% endif %}
{% assign terms = cats | concat: tags | concat: words %}
{% assign termssize = terms | size %}
{% endcapture %}
Ruby: Merge Keywords
Sorry for my Ruby skill.
def term_keywords(cats, tags, keywords)
terms = []
terms += cats if !cats.nil? && !cats.empty?
terms += tags if !tags.nil? && !tags.empty?
terms += keywords if !keywords.nil? && !keywords.empty?
terms
end
Or shorter, just return array []. We can have our final code as below:
module Jekyll
module TermKeywords
def term_keywords(cats, tags, keywords)
[cats, tags, keywords].reject{ |array| array.nil? }.flatten
end
end
end
Liquid::Template.register_filter(Jekyll::TermKeywords)
I’m not sure about cryptic versus readibility. I just want to have some fun with ruby features.
Using Filter
And here we are with the good parts, using the filter in partials.
{% capture spaceless %}
{% assign terms = page.categories
| term_keywords: page.tags
, page.keywords %}
{% assign terms_size = terms | size %}
{% endcapture %}
{% if terms_size != 0 %}
<meta name="keywords"
content="{{ terms | join: ", " }}">
{% endif %}
This will result as below html source code.
What is Next ?
Consider continue reading [ Jekyll Plain - Plugin - Pagination ].
Thank you for reading.