« 2005年11月 | メイン | 2006年01月 »

2005年12月28日

prototype.jsのものすごく簡単な使い方。

はてなダイアリーの方にprototype.jsでHTMLを汚さないロールオーバースクリプトというエントリーを書いたのだが、prototype.jsの入手方法やらすごく簡単な使い方がないようなので、書いてみます。

prototype.jsはJavaScriptのライブラリ(中身はJavaScriptです)で、これを使うとJavaScriptを組むのがかなり楽になるというものです。話題のAjaxのプログラミングも簡単にできます。

ライブラリは、
http://prototype.conio.net/

からダウンロードできますが、TOPページにあるファイルはちょっと古いものなので、Browse the darcs repositoryというリンクを辿り、
http://dev.conio.net/repos/prototype/dist/
からprototype.jsをダウンロードします。右クリックで「リンク先を保存」でOKです

1.4.0がでているので、「Download the latest version」というリンクからファイルをダウンロード&解凍します。prototype.jsは、distディレクトリ下にあります。

prototype.jsを使うには、HTMLからリンクするだけでOKです。scriptタグのsrc属性を使います。headの中などで、

<script type="text/javascript" src="prototype.js"></script>

と書くだけです。
この場合は、HTMLファイルとprototype.jsは同じディレクトリに置くことになります。もちろん違うディレクトリでも大丈夫です。

これで準備が整いました。さっそくすごく簡単なAjaxを作ってみましょう。
「ボタンをクリックしたら、Ajaxで別のファイルの内容を取りにいって、HTMLの一部をそれに置き換える」
というものを作ります。

下のようなHTMLを書きます。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>sample</title>
<script src="prototype.js" type="text/javascript"></script>
</head>
<body>
<p id="blk">ここがターゲット</p>
<input type="button" value="ボタン" onclick="new Ajax.Updater('blk','mess.html',{method: 'get'});"/>
</body>
</html>

これを「test.html」などとして保存します。もう一つ「mess.html」というファイルをつくり、

Ajaaaaaaaaaaaaaaaaaaaaaaaaaaaaax!!

とでも書いておきます。これは読み込まれる方のファイルです。両方のファイルともに漢字コードUTF-8にしておいてください。あと、先ほどのprototype.jsを同じディレクトリにいれます。

これで完成です。test.htmlを開いて、ボタンをクリックしてみるとテキストの内容が変化するハズです。
「mess.html」にimgタグを書けばきちんとその画像もでてきますよ。

Ajaxのほとんどの処理は、ボタンのonclickにつけられた

new Ajax.Updater('blk','mess.html',{method: 'get'});

これがやってます。Ajax.Updaterは「Ajaxで別のURLの内容を取りにいって、指定されたHTMLの部分をそれに置き換える」という機能です。
3つのパラメータを渡します。
1つ目は書き換えるHTMLエレメントのIDです。ここでは「id="blk"」が指定されているpタグの部分になります。
2つ目は内容を取得するURL
3つ目はオプションで、ここではGETメソッドを利用することを指定してます

prototype.jsにはこれ以外にも便利な機能がいくつかあります。詳しくは、
prototype.js v1.3.1 の使い方
はてなの9月29日の技術勉強会
id:naoyaさんのppt資料 prototype.jsと PerlでAjax
などが参考になります。

2005年12月26日

Femoに未明からアクセスができなくなっていました。

FemoとこのBlogに今日(26日)の未明からアクセスができなくなっていました。

原因はDynamic DNSの更新に失敗していたことです。
ユーザ様にはご迷惑をおかけしました。

2005年12月25日

Femoに設定機能がつきました。

メモ&カレンダーWebアプリケーション「Femo」に設定機能がつきました。
femo-pref.jpg

実はいままでFemoには、設定ページがありませんでした。
色もメモの並び方も固定で、僕にとって良いと思われるオレオレ設定でした。
(IDやPWがないので設定の必要性が低いというものありました)

すこし時間がかかってしまいましたが、今日のアップデートで何人かのユーザ様にご意見を頂いていた、1ページのメモ数、メモの並び順、デザインについてカスタマイズができるようになりました。
設定ページでは、

  • 1ページのメモ数
  • メモの並び方
  • Femoを開いたときのページ
  • CSS

が変更できるようになりましたので、ぜひお試しください。

