Blosxom
Blosxom の動作の概要
Blosxom やそのプラグインの動作を理解するために、 クロージャを格納するグローバル変数や、 Blosxom 本体から呼び出されるプラグインのサブルーチンなどについて、 簡単にまとめてみます。
順番やまとめ方がちょっとおかしいですが、 とりあえず分かるところから書いていきます。 プラグインでどんなことが可能かを考えるときに、 参考になるようなものをまとめるのが目標です。
注意: コードは見やすいように改行とかインデントとかをいれています。 あと、コードの行番号とかは書いていません。 順番が前後することもありますので、 エディタの検索機能を使うなどしてソースコード内の当該個所を探してください。
動作の流れ
Blosxom 全体としては、 大雑把にいうと下記のような流れでページが作られています。
- entries: データディレクトリ以下のファイルを集める
- filter: 集めたファイルをふるいわける (デフォルトでは何もしない)
- head: HTML ドキュメントのヘッダ部を作る
- sort: ファイルをソートする(デフォルトでは日付順)
- 上で得た、各ファイルに対して順番に以下の処理をする
- date: 日付部分をつくる
- 先のエントリーと date の内容が同じだった場合は、 その内容を捨てる
- story: 記事本体部分を作る
- foot: HTML ドキュメントのフッタ部を作る
- last: HTML ドキュメントの内容がすべてそろったあとの処理 (デフォルトでは何もしない)
- end: プロセスの最後の処理 (デフォルトでは何もしない)
また、上記で「○○を作る」とか書いてある部分は、 具体的には下記のような動作をします。
- template: 該当するフレーバーを読み込む
- interpolate: フレーバー内のテンプレート変数を、 Blosxom 本体および各プラグインで定義されている変数の値に置き換える
- できた文字列を出力に付け加える
さらに、上記の entries とか filter とかは手続き(あるいはクロージャ)の名前で、 これらはプラグインによって動作を変更または追加することができます。
ほとんどの手続きがプラグインで変更できるので、 プラグインの作り方次第では、 動作を大幅に変えることもできます。
グローバル変数
グローバル変数は、 大きく分けて、 ウェブログ全体に関する変数と、 ストーリーごとに定義される変数の 2 種類があります。
ソースコード内ではウェブログ全体に関する変数の方が先に宣言されていますが、 これらは数が多い上にややこしいのも含まれるので、 先にストーリー固有の変数のまとめを書きます。
ストーリー固有の変数
プラグイン内で story というサブルーチンが定義されていた場合、 そのサブルーチン内から、 現在処理しているストーリーに固有の変数を参照できます。 このストーリー固有の変数には下記のものがあります:
use vars qw/ $path $fn /;
use vars qw/ $dw $mo $mo_num $da $ti $yr $hr $min $hr12 $ampm /;
use vars qw/ $title $body $raw /;
意味は大体わかると思いますが、 $path は $path_info とは別物なので注意してください。 (実際のソースコード内では、 これらの 3 行はばらばらに現れます。)
ウェブログ全体に関する変数
use vars qw! $version $blog_title $blog_description $blog_language $datadir $url
%template $template $depth $num_entries $file_extension
$default_flavour $static_or_dynamic $plugin_dir $plugin_state_dir
@plugins %plugins $static_dir $static_password @static_flavours
$static_entries $path_info $path_info_yr $path_info_mo
$path_info_da $path_info_mo_num $flavour $static_or_dynamic
%month2num @num2month $interpolate $entries $output $header
$show_future_entries !;
use vars で宣言されているグローバル変数は、これだけあります。 これらのグローバル変数はプラグインのなかからも参照や変更ができます。
グローバル変数には、 ブログのタイトルや概要、 リクエストされたパス、 ストーリー(ウェブログのエントリー)のタイトル、日付などが格納されています。 参照のタイミングによってはほしい値が得られない場合もあります。
プラグインとその中で定義するサブルーチンについては、 もうちょっと後のほうで述べます。
@plugins
@plugins はプラグインのパッケージ名の配列です。 この配列に格納された順番に、 各プラグインのサブルーチンが呼ばれます。 下記のように読み込まれます。
# Plugins: Start
if ( $plugin_dir and opendir PLUGINS, $plugin_dir ) {
foreach my $plugin ( grep { /^\w+$/ && -f "$plugin_dir/$_" }
sort readdir(PLUGINS) ) {
my($plugin_name, $off) = $plugin =~ /^\d*(\w+?)(_?)$/;
my $on_off = $off eq '_' ? -1 : 1;
require "$plugin_dir/$plugin";
$plugin_name->start()
and ( $plugins{$plugin_name} = $on_off )
and push @plugins, $plugin_name;
}
closedir PLUGINS;
}
このコードをみて分かるように、 各プラグインはファイル名でソートされて、 順に start というサブルーチンが呼ばれます。 start が真を返すと、 そのプラグインが @plugins に追加され、 偽を返せばそれ以降そのプラグインのことは忘れられます。
クロージャ
プラグインで上書き定義(正しい用語じゃないけど) できるクロージャには下記のものがあります:
$tepmlate $entries $interpolate $sort
それぞれの変数は、 プラグイン中で同名のサブルーチンが定義されていれば、 そのサブルーチンが返すクロージャが代入されます。
Blosxom では、 主要な動作をこのようにクロージャとして記述することで、 プラグインによる動作の変更を容易にしています。
template
$template は、 flavour ファイルから必要な文を読み取ってくるサブルーチンのリファレンス(クロージャ)です。 (%template とはまったく別物なので注意すること。 ややこしいけど。)
# Define standard template subroutine, plugin-overridable at Plugins: Template
$template =
sub {
my ($path, $chunk, $flavour) = @_;
do {
return join '', <$fh>
if $fh->open("< $datadir/$path/$chunk.$flavour");
} while ($path =~ s/(\/*[^\/]*)$// and $1);
return join '', ($template{$flavour}{$chunk}
|| $template{error}{$chunk}
|| '');
};
で、この変数は、プラグインによって上書きすることができます。 それをやっているのが以下の部分です。
# Plugins: Template
# Allow for the first encountered plugin::template subroutine to override the
# default built-in template subroutine
my $tmp;
foreach my $plugin ( @plugins ) {
$plugins{$plugin} > 0
and $plugin->can('template')
and defined($tmp = $plugin->template())
and $template = $tmp
and last;
}
プラグインを順番に見ていって、 その中で template という名前のサブルーチンを定義しているものがあれば、 それを実行して得られた値を $template に代入しています。
entries
$entries は、 データファイル全体の集合を求めるサブルーチンのリファレンスです。
デフォルトでは、 データディレクトリを順番にみていって、 全ファイルの日付順リストをつくるものみたいです。 (長いしよく分からないので、コードの引用は割愛します。)
このサブルーチンは、 (\%files, \%indexes, \%others) という 3 つのハッシュリファレンスを返すようにつくります。
このうち最低限必要なのは %files です。 これは、ファイル名をキーにしたファイルの更新時刻のハッシュです。
$entries も $template と同様にプラグインで上書きできます。 下記の $interpolate と $sort も同様です。
interpolate
interpolate は、 デフォルトではテンプレートに書かれた $xxx という変数名を、 その値に置き換える動作をします。
# Define default interpolation subroutine
$interpolate =
sub {
package blosxom;
my $template = shift;
$template =~
s/(\$\w+(?:::)?\w*)/"defined $1 ? $1 : ''"/gee;
return $template;
};
sort
$sort は表示されるエントリーの順番を決めるサブルーチンのリファレンスです。 デフォルトでは下記のように定義されています。
# Define a default sort subroutine
my $sort = sub {
my($files_ref) = @_;
return sort { $files_ref->{$b} <=> $files_ref->{$a} } keys %$files_ref;
};
本体から呼ばれるプラグインのサブルーチン
filter
filter は、下記のように呼ばれて、 %files と %others の内容を変更することが出来ます。
# Plugins: Filter
foreach my $plugin ( @plugins ) {
$plugins{$plugin} > 0
and $plugin->can('filter')
and $entries = $plugin->filter(\%files, \%others)
}
end
end はすべての出力が終わった後で呼び出されるサブルーチンです。 後片付けが必要なプラグインで使うためにあるんだと思います。
head
head.{flavour} を元にしてできるページの最初の部分を処理するサブルーチン。
story
story は、 ページを作る際、 各エントリーごとに呼び出されるサブルーチンです。 下記のように、 プラグインごとに、 パス、ファイル名、テンプレート、タイトル、本文を引数にして呼び出されます。
# Plugins: Story
foreach my $plugin ( @plugins ) {
$plugins{$plugin} > 0
and $plugin->can('story')
and $entries = $plugin->story($path, $fn, \$story, \$title, \$body)
}