Blessing機能

基礎概念と用法

PureBuilder Simplyはドキュメントのメタデータ、つまりFrontmatterを強く活用します。 Pandocテンプレートからも、eRubyテンプレートからもこのFrontmatterを参照することができ、テンプレートが条件分岐やイテレータのような構造を利用できることで、Frontmatterによって出力内容を根本的に変えることができます。 これにより、ひとつのテンプレートにより、異なる形式の出力を得ることもできます。

PureBuilder Simply自体が、自動的に様々なFrontmatterをセットします。 こうして自動的にセットされる要素が、事前にビルドされるものでありながら、動的な要素をドキュメントに組み込むことができます。

「Bless」はこのFrontmatterにパラメータを動的にセットする処理の拡張です。 Frontmatterを動的に拡張することこそがPureBuilder Simplyの強力さの源であり、それを拡張できる「Bless」は極めて強力な機能です。

Blessを使う方法は、Rubyのライブラリとして実装する方法と、コマンドとして実装する方法があります。

Rubyライブラリとして実装するには、ドキュメントソースルートに.pbsimply-bless.rbというRubyスクリプトファイルを置きます。 このファイルはライブラリとしてロードされ、PureBuilder::BLESSというProcオブジェクトがセットされることを期待します。 Bless時にはPureBuilder::BLESS.call(frontmatter, self)の形式で呼び出され、Frontmatterと、PBSimplyクラスの子孫クラスのインスタンス(PureBuilder Simplyの本体オブジェクト)にアクセスできます。 そして、この処理の中でfrontmatterを更新することができます。

コマンドとして実装するには、Bless用のコマンドを用意し、設定ファイルのbless_cmdにコマンドをセットします。 bless_cmdはRubyのKernel.systemに渡される値です。コマンドと引数からなる配列とすることも、単にコマンドライン文字列にすることも可能です。

Blessコマンドからは$PBSIMPLY_WORKING_DIR/pbsimply-frontmatter.jsonというパスでFrontmatterにアクセスできます。 Blessを行うには、このファイルを書き換えます。 PureBuilder SimplyはBlessコマンドを実行後、Frontmatter JSONファイルを再度読み直してFrontmatterを更新します。

ACCS更新時はbless_accscmdに設定されたコマンドが呼ばれます。

用例

このウェブサイトは日本語と英語に対応しており、各ページで言語切り替えができます。 言語ごとのページは単純に/en//ja/という違いによって切り替えられますが、記事本文以外にも言語によって作りが異なっています。

これを実現するために、.pbsimply-bless.rbで次のようにしています。

  if frontmatter["source_path"].include?("ja/")
    frontmatter["lang"] = "ja"
    frontmatter["lang_specific"] = true
    frontmatter["lang_ja"] = true
    frontmatter["pagelink_en"] = frontmatter["page_url"].sub('/ja/', '/en/')
    frontmatter["pagelink_ja"] = frontmatter["page_url"]
  elsif frontmatter["source_path"].include?("en/")
    frontmatter["lang"] = "en"
    frontmatter["lang_specific"] = true
    frontmatter["lang_en"] = true
    frontmatter["pagelink_en"] = frontmatter["page_url"]
    frontmatter["pagelink_ja"] = frontmatter["page_url"].sub('/en/', '/ja/')
  else
    frontmatter["lang"] = "en"
  end

lang$lang$の形で言語を設定できるように、lang_jalang_en$if(lang_ja)$の形で条件分離に使えるようにセットしています。 lang_specificによって記事本文とルーターを識別できるようにしています。

祝福する

Rubyで祝福する

.pbsimply-bless.rbというRubyスクリプトファイルを使うことでFrontmatterに手を加えることができます。

これを使用したい場合、同ファイルでPureBuilder::BLESSProcオブジェクトを定義してください。 このオブジェクトはPureBuilder::BLESS.call(frontmatter, self)のように呼び出されます。

呼び出されるタイミングはシステムによってセットされる値が全てセットされた後です。

この関数は値を返す必要はなく、引数として渡されたFrontmatter Hashオブジェクトを直接変更する形で利用します。

