Upgrade to Pro — share decks privately, control downloads, hide ads and more …

5 min Jekyll/Liquid Plugin cooking

wtnabe
January 18, 2025

5 min Jekyll/Liquid Plugin cooking

北陸三県.rb / Kanazawa.rb meetup 149 で話した Jekyll/Liquid plugin の作り方、作るときのややこしさの資料です。意外と出回っていない、現実的な plugin 作りのノウハウになってるんじゃないかと思います。

wtnabe

January 18, 2025
Tweet

More Decks by wtnabe

Other Decks in Programming

Transcript

  1. Jekyll plugin とは Plugins | Jekyll • Simple, blog-aware, static

    sites Generators Converters Commands Tags Filters 加えて必要な知識は Hooks
  2. ザクっと一口紹介 plugin 役割 Generators 何らかのコンテンツを生成するもの eg) 例えばブログのアーカイブページ Converters ファイルのコンテンツを変換するもの Tags

    カスタムLiquid タグ ( Liquid-compat ) Filters {{ }} の中で変換 ( Liquid-compat ) 今回は Filters と Tags だけの予定(ここまでの内容はLiquid レベル)
  3. Hooks site posts ( pages etc ) after_init post_init after_reset

    post_read pre_render pre_render post_convert post_render post_render post_write post_write
  4. 公式サンプル module Jekyll module AssetFilter def asset_url(input) "http://www.example.com/#{input}?#{Time.now.to_i}" end end

    end Liquid::Template.register_filter(Jekyll::AssetFilter) 使い方 {{ var | asset_url }}
  5. 引数を持つ場合 # @param [Object] input # @param [Object] mode #

    @return [String] def slugify(input, mode = nil) .. end {{ var | slugify: 'ascii' }}
  6. Filter について覚えること 1. 値を受け取って返すmethod を作って、何らかのmodule に入れる 2. そのmodule を Liquid::Template.register_filter(<module>)

    で登録 3. Liquid 上で {{ var | method: arg1, arg2, ... }} だけ。 ※ Filter はLiquid 上で動くだけなのでRails 上でも再現可能
  7. Tag の基本形 class Tag < ::Liquid::Tag # @param [String] name

    # @param [String] text # @param [Object] tokens def initialize(tag_name, text, tokens) super @text = text end # @param [Object] context def render(context) end end Liquid::Template.register_tag(<name>, Tag)
  8. Tag は2 種類ある 1. render 中に機能を加えるもの(eg, include ) class Liquid::Tag

    を継承 template 上の記法 {% tag attributes %} 2. tag に挟まれたコンテンツを扱うもの class Liquid::Block を継承 template 上の記法 {% tag %} {% endtag %} Liquid::Block < Liquid::Tag なので基本は一緒 ※ Jekyll::Site に依存しなければLiquid が動けばどこでも動く
  9. Tag 実装の基本のキ Tag はClass tag のname は Liquid::Template.register_tag() 時に決定 実装次第ではユーザーにname

    設定を委ねることも可(衝突回避) initialize() に渡ってくるtext は内容じゃなくてattribute 部分 {% tag attribute %} ここのparse は開発者に丸投げされている render(context) にはsite 丸ごと渡ってくる context.registers[:site] 中のコンテンツを取得したければsuperclass の結果を使う
  10. Tag 実装のTips Tag や Block を継承したClass はあくまでAPI と解釈 context を使う必要がないなら潔く無視

    initialize 時に取得できるattribute はインスタンス変数に保存 parse 後がベスト 独自の実装部分はすべてModule に実装してinclude する こうすることでFilter と同様に純粋にロジック部分をテスト可能
  11. Tag のテストのTips 詳細はModule に書いてBlank Slate にinclude してテスト、が原則 {% tag attributes

    %} のattributes の解釈をテストしたい場合は Liquid::Template.register_tag() から コンテンツの内容はLiquid のsuperclass を信じて割り切る
  12. 基本系 _plugins/generator.rb class Generator < Jekyll::Generator # @param [Jekyll::Site] site

    # @return [void] def generate(site) # siteに対する何らかの副作用 end end
  13. 処理の流れ module Jekyll class Site def process .. reset read

    generate # <- ココ render cleanup write end end end ※ plugins_dir の中の Generator は自動で適用される
  14. Page のClass class Page < Jekyll::Page attr_accessor :site, :basename, :content,

    :data, :ext, ... # @param [Jekyll::Site] site # @param [String] base - base directory # @param [String] dir - dirname of source file in base dir # @param [String] name def initialize(site, base, dir, name) end end ページの中身を詰める処理 page = Page.new(site, basem dir, name) page.data[key] = value
  15. 実際のPage のrender 時に利用するtemplate ※ 例えば関連ページが data の中に入っているものとする <ul> {% for

    post in page.posts | reversed %} <li><a href="{{ post.url | relative_url }}">{{ post.title | escape }}</a></li> {% endfor %} </ul> URL として反映される元ファイルのないページを作る場合はlayout な どでrender 処理をアシストする必要アリ
  16. しかも公式がこんなこと言ってる 1 ファイルで閉じてるgenerator を作ったのなら .rb で好きな名前を付 けることができる。もし複数のファイルに分けたいなら gem にパッケ ージして

    rubygems.org に公開すべき。この場合は gem の名前は rubygems.org 上で利用可能なものに限られる。 If your generator is contained within a single file, it can be named whatever you want but it should have an .rb extension. If your generator is split across multiple files, it should be packaged as a Rubygem to be published at https://rubygems.org/. In this case, the name of the gem depends on the availability of the name at that site because no two gems can have the same name. “ “
  17. Generator のおさらい 1. Jekyll::Generator を継承し、 generate メソッドを実装 2. generator はread

    とrender の間で自動的に動作 3. site オブジェクトに副作用を及ぼし、render 対象を増やす 4. render 時に必要なパーツは別途準備 ※ あと当たり前だけど常時動いていると重い 結局site.rb を読むのが重要…