2018年にもなってMiddlemanとJekyllを比較してみたよ

先に結論

独自に拡張していく、外部のデータと連携することを重視するなら Jekyll の方がよさそう。

Rails の view に慣れてて何も設定せずにいきなり使う場合の初速の速さは Middleman の方が上になる。

きっかけ

2016 年に『マイクロサービスアーキテクチャ』を読み、それとは別に Decoupled CMS という概念を知って以来、古くて評判のよくない CMS を使うことや自分で中途半端な CMS もどきを作ることに対して今まで以上に「このままじゃいかんよな」と思うことが増えていて、改めて静的サイトジェネレータをもうちょっとちゃんと試してみようと思っていたのが一つ。

もう一つは Rails の Webpacker gem など、バックエンドや CMS に Ruby を使っていてもフロントエンド周りはフロントエンドの作法に則った方がよさそうなので、その組み合わせ方を知っておかないといけないなと思ったこと。

これまで

Jekyll は GitHub Pages に必須という考え方で触っていて、Liquid が面倒くせえなぁ、ERB で素直に書かせてくれよくらいの気持ちだった。

一方で Middleman は Rails の View を触ったことがあれば違和感なく使えた。特に AssetPipeline のようなものが最初から使える部分でも非常に Rails によく似ており、Rails から大きく考え方や手順を変えることなく、Rails の不要なサイトを作る際に重宝するなぁ、くらいの捉え方だった。

設定

  • Jekyll は YAML
  • Middleman は独自の Config オブジェクトになる config.rb

ここだけ見るとどちらでもよさそうに見える。しかし実際はかなり違う。

なんでもかんでも YAML にするのもどうかとは思うが、一方で Middleman の Config オブジェクトは中に書いたコードの context を変更してしまうので気軽に分割できない。独自の DSL ならまだしも、pure Ruby で context が変わってしまうのはとてもやっかい。正直言うと多少でかく複雑になっても YAML の方がマシに見える。

ERB vs Liquid

ぱっと見て最初に気づく違いだと思うのはテンプレートとして ERB を使えるか否かだ。

個人的には Rails でも Haml や Slim には賛成できない派で、ERB で見にくくなるんなら何かを間違えてるんやで派なんだけど、Jekyll では ERB を使わせてくれない。

Liquid template language

Liquid を通してしか仕組みを提供できないので、一見すごくまどろっこしく感じる。適当に Helper っぽいものを足して気軽に Ruby のコードを call することはできない。ちゃんと Liquid Tag として register して使うように手配しないといけない。

対して Middleman は ERB を書けるので、Rails のようにある意味雑にどんどん Ruby のコードを書いていける。

GitHub Pages など制限環境ではそもそも該当するコードを呼べるかどうかという問題はあるものの、どの程度 adhoc に Ruby のコードを呼びたいか によって、特に最初のもどかしさは変わってくるだろう。なれるまでは Liquid::Tag の register は確かにとてももどかしい。

ただ、最近のフロントエンド向けのテンプレートなどは基本的に Liquid のような制限を持っているものが多いので、フロントエンドから入ってくる人にはそれほど大きな違和感はないかもしれない。もう Rails の View を基本に考える時代ではないのかもしれない。

基本の「構成」はよく似ている

  1. Asset Pipeline 的な Sass, CoffeeScript コンパイル機能を持つ
  2. Front Matter, Data Files, Collection を備える
  3. plugin ( extension ) 機構がある

Asset Pipelineの「外」の実現方法は異なるが考え方は似てる

Asset Pipeline 的なものの機能は似ているが、ではこれらを使わずにモダンフロントエンドの力を使う場合はどうしたらよいか。

  • Middleman v4 には External Pipeline があり、割と賢く asset を管理してくれる
  • Jekyll v3 標準では何も支援機構がないので以下のように自分で設定する
    1. フロントエンド → Jekyll の順に build が走るようにする
    2. 開発中はそれぞれ依存するコードを監視していい具合に動作する

といった具合で、機能の有無で差はあるが、考え方が分かればなんとかなる。ただし cache busting も Jekyll には存在しない機能なので、cache busting にこだわるなら Jekyll Assets など外部の何かを頼るなり、Web サーバの設定に頼るなり、設計が必要になる。

参考

Collectionは大きく違う

Front Matter, Data Files には特に言うことはないのだが、Collection には大きな違いがある。

