Sep 26, 2005

MT 3.2 + Tagwire Pluginでタグ入力をオートコンプリートする

このエントリーではMT 3.2に追加されたJavascriptライブラリのひとつ、tc/autocomplete.jsを使って、Tagwire Pluginのタグの入力をアシストしてみます。ここで説明するのは、drry @->Weblog - Movable Type 3.17-ja and customized interface with application templatesで紹介されているAuto-complete Control(actb.js)を利用するのと同様に、タグの入力をオートコンプリートする方法です。actb.jsの方がtc/autocomplete.jsよりリッチな機能を提供しますが、後者はMT 3.2に標準的に含まれ、また前者より機能が限られているので軽快に動作するというメリットがあります。

Ogawa::Buzz: キーワードを元にエントリのbasenameを設定するの前半、準備段階までの作業が完了しているものとします。

まずはじめに、全タグを格納した配列を生成するJavaScriptファイルを作るためのインデックステンプレートを追加します。「出力ファイル名」は仮にtags.js、「テンプレートの内容」は以下の通り入力して、保存(と再構築)しておきます。

var customarray = [ <MTTags glue=",">'<$MTTag$>'</MTTags> ];
変数名がcustomarrayなのは歴史的事情に因ります。また、Tagwire PluginではなくTags Pluginを使用している場合にはそれにしたがってしかるべくテンプレートを設定しておけば、以下の設定を流用することができます。

次にexttmpl/cms/keywords_ac.tmplというファイルを作り、内容を以下のようにします。赤字で示されているのは、keywords_noac.tmplから追加された部分です。

<script type="text/javascript" src="<TMPL_VAR NAME=STATIC_URI>js/tc/autocomplete.js"></script>
<script type="text/javascript" src="<TMPL_VAR NAME=BLOG_URL ESCAPE=HTML>tags.js"></script>
<script type="text/javascript">
var keywordsAutoComplete;
function keywordsInitHandler() {
    getByID('keywords').parentNode.innerHTML += '<div id="keywords_completion"></div>';
    keywordsAutoComplete = new TC.AutoComplete('keywords', customarray);
}
TC.attachLoadEvent(keywordsInitHandler);
</script>
<style type="text/css">
#keywords_completion { position: absolute; z-index: 1; background: #FFF; border: 1px solid; padding: 2px; }
.complete-none { }
.complete-highlight { font-weight: bold; }
</style>
<div class="field">
<label for="keywords"><MT_TRANS phrase="Keywords"></label> <a href="#" onclick="return openManual('entries', 'keywords')" class="help">?</a><br />
<input class="full-width" name="keywords" id="keywords" tabindex="6" value="<TMPL_VAR NAME=KEYWORDS ESCAPE=HTML>" maxlength="255" autocomplete="off" />
</div>

うまくいけば(?)、冒頭の画面のように「キーワード」入力欄で登録済みのタグによるオートコンプリート機能が使えるようになります。

このHackで重要なポイントは、Ogawa::Buzz: キーワードを元にエントリのbasenameを設定すると同様、Javascriptによるわずかなコード(とスタイルシート)追加だけでオートコンプリートが実現できる、という点です。したがって、例えば両方の拡張を合成するのも容易に実現できます。

このあたりのテンプレートをまとめて固めたものを置いておきますので、興味のあるかたはさらに道を究めてみてはいかがでしょう。

keywords_tmpl.zip

SVN::Web on FastCGI

半年くらい前からMovable TypeのPluginなどのsubversionリポジトリーを公開していたのだが、どうにも具合が良くなく、試行錯誤が続いていた。

Ogawa::Buzz: SVN::Web導入

最初PerlベースのSVN::WebをCGI経由で動かしていたのだが、猛烈にパフォーマンスが悪く、私の借りているサーバーだと数秒待たされたりしていた。代わりにmod_perl環境にdeployしたりもしてみたのだが、Apache 1.3との組み合わせだとReponse Headerが無茶苦茶になってしまう。デバッグしてもよいのだが、mod_perlにそんな思い入れもなく面倒でもあったのでCGIに戻した。ああでもやっぱり我慢できん!! ということで、PHPベースのWebSVNに移行し、イマイチ感に苛まれながらもここしばらく使ってきた。

さて最近になってFastCGIが私の中でプチブームになってきている。今朝、「そう言えばSVN::WebもFastCGIで動かせるかも」とようやく思い至った。調べてみると、これがバッチリ、元から対応していた。気が付くのが遅すぎだった罠。

MT関係のリポジトリーのURL
http://svn.as-is.net/svnweb/mt
MT関係のリポジトリーの更新情報のRSS URL (FeedBurner)
http://feeds.feedburner.com/ogawa-svn-mt

個人的な覚え書き

