PureBuilder Simplyの設計と構造
開発::util
序
完成度が上がってきて評判も上々のPureBuilder Simply。
バージョン1.8以降、立て続けのupdateで新しい姿を見せているが、このソフトウェアが長く続くことは確定的なので、そろそろ解説していこうと思う。
なお、ソフトウェアの概要と使い方に関しては過去の記事で紹介しており、動画を含む解説も別にある。
前提
私の昔のブログを読んでいた人しか知らないと思うが(つまりは誰も知らないと思うが)、私はACCSとは別にCMSのFRECS(FRECS’s Relation Enhancement Content System)を作っていた。
FRECSは極限まで単純なものである。PHPで書かれていたが、今改めてRubyで書くと、概念的には次のようになる。(もちろん、様々な考慮は省いている)
require 'cgi'
require 'erb'
require 'yaml'
= CGI.new
cgi = ERB.new File.read "/srv/http/template.erb"
template
= File.read "/srv/http/content/#{cgi}"
content = YAML.load File.read "/srv/http/content/#{cgi}_meta"
param
puts "Content-Type: text/html; charset=UTF-8"
puts
.run(binding) template
つまり、
- コンテンツを読んで
- 付加情報を変数にセットして
- テンプレートを解釈する
だけである。
そんなものがCMSと呼べるのか、と思うかもしれないが、実際これでCMSとしての本質的な機能は実現できる。 FRECSはCMSとしての機能が事前生成戦略によって成立することを確認するために作ったものであり、これはサイト構築を行うソフトウェアの基幹があると考えるようになった。
legacy
初代のPureBuilderは簡単に言えば次のように動作する。
#!/bin/zsh
. .purebuilder.rc
for i in ./**/*.pdoc
do
puredoc $i > ${i/.\//$outdir/}
done
これに拡張したり割り込んだりするためのフィルタスクリプトを通す機能があったりするようなものであり、PureBuilderはこういう非常に単純なソフトウェアである。
PureBuilderはACCS4と組み合わせて使用することを想定していた。 ACCS4はPureDoc向けであり、PureDocのメタデータを動的に書き換える、いわゆるリフレクションスクリプトだ。 だから
$ accs4.rb
$ purebuilder.zsh
とすればサイトが出来上がる、みたいな感じだった。
PureBuilder ngに関しては、こうした機能がひとつにまとめられている。 PureDocのコンバーターは当然ながら以前として別のソフトウェアだが、コマンドとしてPureDocを呼び出すのではなく、ライブラリとして使用するようになったし、さらにPureBuilder ng自体にKramdownを使ったMarkdownコンバーターが追加された。
だが、PureBuilder ngも方針としては、「やりたいことがあるならAPIを使ってね」という感じだった。 「プラグイン」と呼んではいたが、PureBuilderが短いシェルスクリプトを書くことを前提にしていたのに対し、PureBuilder ngではRubyで書くことを前提としており、「サイト構築スクリプトを書くためのフレームワーク」みたいな感じだった。
だから、どちらかというとJykillみたいなソフトウェアというよりは、Railsの静的ビルド版みたいな感じのほうが近いかもしれない。特にPureBuilder ngは書くべきことが増えたのでそんな感じだった。
根幹
何度も言うように、PureBuilder SimplyはPandocを使っている。
Pandocはソースドキュメントを他形式のドキュメントに変換するソフトウェアだが、テンプレートエンジンを持っており、テンプレート変数はドキュメント内に含めるとこができる。
PureBuilderではPureDocがその役割を担っていたが、Pandocは同等にそれを担うことができるのである。 ただし、PureDocはeRubyテンプレートなのでフルプログラマブルだが、Pandocはもっと単純なテンプレートエンジンであるという違いがある。
いずれにせよ、Pandocを前提にするとPureBuilderの考え方としては単純に再帰的に処理するくらいしかなくなってしまう。 一方、Pandocを使う関係上、処理を差し挟む余地がなくなる。考えられるのは
- ソースドキュメントを編集する
- Pandocの引数をいじる
- 出力したドキュメントを編集する
の3つだけである。
PureBuilder Simplyはこの3つともを実現した。
Pandocの引数に関しては設定ファイルによってオプションを追加することができるし、出力したドキュメントはpost pluginsという仕組みによってフィルタを通される。 PureBuilder Simply 1.2でソースドキュメントを編集するpre pluginsにも対応した。
だが、これだと他の記事のメタデータを参照するようなことができない。 それをスマートに実現するには、ずっと課題であった。
PureBuilder Simplyはこれを実現するために、「処理したドキュメントのメタデータをデータベースとして保存する」という方法をとった。 Ruby Marshal形式なのでRubyでしか扱えないが、post pluginsが呼ばれるのは全てのドキュメントの処理を終えた後なので、同ディレクトリ内の全てのドキュメントのメタデータにアクセスできる。
これが可能になっただけで自由度は飛躍的に向上した。 力技ではあるが、たいていのことはできるようになったし、現時点でもMimir Yokohamaはその方法をとっている。
最新版の概要
PureBuilder Simply 1.8でPandoc 2.8の機能を使うようになって以降、そのモデルは少し変わった。 最新版ではメタデータをJSONとして保存することもできるようになり、pluginsに頼る設計も改められた。
- 設定ファイルを読み込む。設定ファイルはPandocのパラメータや、テンプレート変数などを含めて指定が可能
- 全てのソースドキュメントを読み込み、メタデータを解釈する。ドキュメントはメタデータ(YAML)とドキュメントに分離される。このとき、draft記事は除外される。また、draft記事と競合する生成済み記事がある場合、削除される
- インデックスデータベースを保存する
- Blessingを行う。これによってユーザーはメタデータをプログラマブルに編集することができる
- 更新をチェックする。更新されていないドキュメントはforce refreshでない限り生成がスキップされる
- 各ソースドキュメントを処理する
- ソースドキュメントのドキュメント部をファイルに保存する
- pre pluginsによる処理を行う
- Pandocによって処理を行う。default fileとmetadata fileを使用する
- post eRubyが有効な場合、Pandocが出力したデータをeRubyで処理する
- 出力ドキュメントとして書き出す
- 今セッションで処理したドキュメントのリストに追加する
- インデックスデータベースを保存する
- post pluginsによって今セッションで処理したドキュメントを書き換える
- 処理対象がACCSディレクトリであるならば、ACCSによる処理を行う
シングルファイルモードの場合はまた違う処理を行うが、基本的にはこういう流れである。 変更を追いかけている人には複雑に思えるかもしれないが、実際こうしてまとめると割とシンプルな動作だ。 これは以下のようにまとめられる。
- 設定ファイルの行き場は基本的にはPandocのdefault fileとmetadta file
- draftに戻されたファイルは生成済みのものはディレクトリ処理で削除される
- draftなファイルは最初に除外されるのでないものとして扱える
- Blessingされるのは全てのメタデータを読んだ後。インデックスデータベースもその前に保存されている
- Blessingされたあと改めてインデックスデータベースは保存される
- Pandocで処理されるのは分離されたドキュメント部。Blessing時点ではファイルとして存在しない
- pre pluginsはPandocが呼び出される前に処理される
- post eRubyが行われるときはまだ出力ファイルは保存されていない
- post pluginsでの処理は全ドキュメントの保存が終わってから
- post pluginsで処理されるのは実際に今回生成されたものだけ
- ACCSの処理は全て終わってから改めて行われる
ユーザー側で操作できることは次の通り
- 設定ファイル (default file, metadata fileとPureBuilder Simplyに影響)
- Frontmatter
- Pandocテンプレート
- Pandocテンプレート内のeRuby
- pre plugins (ソースドキュメントの編集, ドキュメント部のみ)
- post plugins (出力ドキュメントの編集)
- Blessing (Rubyの関数として定義するか、外部コマンドのどちらか)
- ACCSディレクトリの有効/無効
- ACCS eRubyテンプレート
また、PureBuilder Simplyは生成先ディレクトリ内の非ドキュメントディレクトリに関しては関知しないので、他のファイル生成や、単純なファイルコピーと組み合わせることができる。