FemoはTagがつけられるメモ帳Webアプリケーションです。TypeKeyのアカウントを取得すれば簡単に使うことができます。ぜひお試しください。ご意見もくださいませ。

2005年12月22日

HTML::Template 2.8 Release でHTML::Template::Plugin::Dotを再テスト

HTML::Templateのバージョン2.8がリリースされました
いくつか変更点はあるのですが、

- Bug Fix: Changed HTML::Template to help sub-classes by called _new_from_loop() via ref($self) rather than hard-coding the package name. [Mark Stosberg]

とある通り、HTML::Templateとハードコーディングされているところがなくなって、サブクラスが作りやすくなってます。んで、このpatchを出していたMark Stosbergさんが作っているHTML::Template::Plugin::Dotが正しく使えるようになってます。

以前試した時は、HTML::Templateにpatchをあてるか、メソッドを上書き必要がありました。

サンプルはあまり面白くないのでソースだけで、
CGI::Appベースのアプリケーションで

sub dispatch_index{
    my $self = shift;
    $self->stash->{test}="sample data";
    $self->stash->{tags}=[
        {tag=>'foo',fsize=>10},
        {tag=>'bar',fsize=>18},
        {tag=>'baz',fsize=>14}
    ];
    my $tmpl=$self->tmpl_plugindot_load(
        'index.tmpl.html',
        die_on_bad_params=>0,
        loop_context_vars=>1,
        associate=>$self->query
    );
    $tmpl->param('c',$self);
    return $tmpl->output;
}

こんなようなrun_modeを作成。
stashはCGI::Application::Plugin::Stashを利用してます。
tmpl_plugindot_loadはCGI::Application::Plugin::HTDotが使いにくいので適当にでっちあげたプラグインのメソッド。

テンプレートは、

<h2><!-- TMPL_VAR NAME=c.stash.test ESCAPE=HTML --></h2>
<!-- TMPL_LOOP NAME=c.stash.tags:tag --><!-- TMPL_IF NAME=__FIRST__ --><ul>
<!-- /TMPL_IF --><li style="font-size:<!-- TMPL_VAR NAME=tag.fsize ESCAPE=HTML -->pt;"><!-- TMPL_VAR NAME=tag.tag ESCAPE=HTML --></li>
<!-- TMPL_IF NAME=__LAST__ --></ul><!-- /TMPL_IF --><!-- /TMPL_LOOP -->


こんな感じで、実行すると。

<h2>sample data</h2>
<ul>
<li style="font-size:10pt;">foo</li>
<li style="font-size:18pt;">bar</li>
<li style="font-size:14pt;">baz</li>
</ul>


こう想定通りなりました。

うまく動いたけど、 これを使う機会はなかなかないだろうなぁ。。。

2005年12月21日

TechnoratiのTag検索ページをみて思った。

TechnoratiのTag検索ページをみて思った。
http://www.technorati.jp/tags.html

これが一般なんだなぁと。

ちなみにはてなブックーマークは
http://b.hatena.ne.jp/t
del.icio.usは、
http://del.icio.us/tag/

TechnoratiのTagはAnchorに「rel="tag"」で入れる事ができるらしいけど、そんなことする人なかなか居ないわなぁ、と思ったらカテゴリーをタグとしても使うらしい。

スクリーンショットがあったほうが分かりやすいなと思ったのであとで入れる。

SebastianがMochikitをやらないか、と言ってる。

先日id:secondlifeさんが紹介していたMochiKitをやらないかとSebastianがCatalystのMLで言ってる。

MochiKitの良いところをいくつかあげているのだが、