設定もろもろ

  1. まず、svnweb-installを実行し、index.cgiをsvnwebにリネームしておく。
  2. config.yamlを編集する。
  3. httpd.confに以下のように追加。
    <VirtualHost *:80>
        ServerName svn.example.com
        ServerAdmin root@example.com
        DocumentRoot /svn_docroot
        <Directory "/svn_docroot">
            Options ExecCGI
            AllowOverride None
            Order allow,deny
            Allow from all
        </Directory>
        <Files "config.yaml">
            deny from all
        </Files>
        <Location /svnweb>
            SetHandler fastcgi-script
        </Location>
    </VirtualHost>
    
  4. apachectl restartしておく。

特定の拡張子(例:fcgi)の付いたURL(http://svn.example.com/index.fcgi/mtなど)で構わなければ、上記のLocationの設定は不要。

真っ当にSVN::WebをFastCGIで動かすためのパッチ

この話はSVN::Web 0.38用。最近のバージョンではOgawa::Buzz: SVN::Web 0.43程度の対策をしておけば大丈夫のようだ。

subversionもバージョンが上がってきてSVN::Coreの仕様が微妙に変わってきたのか、SVN::Webも多少の修正は必要。また、FastCGIで使うことを考えると簡単にdieられないようにする必要がある。

--- SVN/Web/Revision.pm.bak Fri Aug 27 02:26:17 2004
+++ SVN/Web/Revision.pm Tue Sep 27 20:23:43 2005
@@ -35,7 +35,7 @@
     my $pool = SVN::Pool->new_default_sub;
     my $rev = $self->{cgi}->param('rev') || die 'no revision';
 
-    $self->{repos}->get_logs ([], $rev, $rev, 1, 0,
+    $self->{repos}->get_logs ([ $self->{path} ], $rev, $rev, 1, 0,
          sub { $self->{REV} = $self->_log(@_)});
     return {template => 'revision',
      data => { rev => $rev, %{$self->{REV}}}};
--- SVN/Web.pm.bak Wed Nov  3 17:15:52 2004
+++ SVN/Web.pm Wed Sep 28 01:30:40 2005
@@ -251,6 +251,7 @@
  $action ||= 'browse';
  $path ||= '';
 
+ eval {
  run ({ repos => $repos,
         action => $action,
         path => '/'.$path,
@@ -258,6 +259,11 @@
                output_sub => \&cgi_output,
         style => $config->{style},
         cgi => $cgi});
+ };
+ if ($@) {
+     print "Content-Type: text/html\n\n";
+     print "Got an error: $@";
+ }
  last if $cgi_class eq 'CGI';
     }
 }

Sep 24, 2005

キーワードを元にエントリのbasenameを設定する

MT 3.2をざらっと見ていて気がついたのが、mt-static/js/の下に格納されているJavascriptのライブラリ群。MTではあまり使われていないみたいなので本来TypePad用に作られたもののような気がしなくもないですが、妙に便利そうなツールが揃っているみたいです。ドキュメントもないのでよく分かりませんけれども。

このエントリーでは、このライブラリを利用して、エントリー編集画面において、「タイトル」ではなく、「キーワード」を元に「ベースのファイル名」を設定できるようにするHackを考えてみます。

ここで述べている方法は、個別エントリーアーカイブのファイル名を「<$MTEntryKeywords dirify="1"$>.html」のように設定するのとは根本的に異なり、エントリーが持つbasenameフィールドをキーワードから生成するというものです。この方法で生成されたbasenameはブログ内でUniqueであることが保証されます。またエントリーを再編集してキーワードを変更してもbasenameには反映されないので注意してください。

まず、準備作業から始めます。MTDIRにexttmplというディレクトリ、その中にexttmpl/cmsというディレクトリを作り、tmpl/cms/edit_entry.tmplをexttmpl/cmsにコピーしておきます。

次にexttmpl/cms/edit_entry.tmplに以下のパッチを当てておきます。このパッチはエントリー編集画面の「キーワード」入力欄を、別のテンプレート(keywords_noac.tmpl)を使って生成するように変更するものです。

--- tmpl/cms/edit_entry.tmpl	Sat Sep 10 11:14:14 2005
+++ exttmpl/cms/edit_entry.tmpl	Sat Sep 24 01:26:30 2005
@@ -393,10 +393,7 @@
 </TMPL_IF>
 
 <TMPL_IF NAME=DISP_PREFS_SHOW_KEYWORDS>
-<div class="field">
-<label for="keywords"><MT_TRANS phrase="Keywords"></label> <a href="#" onclick="return openManual('entries', 'keywords')" class="help">?</a><br />
-<textarea class="full-width" name="keywords" id="keywords" tabindex="6" cols="72" rows="2"><TMPL_VAR NAME=KEYWORDS ESCAPE=HTML></textarea>
-</div>
+<TMPL_INCLUDE NAME="keywords_noac.tmpl">
 </TMPL_IF>
 
 </div>

exttmpl/cms/keywords_noac.tmplというファイルを作り、内容を以下のようにします。

<div class="field">
<label for="keywords"><MT_TRANS phrase="Keywords"></label> <a href="#" onclick="return openManual('entries', 'keywords')" class="help">?</a><br />
<input class="full-width" name="keywords" id="keywords" tabindex="6" value="<TMPL_VAR NAME=KEYWORDS ESCAPE=HTML>" maxlength="255" autocomplete="off" />
</div>