Jekyll の Collection はものすごく乱暴に言うと Data Files のようにドキュメントを扱えるもの、である。collections_dir 以下に置いたドキュメントの固まりをページとは別に自由に取り出せる。Front Matter も使える。

対して Middleman の Collection は Extension の一形態を取っている。これが曲者で、

  • Middleman でコードを書くのは Contract との戦い
  • Middleman には本体外のコードで使える hook が少ない
    • (before|after)_(configuration|build) しかない

の二重の意味で使いにくくなってしまっている。

collection のデータ形式はこうですよ、こういうデータを作ってください、のような Jekyll 的なアプローチではなく、あくまでどの class のインスタンスを受け取るかというプログラミング言語レベルでのインターフェイスが基本になっていて、collection に対して適当な Array を与えるといったことができない。

Collection というよりは Extension の話になってしまっているし、サンプルに引きずられすぎて可能性を狭めているだけかもしれないが、今のところ collection を設定とデータで実現しやすい Jekyll の方が扱いやすそうに見える。

Plugin vs Extension

自分でそれぞれを拡張しようと思った際に選択できる手法について。

Plugins | Jekyll • Simple, blog-aware, static sites

Jekyll は Plugin であり、その Plugin に種類がある。

  • Generators
  • Converters
  • Commands
  • Tags
  • Filters

Generator は site 全体に対して何らかの追加コンテンツを生成する、Converter はコンテンツを変換するもの、Tags, Filters はそれぞれ Liquid に対する追加 といった具合に役割が整理されている。

Middleman: Custom Extensions

対して Middleman は Extension という仕組みであり、Extension がどんな役割を担うのかは割と自由に決められる。Tag かもしれないし、resources を加工するものだったりする。extension から configuration, template などへメソッドを expose して利用する。

Hookはかなり違う

Jekyll は

Hooks | Jekyll • Simple, blog-aware, static sites

site, page, document に対してそれぞれ細かく hook が設定されており、割といろんなことができそうな感じがする。

Middleman: Custom Extensions

対して Middleman は Extension に対して提供されると決まっているうえで、コア機能以外に解放されているのは

  • before_configuration
  • after_configuration
  • before_build
  • after_build

しかない。

Rack

Middleman は標準で Rack アプリだが、Jekyll は独自に WEBRick 上で動いている。Jekyll を Rack で mount したい場合は rack-jekyll が必要。

adaoraul/rack-jekyll: Transform your Jekyll app into Rack application!

情報量

圧倒的に Jekyll の方が多い。

その他 - MiddlemanのContractに気をつけろ

独自に拡張するコードを書こうと思った場合、ごく単純に言うと Jekyll には Liquid, Middleman には Contract という制約がある。

extension のところでの話のくり返しになってしまうが、Middleman の extension を軽く書いてみた1ところ、Contract がヒジョーにつらいという感想しかなかった。

やったことは単に独自の Collection を生成したかっただけなのだが、ドキュメント不足とあいまって中のコードをひたすら追うことになってしまい、一応動くのは動く状態になったがまったく納得のいくデキにならなかった。

Contract とはごく乱暴に言うと Ruby のコードに対して型を定義できるようなものである。

egonSchiele/contracts.ruby: Contracts for Ruby.

型定義をメソッド定義以外に書けるならよい、と自分は思っていたし、その考え方自体は今もあまり変わらないんだけど、実際には「型だけあってもその型通りのデータを外からどう与えるかという API がないとただツライだけ」だなということがよく分かった。

もともと Web プログラミングのように外から文字列でデータを与えることが前提になっているコードに型だけあってもつらいよねというのは思っていたんだけど、ドキュメントや周辺のツール、そして よい設計 が揃って始めて Contract ベースのプログラミングはスムーズに行えるんだなぁということを痛感したのでした。

※ Contract を外して build するオプションもあるんだけど

正直言うと Contract ベースで書くよりは Liquid のレールに慣れる方がドキュメントも事例も見つかるし、たぶんかなり簡単。

まとめ

ERB と Middleman 標準の asset digest にこだわる必要がないなら Jekyll の方がよさそう。

特に Decoupled CMS やモダンフロントエンドなど、外部とどう組み合わせるかという点を重視した際の拡張しやすさは Jekyll の方に軍配が上がると思う。

  1. 実際には必要なかった 

More