Liquid フィルタの書き方

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 %}

参考