ここまでの作業ができたら、mt-config.cgiに以下の行を加えます。

AltTemplatePath ./exttmpl

これでエントリー編集画面がexttmpl/cms/edit_entry.tmplを使って生成されるようになります。上記の通りの作業を行うと、「キーワード」入力欄がtextareaではなく、inputフィールドとして生成されるはずです。

さて、ここまでが準備段階。本題を始めましょう。

exttmpl/cms/keywords_noac_base.tmplというファイルを作り、内容を以下のようにします。赤字で示されているのは、keywords_noac.tmplから追加された部分です。

<script type="text/javascript">
function basenameHandler(evt) {
    if (!orig_basename) {
	var el = evt.target || evt.srcElement;
        var dirified = dirify(el.value);
        dirified = dirified.substring(0, <TMPL_VAR NAME=BASENAME_LIMIT>);
        var trimmed = dirified.match(/^(.*[^_])/);
        if (trimmed)
            setElementValue('basename', trimmed[0]);
        else
            setElementValue('basename', '');
    }
    return true;
}
function keywordsInitHandler() {
    TC.attachEvent('keywords', 'change', basenameHandler);
    TC.attachEvent('keywords', 'keyup', basenameHandler);
    getByID('title').onchange = function() {};
}
TC.attachLoadEvent(keywordsInitHandler);
</script>
<div class="field">
<label for="keywords"><MT_TRANS phrase="Keywords"></label> <a href="#" onclick="return openManual('entries', 'keywords')" class="help">?</a><br />
<input class="full-width" name="keywords" id="keywords" tabindex="6" value="<TMPL_VAR NAME=KEYWORDS ESCAPE=HTML>" maxlength="255" autocomplete="off" />
</div>

次にこのテンプレートを読み込むようにexttmpl/cms/edit_entry.tmplを変更しましょう。

<TMPL_INCLUDE NAME="keywords_noac.tmpl">
<TMPL_INCLUDE NAME="keywords_noac_base.tmpl">

これで、「キーワード」欄に応じて「ベースのファイル名」が変更されるようになり、また「タイトル」を変更しても「ベースのファイル名」は変更されないようになります。

最後に、keywords_noac_base.tmplに追加したJavascriptコード部分について説明しておきます。

basenameHandlerという関数は、イベントハンドラであり、イベントの発生した要素の値(input要素ならその入力内容)を元にベースネームを生成し、「ベースのファイル名」の値を設定します。

keywordsInitHandlerという関数は、初期化時に呼び出されるハンドラであり、"keywords"というidを持つ要素のonchangeイベント、onkeyupイベントに上で述べたbasenameHandlerイベントハンドラを登録しています。また、"title"というidを持つ要素のonchangeイベントハンドラを空にしています。

最後のTC.attachLoadEvent(keywordsInitHandler);という部分は、ドキュメントのロード後呼び出される初期化ハンドラとして、上で述べたkeywordsInitHandlerを登録しています。

このようにMT 3.2では、簡単にテンプレートに機能を追加・変更することができるようになっています。特にテンプレートに記述されるHTML部分(表現)とJavascript部分(意味)が直交している点が重要です。上の例では、Javascriptがなくても機能しますし、あればbasenameを設定するように機能させることもできますし、他のことを記述すればもっと異なる意味を持たせることもできるわけです。

ちなみにMT 3.2に付属しているJavascriptライブラリを利用すると、キーワードの入力をオートコンプリートする機能の追加も容易に行えます。こちらの方法に関しては別エントリーで説明する予定です。

Sep 20, 2005

早川

えきねっと(JR東日本)|旅どきnet>JR東日本 テレビCMギャラリーにインスパイアされたかどうかは別にして、日曜日快速アクティーで渋谷・品川経由で早川(と真鶴)に行ってきました。大宮でショートケーキをゲットし、岸壁でショートケーキを全部落とし、かつカニに挟まれる、というイベントはこなしていません。


早川駅ホーム。携帯のバッテリが切れ、ろくに写真を撮れませんでした。

早川ではわらべ菜魚洞で食事をしました。法事の団体客がいてえらく(30分くらい?)待たされましたが、リーズナブルだし、シマアジも鯛もあら汁もおいしかったです。

わらべ菜魚洞トップページ
Google マップ - 小田原市早川1-5-4

早川は食事か釣りしか選択肢のないところなので、食事をしたら早々に真鶴の親戚宅へ。

Sep 17, 2005

ヨル、ヨドバシアキバニ、イッテミタヨ。

ヨドバシAkibaがオープンしていましたので、夜になってから行ってみました。


夜8時過ぎなのにみるみる人が吸い込まれていきます。

馬鹿がつく広さでした。広いんですが、もう朝からずっと人大杉だし、アキバだけに雑巾臭い汗臭い人も多いので、息がつまります。地下が駐車場になっていますが、例えばこの賑わいの最中、地下で発煙筒を焚くとどういうことになるのでしょうか。直感的にはすっごく怖い状況が予想されるのですけれど…。