もしも処理中のディレクトリがACCSドキュメントディレクトリである場合、PureBuilder::BLESSのあとでさらにPureBuilder::ACCS::BLESSも(定義されていれば)呼び出されます。

さらにあなたはPureBuilder::ACCS::DEFINITIONS Hashに対してProc値を追加することができます。

これらはACCSで特定の値を動的にセットするために使われます。

Key 動作
:next 戻り値をfrontmatter["next_article"]にセットする
:prev 戻り値をfrontmatter["prev_article"]にセットする

Rubyでの祝福の例

次に示すのはChienomiのblessing scriptです。

#!/bin/env ruby

load "./.lib/categories.rb"

TOPICPATH = {
  "" => ["TOP", "/"],
  "/articles" => ["Articles", "/#Category"],
  "/override" => ["Override", "/"],
  "/archives" => ["Old Archives", "/articlelist-wp.html"]
}

ARTICLE_CATS.each do |k,v|
  TOPICPATH[["/articles", k].join("/")] = [v, ["", "articles", k, ""].join("/")]
end

PureBuilder::BLESS = ->(frontmatter, pb) {
  content = nil
  filetype = nil
  content = File.read(frontmatter["source_path"])
  filetype = File.extname(frontmatter["_filename"])

  url = frontmatter["page_url"].sub(/^\.?\/?/, "/")
  frontmatter["topicpath"] = []
  url = url.split("/")
  (1 .. url.length).each do |i|
    path = url[0, i].join("/")
    if v = TOPICPATH[path]
      frontmatter["topicpath"].push({"title" => v[0], "url" => v[1]})
    else
      frontmatter["topicpath"].push({"title" => frontmatter["title"]})
      break
    end
  end

  if frontmatter["category"] && url.include?("articles")
    frontmatter["category_spec"] = [ARTICLE_CATS[url[-2]], frontmatter["category"]].join("::")
  end

  if content
    if((filetype == ".md" && content =~ %r:\!\[.*\]\(/img/thumb/:) || (filetype == ".rst" || filetype == ".rest") && content =~ %r!\.\. image:: .*?/img/thumb!)
      frontmatter["lightbox"] = true
    end
  end
}

article_order = nil
rev_article_order_index = {}

PureBuilder::ACCS::BLESS = -> (frontmatter, pb) {
  frontmatter["ACCS"] = true
  unless article_order
    article_order = pb.indexes.to_a.sort_by {|i| i[1]["date"]}
    article_order.each_with_index {|x,i| rev_article_order_index[x[0]] = i }
  end
}

PureBuilder::ACCS::DEFINITIONS[] = ->(frontmatter, pb) {
  index = rev_article_order_index[frontmatter["_filename"]] or next nil
  if article_order[index + 1]
    {"url" => article_order[index + 1][1]["page_url"],
     "title" => article_order[index + 1][1]["title"]}
  end
}

PureBuilder::ACCS::DEFINITIONS[] = ->(frontmatter, pb) {
  index = rev_article_order_index[frontmatter["_filename"]] or next nil
  if index > 0
    {"url" => article_order[index - 1][1]["page_url"],
     "title" => article_order[index - 1][1]["title"]}
  end
}

他の言語、あるいはコマンドで祝福する

設定ファイルのbless_styleの値としてcmdをセットすると、Ruby Procの代わりに外部コマンドによって祝福を行います。

bless_cmdは通常の祝福用、bless_accscmdはACCSの祝福用のコマンドを設定します。

いずれの場合も環境変数$PBSIMPLY_WORKING_DIRのディレクトリにあるpbsimply-frontmatter.jsonファイルからドキュメントメタデータを読み取ることができ、同ファイルを書き換えることで変更を反映することができます。

自動的な祝福

いくつかの設定は自動的に予め用意されたメソッドで祝福されます。

これらは設定ファイルにおいて値を設定しておくことで、Blessingフェーズで自動的に処理されます。

ACCSの前後関係

blessmethod_accs_relnext_articleprev_articleを設定します。 これらはurltitleからなる連想配列です。

numbering (ファイル名先頭の数値), lexical (ファイル名辞書順), date, timestampが用意されています。