7wiki

ちょっと豆知識的なページをつくりたいけど、
一つ一つをHTMLでページを作るのがだるく感じたため、
どうにかできんもんかなーと思ってたら、Wikiと言うものがありまして。
Wikiってのは、最低限なアプリで、誰でも参照でき、誰でも変更することができ、
記事との関連をつけやすい、などなどの特徴があります。
簡単に言えば、今回のようなちょっとした事なら比較的やりやすいわけです。


んで、今日はフリーで使えて便利なwikiがないかな、と調べてたんですが、
やはり、個々の要求に完璧にこたえた便利なものはなかなかないわけで。


しかし、そんな中で7wikiというものに出会いました。
特に素晴らしいというわけでもないのですが、あることがすごかったです。



7wiki
http://cm.xrea.cc/prog/7wiki.shtml

これのすごいところは、
1行80文字で、たった7行のプログラムで構成されていること。
外部モジュールを使っていますが、それは初期の状態から大抵あるものなので、
導入には全く支障はないです。


また7wikiをおいてあったページのSevenLinesさんには、
いろいろなものが7行で作られています。



さて、7wikiは560文字以内で構成されているわけですが、
もちろん、ぱっと見ただけでは何の処理をしているかは、なかなかわかりません。


僕はPerlを嗜んでいるので興味本位で、この流れを読み解きたいと思います。
基本的に、1文1処理として読みます。実行は読み終わった後にします。

use CGI":all";

CGIモジュールを読み込みます。
このとき、:allをつけることで、メソッドをインポートします。
CGIモジュールの関数を使うときに名前空間を考慮する必要がなくなります。
(例えばnewとすると、CGI::newを呼び出すようになる。)

charset$c="EUC-JP";

少し字数をかけて書くと&charset($c="EUC-JP");と同じ動作です。
短く書こうとするとそう書けるが、非常にわかりにくい。今後こんな感じ。
処理の内容は、変数$cにその値を代入すると同時に、その値を、
CGIモジュールのcharset関数で、文字コードEUC-JPに設定します。

$w='\b(([A-Z][a-z]+){2,})';

変数$wに文字列をエスケープ処理をせずにセットします。
後で正規表現に使われます。

$f=script_name;

変数$fにスクリプトネームを代入します。

$/=();

特殊変数$/(入力レコードを分割する文字列)を空にします。

($p)=path_info=~/$w/;

path_info情報から$wに最初にマッチした文字列を取得します。
$wの文字列が正規表現文として使われます。
正規表現文中の\bは、単語の境界を示します。
単語の境界と言いましたが、単語を表現するうえで使われない文字なら大抵マッチします。
記号や空白など。
"(([A-Z][a-z]+){2,})"と言う部分から、
大文字1文字と小文字が1文字以上で構成された単語が2つ以上連続していた場合、
というマッチング条件だと思います。
おそらく「〜/7wiki.cgi/aaa/WiKi123AbCdEf」とあれば、
$pには「WiKi」が代入されるはずです。
とにかく、対象のページをここで決めます。

$p||=FrontPage;

$pが存在すれば、$pを、存在しない場合は、FrontPageを$pへセットします。
FrontPageは''や""で囲われていないので関数名かと思いましたが無いようなので、
この記述の仕方だと、FrontPageは文字列となるのかな?

request_method=~PO&!($m=param z)&&unlink$p;

request_method がPOST、かつ、クエリzを代入した$mが偽ならば、
その対象のページを削除します。(データがないデータは要らないので)
この$mがwikiのデータの内容となります。

open F,$m?">$p":$p;

$mが真なら、書き込みモードで対象のページを新規に作成(編集)、
$mが偽なら、読み込みモードで対象のページをオープンします。

eval{flock F,2};

ファイルの排他制御(ロック)をします。

print F$m||=<F>;

$mが真なら、$mを書き出し、
$mが偽であれば、$mにファイルの内容を読み込みます。
($/が空なので、1行で読み込む。結果、ファイルの内容がそっくり$mにコピーされる。)

$_=pre(escapeHTML$m).hr.ul map{li"".localtime((stat)[9]),$_}sort{-M$a<=>-M$b}grep/^$w$/&-f,<*>;

grepの部分がわからず、何をしているかあまりわからなかったが、
-Mのファイル演算子や、stat関数を使っているので、
grepでの-fの部分は、ファイルの一覧を取得するものだと解釈。


後は整形とエスケープ処理した$mに境界線を繋げて、
$wでマッチする、同じ階層のファイルを全て引っ張り出し、
sort関数で、更新時間が古い順にソートし、
mapでソートした順に評価し、最終更新時間から日時を求めて、
それをli関数を通したものを、ul属性に追加し、
できたものを$_へ代入する感じ?


おそらく、$_には、
$mの内容に加え、境界線を書き、ファイルのリストを作成したものが代入されるようだ。


aaa
wikiです。
関連記事:bbb,ccc?
――――――
・aaa
・bbb


こんな感じになるのかな。うーん。

s|$w|(!-f$1&&$1).a{href,"$f/$1"},-f _?$1:"?"|eg;

$_の場合は、正規表現を書くときの最初の部分、「$_ =~」の部分が省略できる。
処理はファイルの条件(単語が2つ以上)にマッチしたものをリンク処理する。
↑の「aaa」とか「bbb」にリンクを貼る感じに。
ただ、存在しないものに対してはそのマッチしたものではなく「?」にリンクするっぽい。

put header, start_html(-Title,$p,encoding,$c,lang,ja) ,h1($p), startform(0,"$f/$p"), p(textarea(z,$m,6,60), br, submit), endform, hr, $_, end_html

表示処理。整形した$_の出力も忘れずに。
ほぼ全てはCGIモジュール内のメソッドで構成されている。
textareaにzの名前がついており、これでwikiの編集を行える感じになっている。
並びからして、
タイトル、フォーム、本文、他のデータへのリンクリスト
という感じになるのかな。


実際に設置して実行してみました。


http://rying.net/test/7wiki/7wiki.cgi


やはり、いくつか思ってたことと違う部分や、不足していますね。
他人のコードを読み解く腕はまだまだ、だなぁ。


一方で、非常に斬新だなぁと思える書き方をしている部分もありました。
「$p||=FrontPage;」とすれば、代入できるとか、
特にファイル名を取得するとき、「-f ,<*>」でやってしまうとかすげえ!って思った。


このwikiの欠点は、
・誰でも編集できちゃう。
・バックアップもうんこもない。
・読むだけならフォームの表示は邪魔だよねー。

などなど。
でも7行で、リンク貼りなどを含めwikiの基本的な部分を実装しているのはすげえとは思う。
これみるまでの俺なら同等なことをするのに300行とか書きそうwww


それにしても、こういう無茶苦茶なコードになると、
はてなダイアリーの色分け機能もなんだか役に立ってないなw