個人的にはTower Recordsと有隣堂が入っている7F以外は用はないです。そもそも私がアキバで利用するのは中古ソフト屋と中古CD屋だけですから。来週は三茶キャロットタワーにTSUTAYA書店ができるし、私の活動域の文化度が少しずつ上昇しているのはよいことです。

レストラン街は思っていたよりまともでした(空港のレストラン街クラスを想定していました)。全体にパッケージで導入された店舗が多く、メニューはコテコテしています。というか、そういうのしかないです。ご家族連れやアキバヘビーユーザーのニーズは私にはよく分からないですが、そうしたものを食べたいのなら肉の万世あたりに行った方がイベント性があると思います。ひとつの店舗ですべて済ませられるというニーズが結構あるのですね、多分。

他のアキバの量販店はどうなってしまうのでしょう…。どんどん縮小して、跡地に今は雑居ビルの2,3階に入っているようなメイド喫茶あたりが路面店をどんどん出すようになるのかもしれませんね。そうするとフロア単価が上がり、客単価を上げる必要性が出てきて、いよいよ風俗産業を指向するようになり、みかじめ料を求める団体構成員が増え、同様にパチンコ屋とか増えるでしょう。そうすると日曜・祝日に歩行者天国にする必要もなくなり、渋滞も少し解消して…まあどうでもいいか。

Sep 16, 2005

MT-XSearchをFastCGI化する

誰もついてきていないみたいな気もするのですが、構わず(いつか誰かの役に立つに違いないと信じて)今度はMT-XSearchをFastCGI化することを考えてみます。

最初にmt-xsearch.cgiをMT::Appに準拠したアプリケーションとして再構成します。実はよく使うMTアプリをとりあえずMT::Appに準拠させておくことは、それ自体意義があります。

と言うのも、MT 3.2では、MT::Appのサブクラスを定義するだけで簡単にMTアプリケーションが構築できるようになっているからです(MTは元来拡張性を考慮して作られていますが、3.2ではそれが一層容易になっています)。このようにして構築されたMTアプリケーションは、mt.cgiの中身を見れば分かるように、ごく簡単なブートストラップスクリプトだけでCGIプログラムとして動作させることができます。それだけではなく、先のエントリーに書いたようにFastCGI化することはもちろん、mod_perl化することも可能です。また、エラーハンドリングやクッキー、セッション管理などいざ一から作ることになったら厄介な作業を代行してくれるsubroutine群も利用できます。つまりは、可用性と保守性の点から望ましいというわけです。

まず、以下のスクリプトをコピペしてextlib/MT/App/XSearch.pmとして保存してください。

package MT::App::XSearch;

use strict;
use MT::App;
@MT::App::XSearch::ISA = qw( MT::App );
use MT::ConfigMgr;
use MT::XSearch;

sub init {
    my $app = shift;
    $app->SUPER::init(@_) or return;
    $app->add_methods(main => \&view);
    $app->{charset} = $app->{cfg}->PublishCharset;
    $app->{default_mode} = 'main';
    $app;
}

sub view {
    my $app = shift;
    my $q = $app->{query};
    require MT::Template;
    require MT::Template::Context;
    my $blog_id = $q->param('blog_id') or
        return $app->error("Missing parameter blog_id");
    my $key = $q->param('search_key') or
        return $app->error("Missing parameter key");
    my $search = MT::XSearch->execute($q);
    my $tmpl = MT::Template->load({ blog_id => $blog_id, 
                                    name => 'XSearch ' . $key,
                                    type => 'custom' });
    my $ctx = MT::Template::Context->new;
    $ctx->stash('MT::XSearch', $search);
    $ctx->stash('CGI', $q);
    my $html = $tmpl->build($ctx)
        or return $app->error("Building search template failed: " . $tmpl->errstr);
    $html;
}

1;

次にオリジナルmt-xsearch.cgiの代わりに以下のブートストラップスクリプトをmt-xsearch.cgiとして保存し、実行パーミッションを設定してみてください。

#!/usr/bin/perl -w

use strict;
use lib 'lib';
use MT::Bootstrap App => 'MT::App::XSearch';

今までのmt-xsearch.cgiと同様に動作するはずです。

これをFastCGI化するのは簡単です。Ogawa::Buzz: MT 3.2 on Apache + FastCGIで述べたdispatch.fcgiに以下のように一行追加し、

my $handlers = {
    'mt.fcgi' => 'MT::App::CMS',
    'mt-comments.fcgi' => 'MT::App::Comments',
    'mt-tb.fcgi' => 'MT::App::Trackback',
    'mt-search.fcgi' => 'MT::App::Search',
    'mt-xsearch.fcgi' => 'MT::App::XSearch',
## uncomment if necessary, but this adds a lot of
## overhead since it loads up LibXML.
##    'mt-atom.fcgi' => 'MT::AtomServer',
};

.htaccessに以下の一行を追加すれば、mt-xsearch.fcgiという名前で、今度はFastCGI化されたMT-XSearchも利用できるようになります。