There is a better alternative to prototype at http://mochikit.com
* Event based like our POE and Python Twisted
* Cool developer features like a terminal, a dumper (like
Data::Dumper) and logging (http://mochikit.com/screencasts/
MochiKit_Intro-1.mov)
* First class JSON support
* Very good test coverage
* Very good documentation
* JSAN support
* Framework agnostic and very open development (it's already in use
by http://turbogears.com)
* No script.aculo.us :(


Perlが中心な感じが笑える :(
んで、script.aculo.usのようなeffectライブラリをつくらないか。と書いていました。

このメールの最初の部分はprototype.jsの嫌なところを書いてたりしているのですが、

* No JSAN integration
* RoR oriented development

などなど、こっちもPerl中心(w


MochiKitも微妙に興味が湧いたけど、どうなのでしょう。

2005年12月20日

HTTP::Server::Simple::CGIの使い方

HTTP::Server::Simple::CGIのhandle_requestに渡される二つ目のパラメーターは、CGI.pmのオブジェクトです。ソースコード中の

sub handler {
    my $self = shift;
    my $cgi  = new CGI();
    eval { $self->handle_request($cgi) };
    if ($@) {
        my $error = $@;
        warn $error;
    }
}

で作成されてます。なので、handle_requestでは、

sub handle_request {
    my ($self,$q)=@_;
    $q->param('hoge');
    $q->header;
}

などとすることができますです。
ちなみに環境変数もセットしてくれるので、$ENV{'PATH_INFO'}なども取得可能です。便利。
CGI::Appなアプリケーションを動かす時は、CGI::Application用(デバッグ)サーバで書いた事とほぼ同じですが、

use base qw(HTTP::Server::Simple::CGI);
use HTTP::Status;
sub handle_request {
    my ($self,$q)=@_;
    local $ENV{CGI_APP_RETURN_ONLY} = 1;
    my $engine = MyApp->new(QUERY=>$q);
        my $ret = $engine->run();
    my $headers = $engine->_send_headers();
    my $message=200;
    foreach (split /\n/,$headers){
        if($_ =~ /^Status:\s+(\d+)/){
            $message=$1;
        }
    }
    print "HTTP/1.0 " . $message . " "  . status_message($message) . "\015\012";
    print $ret;
}

でいけると思う。

Dispatchの方は、dispatchメソッドをコピーしてくるのが早いかなぁ。

2005年12月19日

Folksonomy + Taxsonomy

たつをさんのエントリー
分類→キーワード→分類
がはてなブックマークで話題になっている様ですが、
Goodpicの金子さんの今年の1月のエントリー
Folksonomy : del.icio.usとFlickrを支える情報アーキテクチャ
と対照的で面白い。今年1年間で起きた変化でしょうか。

「分類→キーワード→分類」となっていくかどうかは分からないですが、そういう仕組みはあってもいいと思う。パソコンやメールのフォルダ分けとかがきちんとできる人はTaxonomyの方が好みじゃないかと予想したり。

2005年12月17日

CGI::Application用(デバッグ)サーバ

CGI::Application用(デバッグの機能はまだない)サーバを作ってみた。

とりあえず動いた版ソース
CGI::Application::HTTP
↑アップロードしていたファイルが古いものでした。修正しました(2005-12-17 16:37)
使い方は、

package MyApp;

use strict;
use warnings;
use base qw(CGI::Application::HTTP);

package main;

MyApp->new()->run(3000);

最後の3000はポート番号。これを

# perl server.pl
You can connect to your server at http://example.com:3000   

と出力されてPort 3000でCGI::Appが動きます。

ソースはほとんどCatalyst::Engine::HTTPからのコピーなんだけど、一部分に手を加えた

一つは、リクエストごとに$selfを別にするところで、最初のリクエスト前に

my %copy_of_self = %{$self};

としてコピーして、リクエストの処理時にblessして、それを使う

my $copy_of_self = bless \%copy_of_self,ref $self;
my $output = $copy_of_self->SUPER::run();

無茶な手かもしれないけど、こうしておかないと、CGI::Appの__PARAMSや__HEADER_TYPEがクリアできずにGlobal変数的な動作をしてしまう。実際作成時にredirectループにはまりました。

二つ目は、CGI_APP_RETURN_ONLYの利用とstatus lineの出力。
これも無理矢理だけど、再度headerだけ出力させて「Status: 200 OK」のラインを探すということをしている。

$ENV{CGI_APP_RETURN_ONLY} = 1;
my $output = $copy_of_self->SUPER::run();
        
# Generate Status Line
my $message=200;
foreach (split /\n/,$copy_of_self->_send_headers){
    if($_ =~ /^Status:\s+(\d+)/){
        $message=$1;
    }
}
print STDOUT $ENV{SERVER_PROTOCOL}. " " . $message . " "  . status_message($message) . "\015\012";
print STDOUT $output;

status_messageはHTTP::Statusモジュールのメソッド。これで

HTTP/1.0 200 OK

を出力の頭につけれます。

三つ目はmode_paramの書き換え。
元のままだとうまく動かない。

CGI::App版TropyのCaropyを動かしてみたけどうまく動きました。

2005年12月16日

CGI::AppでのDebugScreen

CGI::AppでのDebugScreenがCPANにアップされました。
http://search.cpan.org/~nekokak/CGI-Application-Plugin-DebugScreen-0.01/
id:nekokakさんグッジョブです。

id:tokuhiromさんが指摘している

たぶんこれは期待した動作をしないハズです。
他のライブラリが eval-block とか使ってるケースがあるので。


これのシンプルな例をあげると、

my $denominator = $q->param('denominator') || 0;
my $ret = eval{10/$denominator} || 0;

このコードで $denominatorや$q->param('denominator')が0の場合、当然2行目のeval blockの中はIllegal division by zeroでdieします。この場合eval blockがundefを返すのでその動きを期待して、$retに0を代入できる。

この場合に

$SIG{__DIE__}=sub{
    #hogehoge
    print "error";
   #dieせず次へ
}

としてしまうとたぶん期待通り動かない。
エラー文章が画面に出力された上で、次が動く。dieしてもエラー文章は表示してしまう。

どうしたらいいのかと考えると、おなじくtokuhiromさんが書いているのを参考にするといいと思う。
CGI::Appでは$SIG{__DIE__}でstacktraceを$self->{___HOGEHOGE}にでも入れて(代入するかpushにするか)、別にinit callbackで設定した設定したerror_modeのページでそれを出力というのはどうでしょう。
ただしこの場合、prerunやpostrunでのdieは補足できないです。この辺りが12 things。

このあたりが修正できたら開発時には結構便利なプラグインだと思われです。

pure-ftpdのカスタム認証ハンドラ

ライブドアBlogでも使っているらしいpure-ftpdは、カスタム認証ハンドラを簡単につくれます。
認証だけではなく、ftpd内蔵のquota機能なんかも使えて便利です。
http://www.pureftpd.org/
ドキュメントも上のサイトから行けます。

インストールは、

$ ./configure --with-altlog --with-extauth --with-paranoidmsg --with-quotas --with-throttling --without-inetd --prefix=/usr/
$ make install-strip

こんなconfigureでやってます。

認証ハンドラに使うスクリプトでは環境変数として

#!/bin/sh
$AUTHD_REMOTE_IP
$AUTHD_ACCOUNT
$AUTHD_PASSWORD 
$AUTHD_LOCAL_IP
$AUTHD_LOCAL_PORT

が使えます。認証の結果は標準出力に

auth_ok:1
uid:42
gid:21
dir:/home/foo
user_quota_size:100000
end

という形式で出力します。シェルスクリプトなら

echo 'auth_ok:1'
echo 'uid:42'
echo 'gid:21'
echo 'dir:/home/foo'
echo 'user_quota_size:100000'

という形で出力するだけです。

カスタム認証で適当なuid、gid、homedir、容量が割り増えるので便利。詳しい説明は、
http://www.pureftpd.org/README.Authentication-Modules
ここで。

あとは、デーモン起動ですが、認証ハンドラをまず起動して

$ /usr/sbin/pure-authd -s /var/run/pureftpdauth.sock -r /path/to/bin/authhandler.sh &

次にデーモン起動。

$ /usr/sbin/pure-ftpd -b -A -B -c 100 -E -H -I 5 -j -l unix -l extauth:/var/run/pureftpdauth.sock -u 499


「-j」オプションに認証ハンドラを「-l ハンドラ名」で付けて行きます。この場合、unixのユーザ認証をまずトライしてからカスタム認証を行います。

その他のオプションは、
http://homepage.mac.com/proc/pureftpd/pureftpd-option.html
が日本語で詳しくてありがたいです。

Perlで認証ハンドラを書きたい場合に%ENVでは拾えなかったので

#!/bin/sh
/usr/bin/perl ftpauth.pl $AUTHD_REMOTE_IP $AUTHD_ACCOUNT $AUTHD_PASSWORD  

こんな無理矢理な手でやってます。

12 Things I dislike with CGI::Application 続き

nekokakさんが補完してくださってます。
自分でも追加。

10. CGI.pmへの依存が高い。header出力やcookieあたりがCGI.pmに依存度高し。CAP::Apacheなどもあるけど、内部ではCGI.pmのコードをコピーしてきていたりしている。Request、Responseクラスが欲しいところ。
11. Setupでrun_modesを書くのが面倒。AutoRunmodeというプラグインがあるけど無理矢理実装感満載。runがスマートになれば改善するか。
12. runと合わせてstart_mode、error_mode、tmpl_path、prerun_modeあたりもリファクタ必要
13. 10分で(ryがない

12個超えた。run関連のことが多い。
まとめると、名前がかっこ良くて、簡単に使えて実装シンプルで依存性があまりなくてプラグインでの拡張が容易な次期CGI::Appを期待というところですか。

13番の10分で(ryですが、以前挑戦しかけたんだけどミスタイプが多くとretrieveの綴りが正しく書けなくてあきらめた。どなたか挑戦して公開しませんか?あれがあるとインパクトが強くていいいと思うのですが。

2005年12月15日

12 Things I dislike with CGI::Application

タイトルはmiyagawaさんからインスパイア。12個も書いてない。

1.runメソッドへ機能が集中しすぎ
CGI::Applicationをいじり始めると気になるのは、runメソッドへ機能が集中しすぎているところだと思う。
しかもちょっとスパゲティ。

sub run {
	my $self = shift;
	my $q = $self->query();#クエリーを呼び出し
	#RUNMODEの決定
	my $rm_param = $self->mode_param() || croak("No rm_param() specified");#
	my $rm;
	if (ref($rm_param) eq 'CODE') {
		$rm = $rm_param->($self);
	〜〜〜略〜〜〜
	#__CURRENT_RUNMODEにRUNMODEを挿入
	$self->{__CURRENT_RUNMODE} = $rm;
	〜〜〜略〜〜〜
	#prerun hookの呼び出し。$rm
 	$self->call_hook('prerun', $rm);
 	〜〜〜略〜〜〜
 	#prerun_modeがあればそれを使う。
	my $prerun_mode = $self->prerun_mode();
	if (length($prerun_mode)) {
		$rm = $prerun_mode;
		$self->{__CURRENT_RUNMODE} = $rm;
	}
	#RUNMODEから実行するメソッドを求める。
	my %rmodes = ($self->run_modes());

	my $rmeth;
	my $autoload_mode = 0;
	if (exists($rmodes{$rm})) {
		$rmeth = $rmodes{$rm};
	〜〜〜略〜〜〜
	}
	#実行。ここだけeval
	my $body;
	eval {
		$body = $autoload_mode ? $self->$rmeth($rm) : $self->$rmeth();
	};
	if ($@) {
		#エラーがあればそれを表示
		〜〜〜略〜〜〜
	}
	
	〜〜〜あとはレスポンスに関わるところ〜〜〜
	return $output;
}

ずいぶん省略したけど、まだまだ長い。
分かれていないという事は、一部分だけ処理に変更を加えたいと思ってもできない。call_hookだけでは限界がある。

2. runの中でのよくわからないcroak
できればevalブロックの中にいれて補足できるように。prerun、postrunもevalの中だとうれしい。

3. あまりモテない
はてなブックマークのブックマーク件数があきらかに
Class::DBI > Catalyst >>超えられない壁 >> CGI::Application
という現実

2005年12月14日

CGI::AppでのDebugScreen

このエントリーは最初の投稿時と大きく書き換えました。

nekokakさんがつくられた「CGI::AppでのDebugScreen」はかなりハゲシクGJです。

ただ、prerunに引っ掛けるとその他のPluginで動かなくなるのがありそうなので、error_modeを使うinitにHookしてSIG{__DIE__}を差し込むというのはいかがですか?

importとdebug_reportを書き換えた。

sub import {
    my $caller = caller;
    $caller->add_callback( 'init', sub{
        my $self = shift;
        $caller::SIG{__DIE__} = sub{
            $self->debug_report(@_);
            die @_;
        };
    });
    no strict 'refs';
    *{"$caller\::debug_report"} = \&debug_report;
}

sub debug_report{
    my $self = shift;
    my $desc = shift;
    my $vars = {
        desc => $desc,
        title => ref $self || $self,
        context => \&print_context,
    };
    $vars->{stacktrace} = [Devel::StackTrace->new(ignore_package=>[qw/CGI::Application::Plugin::DebugScreen Carp/])->frames];
    my $t = Template->new;
    my $output;
    $t->process(\$TEMPLATE, $vars, \$output);
    
    $self->header_props( -type => 'text/html' );
    my $headers = $self->_send_headers();
    print $headers.$output;
}


サンプルはこちら。
http://nomadscafe.jp/test/cgiappdebugscreen/app.cgi

ソースはここに
CGI::Application::Plugin::DebugScreenはこれ

「そんなRun modeはない」というエラーも補足可能です。


2005年12月12日

サーバの電気代 Dual Xeonは一台2A食います。

「電気代がサーバ本体よりも高くなる」--グーグルエンジニアが警告
最近どこの会社でもサーバの消費電力が問題です。

サーバセンターのラックを借りるときには、1ラックと標準電源1系統20Aとなると思います。(半分とかでも借りれるところがありますが)
空間的には1ラックは40Uなので、1Uサーバなら単純計算40台、スイッチとか入れても30U以上は使えます。 

ただし、Dual Xeonのマシンなんて置こうものなら1台2A食いますので、せいぜい7台、8台しか置けません。電源1系統追加(月費用増加・3系統以上は要相談)しても15台かそこら。残り半分以上は無駄空間となります。もったいない。

ちなみに、Pen4で1.5A、PenMで0.7〜0.8Aっていうのが最近の感覚。

そんなわけで会社の新規に購入するサーバはPenMなわけだが、Akiba Watchで次期Pentium M用デュアルCPUマザーの写真と性能表が展示中なんて記事をみつけて非常に気になっていたりする。

このBlogやFemoを動かしている自宅サーバは、Pen4の2.8GHz。動かし始めてから月の電気代が1000円あがりました。

SafariでXMLHttpRequestの時にキャッシュから読んでしまう件

Safari(1.3)でXMLHttpRequestによって何かしらにリクエストを送ったときに、サーバへリクエストせずにCacheから読んできてしまう事がある。かなり困りもの。

http://www.bitterpill.org/logid/1117777362000
で解決方法らしきものを見つけた。

xmlReq.setRequestHeader('If-Modified-Since', 'Wed, 15 Nov 1995 00:00:00 GMT');

をいれると良いらしい。

prototype.jsで使う場合は、

new Ajax.Request("/example",{
    method:'get',
    requestHeaders: ['If-Modified-Since','Wed, 15 Nov 1995 00:00:00 GMT']
});

こうやってみれば多分OK。

Femoにはすでに導入済み。

ちなみにサーバからのレスポンスに「expires:現在時間」をいれていても上のトラブルは起きます。Pragma:no_cacheまでは試していませんです。

2005年12月11日

Parsing of undecoded UTF-8 will give garbage when decoding entities at ...

Apacheのエラーログに

Parsing of undecoded UTF-8 will give garbage when decoding entities at ...

がいっぱい記録されている。
これは、HTML::Parserで出力されるwarningで、解決方法としては

$p->utf8_mode(1);

すればいいらしい

HTML::Parserに渡す前にflagを立てるしかなさそう。

けど、、
エラーをはいている場所が、Text::Hatena::HTMLFilterやHTML::FillInFormなのでちょっと困っている。

Text::Hatenaの0.06でHTML::Parserの3.45がPREREQ_PMに入っていますが、新しい機能を使うわけではないのなら、FillInFormと同じ3.26にしておくとかどうでしょう? > id:jkondoさん
上のwarningはHTML::Parser 3.40以降の機能らしいので。

今、HTML::Parserをダウングレードしてしまうか考え中。
何か別にいい方法があれば教えてください。

2005年12月09日

mysql_auto_reconnect

FemoはFastCGIで動かしているのですが、FastCGIで最後に悩んだのは、しばらくアクセスがないと(開発中など)MySQLのコネクションが切れて、

Caught exception in engine "DBD::mysql::st execute failed: MySQL server has gone away

などとエラーがでてしまうところです。再接続してくれません。

いままで気にもしなかったのですが、普通のCGIやmod_perlで動かしている場合には、DBD::mysqlみるとmysql_auto_reconnectというフラグが自動的にOnになります。
FastCGI(Catalystの方?)では$ENV{GATEWAY_INTERFACE}も$ENV{MOD_PERL}もないので、どうもmysql_auto_reconnectを自分でOnにしないとだめなようです。

Class::DBIでは

__PACKAGE__->connection('DBI:mysql:*','*','*',{mysql_auto_reconnect=>1});

こんな形です。

Femoは想定外にアクセスをいただいてます。ありがとうございます。
少々緊張しております。

メモ&カレンダーWebアプリケーションつくりました。

Tagがつけられるメモ帳Webアプリケーションをつくってみました。次のキラーアプリらしいカレンダー機能もついています。

http://femo.nomadscafe.jp/

↓Helpで使っているスクリーンショット
femoscreen.png

メモのTagとして、2005-12-09と日付をいれるとカレンダーの方に整頓される仕組みです。カレンダーでもTagCloud風にメモ件数の多い日は字が大きく表示されます。

Catalyst + Ajax + TypeKey認証 + Tagging + はてな記法と話題の技術を使い派手に実装しました。TypeKeyのアカウントを持っていればログインだけで使えるのでぜひお試しください。ご意見もくださいませ。

2005年12月07日

Class::DBI::SweetでのJOIN

Class::DBI::Sweetのjoinをつかった検索を試してみた。
ソースコードは作っているアプリケーションからの抜きだしなので足りないところがあるかも。

EntryとそのTagを想定して、

package Entries;

__PACKAGE__->table('entries');
__PACKAGE__->columns(All=>qw/id title text created_on/);

__PACKAGE__->has_many("tags"=>'Tags');

1;

package Tags;

__PACKAGE__->table('tags');
__PACKAGE__->columns(All=>qw/id entry_id tag/);

__PACKAGE__->has_a('entry_id'=>'Entries');

1;

の2つのクラスを作成。
例えば、「Perl」というTagを含むEntryがあるとして、それを検索するには

my ($pager,$entries) = Entries->page({
    'tags.tag'=>'perl'
},{
    rows=>10,page=>1,
    order_by=>'created_on desc'
});

というのでOK。「tags.tag」でjoinを指定できます。「tags」の部分はテーブル名ではなくhas_manyで指定したリレーション名です。

このときに発行されるSQLは

SELECT me.id, me.title, me.text, me.created_on
FROM   entries me, tags tags
WHERE  ( tags.tag = 'Perl' ) AND me.id = tags.entry_id ORDER BY created_on desc LIMIT 0, 10

このようになっている。

ちなみにどんなSQLが発行されているか確認する為には、環境変数DBI_TRACEを2にすればいいらしい。Class::DBIのwikiに書いてありました

$ENV{DBI_TRACE}=2;

とどっかに書くとトレースされます。出力される情報は結構多いです。

DBIx::Classはこの辺(joinの周り)がちょっと異なっていてよくまだわからない。

はてなブックマークのコメント欄のようなものをParseするモジュール

はてなブックマークのコメント欄のようなものをParseするモジュールを組んでみた。

本家の完璧なエミュレートではないです。

package Text::HatenaBookmarkLike::Comment::Parser;

use strict;
use warnings;

sub new{
    bless {},shift;
}

sub parse{
    my $self = shift;
    my $string = shift;

    my @tags;
    while(length $string && $string =~ m!^\s*\[([^\]]+)\]!){
        my $tag = $1;
        $string =~ s!^\s*\[([^\]]+)\]!!;
        next unless length $tag;
        push(@tags,$tag);
    }
    
    return Text::HatenaBookmarkLike::Comment::Parser::Result->new({
        tags=>\@tags,
        comment=>$string
    });
}

1;

package Text::HatenaBookmarkLike::Comment::Parser::Result;

use strict;
use warnings;
use base qw(Class::Accessor::Fast);
__PACKAGE__->mk_ro_accessors(qw(tags comment));

sub tags{
    my $self = shift;
    return wantarray ? @{$self->{tags}||[]} : $self->{tags};
}

sub uniq_tags{
    my $self = shift;
    my %seen;
    my @tags = grep {!$seen{$_}++} $self->tags;
    return wantarray ? @tags : \@tags;
}

1;


使い方は、

my $ret = Text::HatenaBookmarkLike::Comment::Parser->parse("[foo][bar][baz][foo]適当です");
print join(",",$ret->tags),"\n";
print join(",",$ret->uniq_tags),"\n";#ユニークなTagだけ
print $ret->comment,"\n";


今分かっている本家と違うところは、

  • Tagの数、コメントの文字数に制限がない
  • ? / % [ ] などの記号が使える

あたりが違う。

Tagの抜き出しの方法として、正規表現の\Gを使うことも考えられるのですが、上の方法の方が速かったです。

↓確認ベンチマークスクリプト

use strict;
use Benchmark;

my $str="[foo][bar][baz][foo]適当です";

Benchmark::timethese(300000, {
        'USE_G' =>\&use_g,
        'USE_S' => \&use_s,
});

sub use_g{
	my $str = shift;
	my @tags;
	while($str =~ /\G\s*\[([^\]]+)\]/gc){
		my $tag = $1;
		push(@tags,$tag) if length $tag;
	}
	$str =~ /\G\s*(.*)/g;
	my $comment = $1;
}

sub use_s{
	my $str = shift;
	my @tags;
	while($str =~ /^\s*\[([^\]]+)\]/){
		my $tag = $1;
		push(@tags,$tag) if length $tag;
		$str =~ s/^\s*\[([^\]]+)\]//i;
	}
	my $comment = $str;
}


実行結果

$ perl tag_seprate.pl 
Benchmark: timing 300000 iterations of USE_G, USE_S...
     USE_G:  2 wallclock secs ( 1.49 usr +  0.01 sys =  1.50 CPU) @ 200000.00/s (n=300000)
     USE_S:  1 wallclock secs ( 0.47 usr +  0.00 sys =  0.47 CPU) @ 638297.87/s (n=300000)   

Text::TagsでTagging

CPANでTagging関連のモジュールを探すと

あたりが見つかると思う。どれも基本は半角スペースで区切られたfolksonomy文字列をパースするモジュールです。Text::Folksonomiesはシンプルすぎて足りない部分があったりします。Data::Taxonomy::Tagsは「:」コロンで区切ってのカテゴリー付けなど、複雑なタグをパースできます。

Text::Tagsはシンプルでいい感じです。
基本的な使い方は

my @tags = Text::Tags::Parser->new->parse_tags(q{ foo  bar  "baz bap" jenny's   'beep beep' });

なんだけど、日本語はそのままではうまく通りません。utf8フラグが立っている事が必要です。
textのエンコードがUTF8前提だけど

my $text = qq/日本語 かぜぶろ blog perl/;
my @tags;
utf8::decode($text) unless utf8::is_utf8($text);#必要なら
foreach my $tag (Text::Tags::Parser->new->parse_tags($text)){
    utf8::encode($tag) if utf8::is_utf8($tag);#必要なら
    push(@tags.$tag);
}

とするとうまくTagに分解できます。ちなみにparse_tags時にダブっているTagを自動で取り除いてくれます。

日本語のことを考えるのであれば、Tagの区切り文字としては半角スペースだけではなく全角スペースも必要じゃないかと思ったりもする。

2005年12月06日

大雪で高山線止まり新幹線も止まる

積雪でJR高山線の列車止まる、80人が車中泊 from Yahoo! NEWS

よくあるんだろうけど、正月にこういうことがありませんように。

東海道新幹線、三島市のレール継ぎ目損傷でダイヤ混乱 from Yahoo! NEWS

同日に両方の帰省先に電車のトラブルというのはいやだなぁ。

CSSXSSってかなりヤバい?

もうはてなキーワードになっているCSSXSSってかなりヤバくない?

CSSの@importで読んできた「CSSらしきテキストファイル」を

document.styleSheets(0).imports(0).cssText

で読む事ができる。CSSらしきテキストファイルは同じドメインとかそういう制限もないらしい。

「CSSらしきテキストファイル」がHTMLであれば正規表現とかgetElementByIdで読める。
これだけでも悪用方法がいくつも浮かんでくる。CSSXSSかなりヤバす。

参考
antipopさんのWeb における最新の攻撃手法 CSSXSS がアツイ!!!じゃなくてヤバイ!!!

2005年12月02日

Catalyst 5.59でデバッグ用サーバの自動再起動がきかない。

Catalyst 5.59でデバッグ用サーバの自動再起動がきかないようです。

開発版ではなおっている模様。
http://dev.catalyst.perl.org/changeset/2324

自動再起動、一度慣れたらもどれません。
5.60を待つか、開発版をいれるか、迷っているうちに5.60がでそうだな。

よくよく調べると、デバッグ用サーバのスクリプトの一部を変更すれば直る事がわかりました。
script/myapp_server.plの47行目あたり

use MyApp;


require MyApp;

とrequireにするだけでOKです。

影響は「5.59」でcreateしたプロジェクトだけのようです。