最近作った Jekyll (Liquid) のプラグイン

最近作った、Jekyll (Liquidテンプレート) のプラグインの解説を書きます。Jekyllの良いところは、Rubyプログラマーなら簡単に独自タグ等のプラグインが書けるところです。ただし、情報が少ないので参考になれば嬉しいです。


1. h1タグに付けた番号を引き継げる h2タグ

テキストを書いていると 1. Ruby入門 ... 1.1 リテラル ... 1.2 式 ... 1.3 制御構造 ... のように連番のサブタイトルを書きますが、途中に章が追加さると番号を振り直さないといけません。そこで、サブタイトル(h2)の連番を タイトル(h1) から引き継いだ連番を自動生成すれば良いわけです。

このタグはこのように使います、これで <h2>1.1 リテラル</h2> ... <h2>1.2 式</h2> ... <h2>1.3 制御構造</h2> ... が生成されます

{% h1 1 Ruby入門 %}
 ...  
{% h2 リテラル %}
 ... 
{% h2 式 %}
 ...  
{% h2 制御構造 %}
...

コードは以下のようになります。

module Jekyll
  class SequencedH1Tag < Liquid::Tag
    def initialize(tag_name, text, tokens)   
      @h1_text = text      
      super      
    end
  
    def render(context)
      context['h1_seq'] = @h1_text.to_i
      context['h2_seq'] = 0

      "<h1>#{@h1_text}</h1>"
    end 
  end

  class SequencedH2Tag < Liquid::Tag
    def initialize(tag_name, text, tokens)   
      @h2_text = text      
      super      
    end
  
    def render(context)
      context['h2_seq'] = context['h2_seq'] + 1

      "<h2>#{context['h1_seq']}.#{context['h2_seq']} #{@h2_text}</h2>"
    end 
  end
end

Liquid::Template.register_tag('h1', Jekyll::SequencedH1Tag)
Liquid::Template.register_tag('h2', Jekyll::SequencedH2Tag)
  • タグの解釈は initialize メソッドに、展開された文字列作成は render メソッドに書きます
  • initialize から render へ値を渡すには インスタンス変数を使います
  • initialize、render は テキストにタグ(ここでは h1, h2) が書かれている度に呼び出されます
  • ページ生成中に覚えておきたい値は context に格納します

2. テーブルの幅(%)指定タグ

これは

{% table_width table1 30,8,8 %}
table(table1).
|_. Railsのバージョン |_. Ruby1.8.X |_. Ruby1.9.X |
| 2.3.X |=. ○ |=. x |
| 3.0.X |=. ○ |=. ○  |
| 3.1.X |=. ○ |=. ○  |
| 3.2.X (現在) |=. ○ |=. ○ |
| 4.0.X (次期) |=. x |=. ○ |

のように書くと下のような幅が指定されたテーブルが生成されます。

... ... は Textile のテーブルで table(table1). でテーブルに table1というクラス名を付けています。 我ながら便利だと思います ^^)/
<style type="text/css">
.table1 {width: 46%;}
.table1 td:nth-child(1) {width: 30%;}
.table1 td:nth-child(2) {width: 8%;}
.table1 td:nth-child(3) {width: 8%;}
</style>
<table class="table1">
	<tr>
		<th>Railsのバージョン </th>
		<th>Ruby1.8.X </th>
		<th>Ruby1.9.X </th>
	</tr>
	<tr>
		<td> 2.3.X </td>
		<td style="text-align:center;"></td>
		<td style="text-align:center;">x </td>
	</tr>
 	<tr>
		<td> 3.0.X </td>
		<td style="text-align:center;"></td>
		<td style="text-align:center;"></td>
	</tr>
    ・・・・
</table>

コードは以下で <style ...> 〜 </style>を生成しています。

module Jekyll
  class TableWith < Liquid::Tag
    Syntax = /(.*)\s+([\d,]+)\s*/o

    def initialize(tag_name, markup, tokens)
      if markup =~ Syntax
        @table_class = $1
        @width_array = $2.split(/,/).map{|e| e.to_i}
      else
        raise SyntaxError.new("Syntax Error in 'table_width' - Valid syntax: table_width [class of table] [width, width...]")
      end
    end

    def render(context)
      s = %|<style type="text/css">\n|
      s += "  .#{@table_class} {width: #{@width_array.inject(0){|x,s| s += x}}%;}\n"
      @width_array.each_with_index {|w, ix| s += "  .#{@table_class} td:nth-child(#{ix + 1}) {width: #{w}%;}\n"}
      s += "</style>\n"
      s
    end 
  end
end

Liquid::Template.register_tag('table_width', Jekyll::TableWith)
  • initializeメソッドの引数の解釈やエラーメッセージの表示は Liquid テンプレート コード https://github.com/Shopify/liquid の定型です
  • TextileとLiquid の適応の順番ですがですが、Textile が先に適応されます

3 「この章の目標」タグ

これは

{% goalbox %}
* TDDを知る
* RSpecについて理解する
* 受け入れテストを知る
{% endgoalbox %}

が 下のような tableになります

<table class="goalbox">
<tr><th>この章の目標</th></tr>
<tr>
   <td>
    <ul>
	<li>TDDを知る</li>
	<li>RSpecについて理解する</li>
	<li>受け入れテストを知る</li>
    </ul>
  </td>
</tr>
</table>

コードは

# -*- encoding : utf-8 -*-
module Jekyll
  class GoalBox < Liquid::Block
    def render(context)
      output = super
      %|<table class="goalbox">
          <tr><th>この章の目標</th></tr>
          <tr>
            <td>
              #{output}
            </td>
          </tr>
        </table>|
    end
  end
end

Liquid::Template.register_tag('goalbox', Jekyll::GoalBox)
  • Liquid::Blockを使うとコンテンツを持つタグが作れます。閉じタグは end〜 になります。
  • コンテンツの文字列は親クラスからもらいます

まとめ

このように、Jekyll なら同じパターンが良く出てくるドキュメントを書くのに、プラグインを書けば楽ができます。 Jekyll は怠慢なプログラマーに超お勧めなブログ、ドキュメントツールです。