RewriteRule ^mt-xsearch\.fcgi(.*)$   dispatch.fcgi$1 [L]

ところで上記の方法だと、Ogawa::Buzz: TagwireとMT-XSearchによる動的タグアーカイブなどを実現しようとすると困ったことになります。

ひとつには、

http://www.example.com/blog/tag/MovableType
↓
(snip)/mt/mt-xsearch.fcgi?blog_id=1&search_key=Tagwire&search=MovableType
↓
(snip)/mt/dispatch.fcgi?blog_id=1&search_key=Tagwire&search=MovableType

という二段階のsubstitutionが必要になり、かつURLはrewriteしたくないという要請が発生することです。これはよく検討していませんがApacheのRewriteRuleでは実現できなさそうです。

もうひとつはCGI::Cacheを使ったキャッシングができないということです。

ならば真っ当にmt-xsearch.fcgiを用意してやった方が都合がよいということになります。以下はその例です。

#!/usr/bin/perl -w
use strict;
use lib 'lib';
use MT::Bootstrap;
use CGI::Fast;
use CGI::Cache;
use MT::App::XSearch;

eval {
    CGI::Cache::setup({
        cache_options => { cache_root => './cache', default_expires_in => 600 }
    });
    while (my $q = new CGI::Fast) {
        my $cgi_cache = $q->param('cgi_cache') || 0;
        if ($cgi_cache) {
            CGI::Cache::set_key($q->Vars);
            CGI::Cache::start() or next;
        }
        my $app = MT::App::XSearch->new(CGIObject => $q)
            or die MT::App::XSearch->errstr;
        local $SIG{__WARN__} = sub { $app->trace($_[0]) };
        MT->set_instance($app);
        $app->init_request(CGIObject => $q) unless $app->{init_request};
        $app->run;
        CGI::Cache::stop($app->errstr ? 0 : 1) if $cgi_cache;
    }
};
if ($@) {
    print "Content-Type: text/html\n\n";
    print "Got an error: $@";
}

上記の例ではcgi_cacheというオプションをURLで渡すことによってキャッシュをするかどうかを指定できます。動的タグアーカイブと組み合わせるときには以下のようにRewriteRuleを書けばよいということになります。

RewriteRule ^tag/(.*)$ /mt/mt-xsearch.cgi?blog_id=1&search_key=Tagwire&search=$1&cgi_cache=1 [QSA,L]
2005-09-28追記: mt-xsearch.fcgiに一箇所致命的なミスがありました。17行目に「CGI::Cache::start() or exit;」とか書いてあったのですが、これだとキャッシュにヒットするたびにFastCGIプロセスが終了してしまいます。しかも悪いことにはFastCGIプロセスが終了・再開を頻繁に繰り返すと、mod_fastcgiはbackoff timeを600秒とかに設定してしまい、500 Errorが頻発してしまうわけです。exitではなくnextでしたね。

Sep 14, 2005

Google Blog Search

Google Blog Search (Find blogs on your favorite topics)

チョー有意義!Technoratiに対するdisadvantageは、今のところCoverageの低さとTag Searchくらい。前者は徐々に改善するだろうし、後者はTechnoratiくらいcoverageがあると使い物にならないという問題がある。

Keyword Searchはもちろん、Cosmos Search相当のことは当然できるし、AtomやRSSも利用できてしまうわけだね。

「後発の強み」の実例を絵に描いて額に入れて飾ったようなもの。

Sep 12, 2005

MT 3.2 on Apache + FastCGI

Brad Choate: MT 3.2 and LightTPD/FastCGIを読んでLightTPD/FastCGI化したいなあと思っていましたが、Apacheの負の遺産が大き過ぎて結局LightTPDへの移行は見送りました。

同様に、BP's Weblog: MT 3.2 with FastCGI under Apache)でApache上でFastCGI化しようとしている方がいますが、これで紹介されている方法ではアプリケーションごとに異なるインスタンスが生成されてしまうので効率が良くありません。

そこでこのエントリーでは「ApacheでMT 3.2を(真っ当にシングルインスタンスで)FastCGI化する方法」について説明します(けど、あまりよく分かっていないので私にFastCGIの質問はしないこと!!)。

前提としてApache 1.3.33+mod_fastcgiがインストールされているものとします。Apache2でも同様に設定できると思いますがここでは説明しません。

設定方法

設定方法を6ステップで説明します。

ステップ0: Movable TypeにUnofficial Patchをあてる。

Ogawa::Buzz: MT 3.2日本語版 Unofficial Patchで公開しているUnofficial Patchをあてておきます。このパッチを当てることによって、FastCGI環境などで生じるMTの不具合がまとめて修正されます。

ステップ1: httpd.confを設定する。

httpd.confには以下のように設定して動的にFastCGIアプリケーションが起動できるようにしておきます。

LoadModule fastcgi_module libexec/apache/mod_fastcgi.so
AddModule mod_fastcgi.c

