Jekyll で使われているテンプレートエンジン Liquid は、フィルターを作ることで、機能を拡張することができます。
簡単な例
まずは、簡単なフィルタの例を示します。
まず module を作成して、1 つ以上の引数を持つメソッドを実装します。この module を Liquid::Template.register_filter
に渡すことでフィルタを追加できます。
# URL エスケープする
module URLEscapeFilter
def url_escape(input)
ERB::Util.u input
end
end
Liquid::Template.register_filter(URLEscapeFilter)
Jekyll なら、これを _plugins
下に適当なファイル名で保存します。
メソッド名がそのままフィルタの名前になるため、Liquid テンプレート内では、このフィルタは下記のように使えます (page.tags
は文字列の配列とする)。
{% for tag in page.tags %}
<a href="/tag/{{ tag | url_escape }}">{{ tag }}</a>
{% endfor %}
フィルタに対応するメソッドの 1 つめの引数は、テンプレートで |
の前にあるものが渡されます。また、メソッドの返り値がそのままフィルタの出力になります。
それでは、いろいろなフィルタの例を見てみましょう。
引数をとるフィルタの例
フィルタが引数をとる場合、対応するメソッドは第 2 引数でそれを受け取ることができます。
# length より長ければ、length 以降を省略して ... を追加
def truncate(input, length)
if input.length <= length
input
else
input[0..length] + "..."
end
end
{% post.title | truncate: 20 %}
配列を返すフィルタの例
フィルタは文字列以外にも、配列や Hash を受け取ったり、返したりすることができます。
この例では、配列を受け取り、配列を返しています。
# 配列の最初の number 個を返す
def head(input, number)
input.first(number)
end
{% assign first_ten_posts = site.posts | head: 10 %}
{% for post in first_ten_posts %}
<a href="{{ post.url }}">{{ post.title }}</a>
{% endfor %}
返り値のオブジェクトは to_liquid
メソッドを持つ必要があるようです。下記のクラスは Liquid により拡張されて、to_liquid
メソッドが定義されている (self を返す) ので、返り値に使うことができます。
String
Array
Hash
Numeric
Time
DateTime
Date
TrueClass
FalseClass
NilClass
Jekyll では、Jekyll::Post などが to_liquid
メソッドを実装しています。
なお、Hash を返す場合、キーを String にしておく必要があります。キーが String 以外だと {{ foo.bar }}
のような書き方でアクセスすることができなくなくなります。
すこし複雑なフィルタの例
最後にこのブログのトップページで使用しているフィルタの例を紹介します。
Jekyll::Post の配列を受け取り、配列を含む Hash の配列を返しています。
# post の配列を受け取り
# post.date の年ごとに {"year" => 年, "collection" => post の配列} の配列で返す
def group_by_year(input)
input.group_by{|item|
item.date.year
}.map{|year, collection|
{"year" => year, "collection" => collection}
}
end
{% assign posts_group_by_year = site.posts | group_by_year %}
{% for posts_of_year in posts_group_by_year %}
<h2>{{ posts_of_year.year }}</h2>
{% for post in posts_of_year.collection %}
<a href="{{ post.url }}">{{ post.title }}</a>
{% endfor %}
{% endfor %}