<IfModule mod_fastcgi.c>
    AddHandler fastcgi-script .fcgi
    FastCGIConfig -maxClassProcesses 4 -minProcesses 1
    FastCgiIpcDir /tmp
</IfModule>

ステップ2: dispatch.fcgiを用意する。

以下のスクリプトをdispatch.fcgiという名前でMTDIRに置き、実行パーミッションを設定するのをお忘れなく。

#!/usr/bin/perl -w
use strict;
use lib 'lib';
use MT::Bootstrap;
use CGI::Fast qw(:standard);

my $handlers = {
    'mt.fcgi' => 'MT::App::CMS',
    'mt-comments.fcgi' => 'MT::App::Comments',
    'mt-tb.fcgi' => 'MT::App::Trackback',
    'mt-search.fcgi' => 'MT::App::Search',
## uncomment if necessary, but this adds a lot of
## overhead since it loads up LibXML.
##    'mt-atom.fcgi' => 'MT::AtomServer',
};
eval "use $_" foreach (values %$handlers);

eval {
    while (my $q = new CGI::Fast) {
        my $cgi = $q->url;
        $cgi =~ s!.*/!!;
        my $pkg = $handlers->{$cgi};
        die "Invalid handler for $cgi" unless $pkg;
        my $app = $pkg->new(CGIObject => $q) or die $pkg->errstr;
        local $SIG{__WARN__} = sub { $app->trace($_[0]) };
        MT->set_instance($app);
        $app->init_request(CGIObject => $q) unless $app->{init_request};
        $app->run;
        my $mode = $app->mode || '';
        if ("$pkg->$mode" eq 'MT::App::CMS->plugin_control') {
            exit; # allows server to recycle after changing plugin switches
        }
    }
};
if ($@) {
    print "Content-Type: text/html\n\n";
    print "Got an error: $@";
}

dispatch.fcgiを手で起動してみて不足しているモジュールをCPANなどからインストールしましょう。FreeBSDならportsからp5-FastCGIとfcgi-devkitをインストールしておけば大丈夫だったみたいです。

ちなみにこのスクリプトはlighttpd + FastCGI環境でも問題なく動作するはずです。また、ときどき修正しているので最新版をフォローしたい場合にはogawa - Revision 650: /trunk/fastcgiをウォッチしていてください。

ステップ3: RewriteRuleを設定する。

MTDIRに.htaccessを作り、以下のように設定しておきます。

RewriteEngine on
RewriteRule ^mt\.fcgi(.*)$          dispatch.fcgi$1 [L]
RewriteRule ^mt-comments\.fcgi(.*)$ dispatch.fcgi$1 [L]
RewriteRule ^mt-tb\.fcgi(.*)$       dispatch.fcgi$1 [L]
RewriteRule ^mt-search\.fcgi(.*)$   dispatch.fcgi$1 [L]

ステップ4: mt-config.cgiを変更する。

最後にmt-config.cgiを編集して以下のように設定しておきます。

AdminScript mt.fcgi
CommentScript mt-comments.fcgi
TrackbackScript mt-tb.fcgi
SearchScript mt-search.fcgi

他のXMLRPCScript, ViewScript, AtomScriptなども同様に設定できるのかもしれませんが、私は確認していません。Brad Choateの記事を読む限り、AtomScriptはFastCGI化できるようですね(ただXML::Atomが読み込まれるので常駐領域が巨大になるっぽい)。

ステップ5: 動作確認とパラメタチューニングをする。

これで動くはずです。また、httpd.confのパラメータチューニングもお好みで行うと良いでしょう。詳しいパラメータの意味はApache module mod_fastcgiあたりを参照してください。


2005-09-12追記: 動的にFastCGIアプリケーションを起動するとちょっと動作がおかしいことがあるようです。気がついた範囲ではコメントのNotifyメールを送られなかったり、管理画面が一部文字化けしたりすることがあります。後者は純粋にベータ版のせいのような気もしますが、試しに静的に起動するようにして実験中です。こんな感じで↓

<IfModule mod_fastcgi.c>
#    AddHandler fastcgi-script .fcgi
#    FastCgiConfig -maxClassProcesses 4 -minProcesses 1
    FastCgiIpcDir /tmp
    FastCgiServer <fullpath>/dispatch.fcgi -processes 2
</IfModule>

2005-09-14追記: どうもNotifyメールが送られないのはLaunchBackgroundTasksが1にセットされている場合のようです。このオプションをセットするとクリティカルパスがforkされたプロセスで実行されるようになり、Notifyメールはおそらくそのforked processの中で送っていると想像されます。送信処理でもどうせsendmailを起動するのにforkしているはずだからこのあたりがやばいのかもねーと思って、Mail::Sendmailを使うように変更してみましたがやっぱり送信されません。しょうがないのでLaunchBackgroundTasks 0にしてみています。


2005-09-16追記: ちょっと気になってMT->set_instance($app);という行を追加してみた。管理画面が時折文字化けしたり、FastCGIを動的に起動したときにたまにconfig情報の取得に失敗したりする問題の解決を期待して。


2005-09-22追記: 09-16の修正は結果的に大正解。動的に起動しても非常に安定して動作するようになりました。

また、FastCgiServerで静的に起動するのは逆にerrorousなので止めました。強制的に2プロセス以上静的起動させていると、例えばプラグインの設定変更を行うと、そのリクエストを処理したプロセス自体は再起動して設定が反映されますが、それ以外のプロセスは影響を受けません。つまり、リクエストごとに不整合が生じ得ます。

これを避けるには、(1) リクエストごとにpluginsディレクトリを再帰的に読み込み直す、(2) リクエストごとにプラグインの初期化をし直す、(3) ビジーでないプロセスはとりあえず再起動させる、くらいの解決策があるでしょう。(1) はリクエストごとにナンセンスなコストがかかり、わざわざFastCGIにした意味がありません。(2) は初期化ルーチンを分離してプラグインが記述されている必要があります。MT 3.2的にはプラグインをMT::Pluginのサブクラスとして定義し、init_requestというメソッドを定義して欲しい感じにも見えるのですが、実装が中途で終わっている模様。下位互換性もない方法になります。

というわけで(3)が現状有効っぽいわけです。ループカウンタやタイマで制御することもできますが、ApacheのFastCGIモジュールでは、動的起動するアプリケーションはdemandがなければ残り1個になるまで自動的にkillしてくれるので、結局意図したような動作になってしまうようです。

Sep 11, 2005

Movable Type 3.2日本語版公開ベータテスト

いくつかお知らせとパッチ情報を。

まず、Movable Type 3.2日本語版公開ベータテストに参加します。すでにこのブログもbeta1にアップグレードしました。ただ忙しいのでちゃんとバグ出しに付き合えるかどうかはよく分かりません。

また、3.2日本語版ベータ1のリリースに伴い、Ogawa::Buzz: Quasi-Spam Filter Pluginの保守は終了します。私自身、3.2付属のSpamLookupを使用することにしました。SpamLookupは、個人的にはQuasi-Spam Filterでやろうとしていたフレームワークを明快に実現してくれました。今後はSpamLookupへのadd-onやJunkFilterという形で貢献していくことができればと思います。

最後にパッチ情報。

  • MTArchiveListにlastnオプションを与えると、lastnで指定した件数と実際に表示される件数が一致しない。[静的生成]
    hdlr_archives.patch
  • mt.cgiで検索・置換が機能しない。
    search_replace.patch
  • mt.cgiの検索・置換機能がmod_perl環境・FastCGI環境で正常に動作しない。
    search_replace.patch2(上記修正も含んでいます)

Sep 8, 2005

Gmailを本格的に使ってみる

私はMicrosoft Outlookを愛用している(いた)。Outlookではフォルダ単位やメッセージ単位でファイルが作られない(基本的に一個の巨大なスプールファイルを操作する)ので引越しなどが便利な反面、メッセージ群を別スプールファイルに格納しようとすると必ずコピーが生じるし、メッセージを削除してもGCが即座には行われない。また、普段の使用で徐々に容量が増すためディスクの断片化が進行しやすい。が、まあフェアな目で見れば、スケジュールなどを含むオフィスアプリケーションとの包括的な連携が可能であるなど、現代において最も優れたメールクライアントのひとつであることは確かである。

それはそうなのだが、10数年来のメールがたまりにたまって、今私のOutlookのスプールファイルは8つ、総計10GBのディスクを消費するという悲惨な状態になっている。

思えば、石器時代[mailxコマンドなどの打製=だっせー石器を使用する時代]から始まり、青銅器=Emacs時代[mail-mode, vm, gnus, mh-e, mewなど順不同、他に何があった?]を経て、鉄器=Windows=Naive GUI時代[Becky!, Microsoft Outlook他多数]にいたるまで*1、さまざまな方法でメールを送受してきた。それが積もり積もって10GB、なのである。その都度律儀にコンバートし、(java-house-brewersを筆頭として)取るに足りないMLなどは削除している。

*1 これらをまとめて先史時代と呼ぶ。あなたが使っているのがThunderbirdかWanderlustかPostPetかよく知らないが、それらはすべて先史時代の道具である。例えば、はてなの中の人は「メールメッセージやアドレス情報はあまねく固有のURIを持つべきで、それらの上に定義されるオープンなインタフェースが当然必要である」と考えないのだろうか。今でもある意味そういう実装がなされていると言えなくもないのだが、メールアドレスから個人情報を、Message-Idからメッセージ本文をextractすることはできない。だからローカルコピーを作るソリューションが先史時代において一般的なのであり、どこからでもtransparentなアクセスを実現しようとしてもできないのである。ともあれ「フィードで購読 + SMS/ボイスメッセンジャーで記述」あたりが最初に刻まれる歴史となるのではないかと勝手に予想してみる。

Portable PCを買い換えたりするといつも憂鬱になるのは、この、すでに手に余る引っ越し荷物をどうするのかということだ。普段から荷物を減らすことができない人はいざ引っ越し時に荷物を減らそうとしても大抵徒労に終わって、間違いなく10年は開いたことのない段ボール箱を引っ越し先に持ち運ぶ羽目になる。80Gのディスクを買っても(80平米の部屋を借りても)一割以上の場所をあらかじめ占めてしまうにも関わらず。

そろそろこの循環を断ちたい…

と思うのも無理からぬこととご理解いただけるだろう。ということで、今回の引っ越しに際して試行的にGmailに移行してみた。基本的には、普段のすべてのメール送受信をGmailのWebインタフェースだけで済ませ、たまにOutlookを使ってPOP3経由でバックアップをとるだけである。

とりあえず10日ほどこの環境を使ってみた感想としては、ローカルにフレッシュコピーがないと死んでしまうかもと思っていたのだが、それは単に幻想か鉄器時代に植え付けられた脅迫観念に過ぎなかったということである。考えてみれば青銅器を使っていたころまでみんなそれで平気だったのであり、逆に今はローカルコピーの存在に振り回され過ぎている。また、メッセージの保管や検索にローカルリソースを消費せずに済むのは思いのほか愉快である。特に検索はローカルで行うよりははるかに高速で簡便だ。ローカルコピーが手元にあるとついついフォルダ分けして整理しなくてはという脅迫観念が働くが、オンラインだととりあえず検索して見つかればいいやと思うので整理する必要もない。IMAP+Searchでも同じことができるけれども自分のリソースを一切使わないあたりが精神衛生上もよい(数年前Courier-IMAPを使っていたこともあるのだがメッセージが増えると異常にリソースを消費したり不安定になったりしたのでいまいち信頼できなかった)。

今のところメッセージの蓄積速度は、Gmailスプールの最大容量の増大速度をやや上回る程度である。何かの拍子で一日に数10MB受信することもままあるのでとりあえず一年、いや半年この環境を究めてみようと思う。

Sep 5, 2005

記録的短時間大雨(&copy;NHK)

台風14号の前哨戦(昨日=日曜日)で、世田谷区でも海抜10メートルを超えないエリアにある私のマンション付近も大ピンチ。

なにやら杉並区や中野区で千川や野川が溢れているよ~ん、気をつけた方がいいわよ~ん、という広報車の放送を聞いて、やばいかなーと思って駐車場の防潮堤を立てに行ったわけです。確かに雨足強いわな強過ぎだわなと思い、とりあえず一人で防潮堤を立て始めるや否や、来ました来ました、みるみる水嵩が増えてきましたですよ、道路が冠水ですよ、まずいですよ、というかぐゎ重過ぎて一人では立てられんのですよ。と地団駄を踏んでいるとほどなくマンションの他の住民の皆様も駆けつけてくれ、数人がかりで無事設置できました、すでに水が溜まり始めていたのでかなり力任せで。水圧とは馬鹿にならんものです。冠水したのはほんの一時ですぐに水位は通常に戻りました。

さてこのようにうちのマンションの前の道路は年に一二度冠水します。そしていずれも一瞬で水位が元に戻ります。このパターンはある種の問題を示唆しているようにも思えます。それは水嵩にその時点の雨量と関わりなくピークがあり、かつそのピークが排水溝の許容量を越えるという問題です。そもそもピークがあるのは、一定量の降雨が続く場合、各水路の水位は単調に増加し得るけれども、それを低減せしめる機構(河川や下水が増水すると通常取水側の水門を閉じたり、放水側・貯水池の水門を開けたり)が当然存在することを意味します。したがって問題は、その機構の効果が波及して実際に水位を下げ始めるまでの時間にあるマンションの前の排水溝の水位が許容量を超えてしまうことなのだと言い換えられます。

ということは、しかし、仮に貯水余地を増すようなインフラの整備を行ったとしても(例: KUBOTA GLOBAL INDEX/NATURAL DISASTERS/新しい治水コンセプトとしての「環七線地下河川」構想-1)、その貯水池までの総延長が短くなるエリアはハッピーになるけれども、そうならないエリアあるいはそうなり得ない下流域ではやはり同じように冠水し得るのでは?いや確かに地下貯水池があれば下流域に流れ込む水量は減りますが、それが冠水を防ぐのに十分かどうか見積もれるのかしら?んー?

…とか思いながら通勤する月曜日でした。微妙に足腰が筋肉痛です。

Sep 1, 2005

TBDecoder Plugin

主に英語版Movable Typeで発生するトラックバックの文字化けを解消するプラグインを公開します。

TBDecoder_Plugin - ogawa - Google Code

英語版がリリースされるとついついインストールしたくなってしまう人柱志向の強い人向けのプラグインです。とりあえずお守りのつもりでプラグイン・ディレクトリにコピーしておくだけで、英語版MTがトラックバックメッセージをPublishCharsetに自動変換して保存するようになります。多分。

注意: 日本語版Movable Typeにはインストールしても意味がありません。

About Me

My Photo

つくばで働く研究者

Total Pageviews

Amazon

Copyright 2012 Ogawa::Buzz | Powered by Blogger
Design by Web2feel | Blogger Template by NewBloggerThemes.com