D-7 <altijd in beweging>

Day to day life of a Perl/Go/C/C++/whatever hacker. May include anything from tech, food, and family.

2009年05月

注:まだ本当にレンタルサーバーでは試してないです。

思い立ってすっげぇ簡単なスクリプトを書いてみた。local::libを同梱する必要があるのと、~/perl5と.cpanがスクリプト実行ユーザー権限で書き込み・読み込みできる必要がある。
#!/usr/local/bin/perl use local::lib qw(/home/daisuke/perl5); use CGI; use CGI::Carp qw(fatalsToBrowser); use CPAN; sub main { local $| = 1; my $q = CGI->new; if ($q->param('mode') eq 'install') { install($q); } else { control($q); } } sub install { my $q = shift; print $q->header('text/plain'); CPAN::Shell->install($q->param('module')); } sub control { my $q = shift; print $q->header(), $q->start_html(), ; print $q->start_form(-action => $ENV{SCRIPT_NAME}, -method => 'GET'); print $q->textfield( -name => 'module', -size => 30, -value => $q->param('module') ); if ($q->param('module')) { my $mod; { local *STDOUT; close(STDOUT); $mod = CPAN::Shell->expandany($q->param('module')); } if (! $mod || ! $mod->inst_version) { print $q->div($q->param('module') . ": Not installed"); } else { print $q->div( ($q->param('module')) . ": Installed version is " . $mod->inst_v ersion); } } print $q->submit(-name => 'mode', -value => 'check'), $q->submit(-name => 'mode', -value => 'install'), ; print $q->end_form(), $q->end_html(); } main();
    このエントリーをはてなブックマークに追加 mixiチェック

Catalystはたいへんすばらしいフレームワークですが、新しいプロジェクトを始める、という時にcatalyst.plでスケルトンから作り直していつものプラグインを設定して・・・みたいな面倒な手間がいろいろあります。

Pixisはなるたけ簡単に新しいアプリを作れるようにしたかったので最初からプラグイン機構を念頭に置いて書き始めましたが、それはあくまで機能の追加にしか使えず、JPAサイトのようにPixisというフレームワークを使って、JPAというサイトがPixis機能を乗っ取るというような場合はそれだけではうまく設計ができませんでした。

これについては悶々と考えていたのですがCatalyst 5.8になり、Mooseベースのオブジェクト指向ができるようになったことでひとつひらめきました。たとえばJPA::Webというアプリを作るとして、基本的にPixisがすでに提供しているすべてのコントローラーやモデルをそのまま使いたい、と思うならこれまでは自前でPixisを継承したコントローラーやモデルを作ることが必要でした。ですがMooseが前提ならClass::MOPの機能を使って、イン・メモリでクラス作っちゃえるじゃないですか。

ってことでやってみました。今のPixisは以下のように継承を宣言するだけで、とりあえずさくっと動くようになりました:
package JPA::Web; use Moose; BEGIN { extends 'Pixis::Web' } __PACKAGE__->setup_config(); __PACKAGE__->setup(); 1;

これは便利!

実装方法まだハックっぽい面がありますが、基本的な方向性はこれで正しいと思います。setup_components()とconfig()をオーバーライドして、子クラスはconfig()に引数を渡さずに設定できるように、setup_components()はPixis::Web::名前空間以下のすべてのモジュールをMoose::Meta::Classのcreate()を使ってインメモリで子クラスを作れるようにしてみました。

もうちょいしたらJPAサイトもこの方式に切り替えようと考えています。
    このエントリーをはてなブックマークに追加 mixiチェック

DSCN0878.JPGアホみたいに忙しかった12月〜2月くらいの間にもうどうしてもたまらなくて5月の連休明けに伊豆旅行を衝動的に予約しておいたので、先週末行ってきた。

今回は例の本の印税の一部を使って自分的には豪遊。3日間、ネット回線もない旅館で上げ膳据え膳、食う寝る湯につかる以外のことをほとんど何もせずにぼ〜〜〜〜〜っと過ごした。体力が大分チャージされたのを感じる。

しかしネットのないところに泊まるって、4,5年ぶりじゃなかろうか。メールもチェックしないなんてほんとう久しぶり。ネットのつながらない状況でPixisを継承して新しいアプリを作るためのハックをいろいろとしてみたくらい。あとは本当に本当になんもせんかった。

1年に一回くらいはこういうのしたいよなぁ。また本を書かせてもらうか・・・

DSCN0868.JPG
おまけ。休息する俺様



サウナでしゅ〜しゅ〜言ってるところ。

    このエントリーをはてなブックマークに追加 mixiチェック

本日は東京で最高気温27度だそうで。いやー。いいすね。夏!夏!夏!梅雨はすっとばして早く夏こーい!

摂氏20度を常に超えるようになってからすこぶる調子がいい。自分はやっぱりブラジル育ちの夏の子なんだなぁと毎年思う。暑けりゃ暑いでまぁまたつらいこともあるけど、寒くなって身動きとれなくなるのに比べると、本当夏はいいわ。暑い朝、出社しながら「今日も元気だ、ガハハハ」とか言いたくなる。

というわけで今日も元気です。
    このエントリーをはてなブックマークに追加 mixiチェック

これから新幹線の中で書きます。GunghoPixisで色々Moose::RoleとTest::FITesqueでやってみた結果についてぼちぼち。ちなみにTest::FITesqueはJay Shirleyさんに言われて、えーと思ってたんだけどid:lopnorががっちり基本を書いてくれて、それでようやく理解した。lopnor++
    このエントリーをはてなブックマークに追加 mixiチェック

(5/13 追記): メソッドモディファイヤーの件、Catalyst開発チームにテストパッチを送ろうと思って色々書いたら理由がわかったので、追記しました

最初からMooseベースでアプリケーションを作るというのは、実務ではなかなか難しいのはわかります。なので使うとしたら既存のシステムにMooseを組み込む事のほうが多いとは思います。

それについての一般論は JPA #02で話すのでおいておきますが参加申し込みは今日5/12までですよ!)、5.8 からMoose化したCatalystにポートを行う際に実際にあった問題・注意点をちょっと書き出してみます。

1. use Catalyst

Catalyst::Upgradingを読んでいると
package MyApp; use Moose; extends 'Catalyst'; __PACKAGE__->setup(qw/ ConfigLoader /);
という表記が見られるが、これは気をつけないと駄目。

自分が直面した問題は、path_to()等を使った時に起こった。path_to() は現アプリのルートディレクトリからのパスを指定したい時に使う。例えばTTテンプレートのコンパイルされた物をMyApp/tt2 以下に格納したければ、
my $path = MyApp->path_to("tt2");
というようにする。このpath_to() というのは $c->config->{home}という値を使用するのだが、この値は通常Catalystによって自動的にMyApp/ 以下に設定されるので、デフォルト状態のファイルレイアウトでCatalystを使用する分には特に問題にならない。だが、上記のextends記法を使っているとなぜか home変数が設定されず、path_to()の挙動がおかしくなる。

これを回避するには、home変数を設定する必要があるのだが、homeの自動設定はCatalyst::import()で行われており、通常extends() (use baseでも一緒)を使うとimport()は実行はされないのだ(importが自動的に実行されるのは、「use Module」とした時だけ)。それはすなわちhomeの初期化が行われない、ということである。

ということで、実際はこうする必要がある:
package MyApp; use Moose; use Catalyst; extends 'Catalyst';

2. :Index, :Private, :Local, :Chained等

CatalystでコントローラーメソッドをURIパスに結びつけるのが:Indexや:Chained等のメソッドアトリビュートと呼ばれる物だが、これらはPerlのコンパイルフェーズで処理される。いわゆるBEGIN {} ブロックが走るタイミングと考えて良い。

コンパイルフェーズというのは特殊で、use() 宣言やBEGIN{}ブロック等、Perlのネイティブレベルで定義されたものだけが(注:割愛するが、実はそれ以外の黒魔術的なやりかたもある)、それ以外のコードを走らせる前に全部動く。本当のコードが走る前の一種の初期化フェーズと考えてもいいかもしれない。

通常Mooseマニュアルには継承を行う際はextendsを使えと書いており、同じノリでMyApp::Controller::RootをCatalyst::Controllerから継承する、というようにMooseで書こうとすると以下のようになる:
package MyApp::Controller::Root; use Moose; extends 'Catalyst::Controller'; sub index :Index { .... }
が、ここが落とし穴。これだと :Indexを見た瞬間にPerlがエラーを吐く。なぜか。

:IndexをPerlが最初に見るのはコンパイルフェーズだ。前述の通り、use宣言やBEGINブロック以外はその段階では実行されない。:Indexを理解するためにはMyApp::Controller::Rootはその時点ですでにCatalyst::Controllerを継承していなければならないのだが、継承を指定するextends宣言は普通の関数なので、コンパイルフェーズでは走らない。よって、コンパイルフェーズでは MyApp::Controller::Root->isa('Catalyst::Controller')にはなっておらず、メソッドアトリビュートも理解されないのだ。

これを回避するのには単純にBEGINブロックで囲ってやればいいのだが、正直一瞬意味がわからなくて困る
package MyApp::Controller::Root; use Moose; BEGIN { extends 'Catalyst::Controller' } sub index :Index { .... }

3. プラグインが実行されない

(5/13 追記) 以下の理由がわかった。単純。Catalyst->setupはMooseのメソッドモディファイヤーを使用する前に呼び出されなければならない。
package MyApp; .... before finalize => sub { ... }; __PACKAGE__->setup(...); # これは駄目
package MyApp; .... __PACKAGE__->setup(...); # これならOK before finalize => sub { ... };
Catalystは過去の経緯から、setup()時にクラスの継承関係を変更するのだが、それをやる前にメソッドモディファイヤーを使ってしまうと親クラスをきちんと認識してくれない、ということっぽい。setup()の中で親クラスのリセットを行ってくれればそれでいいんじゃねと思うが、それまでは単純にMyApp.pmの上のほうでCatalyst関係の初期化を行えば良い、ってことですな。

プラグインの多くは継承のメカニズムを使ってCatalyst内のメソッドの前か後に実行されるように書かれている。

Catalyst 5.8からはMooseなので、プラグインを書くほどでもないちょっとした事ならMyApp内でメソッドモディファイヤー(afterやbefore)を使ってちょこちょこと自前のロジックを足したりもしたいのだが、それをすると突然プラグインが動かなくなったりする。

自分がはまったのはCatalyst::Plugin::Unicode。それまで動いていたのに、以下のようにbeforeメソッドモディファイヤーで自前のエラー処理をつけようとしたら、"Wide character in syswrite"というエラーが出始めた。
package MyApp; ... before finalize => sub { my $c = shift; $c->handle_exception if @{ $c->error }; };
色々と試してみたところ、これはメソッドモディファイヤーが適用されるタイミングに左右されることがわかった。モディファイヤーがCatalystの継承によるディスパッチを邪魔して、Catalyst.pm以外のメソッドを呼び出してくれない(ちなみにこれでより一層Moose::Roleが素晴らしい、ということがわかった。継承を意識しながらメソッドディスパッチとかうざすぎる)

ちょっとまだ一般解まではわかってないのだが、とにかく、上記のようにMyApp内からfinalizeにフックしたいなら、overrideとnext::methodを組み合わせるのが吉
package MyApp; ... override finalize => sub { my $c = shift; $c->handle_exception if @{ $c->error }; $c->next::method(@_); };
このようにすると正しくプラグインへのディスパッチを行いつつ、フックもできる。

4. エラーがよくUnknown Errorになる。

Perl 5.10.x ではUnknown Errorがよく出る、というのは聞いていたが、5.8.9でもなぜかなる。あんまりちゃんと調べてないので、そもそもこれは既知の事だったらあれなのだが、わかっているのはこれはとにかくコンパイルフェーズやその他セットアップしている時に起こるということ。

なので、このエラーが出たら、まずモジュール類がちゃんとコンパイルすることから調べ始めるのを薦める。それとちょうど良い機会なので、エラーを吐きそうなブロックは明示的に囲ったりして自前でエラーを出力するようにすればよい。warn && confessみたいな単純な処理でも以外と役立つ。
eval { Class::MOP::load_class($pkg); # 個人的にはこういうのとか、DBICのconnect()とかでよくなった }; if ($@) { warn && confess; }
いずれにしろ、エラーケースはちゃんと自前で処理したほうが良いので、これを機に怪しいところは全部やっつけるといいのではないだろうか。




とりあえず、今回Catalyst 5.7 -> 5.8に移行した際に直面した問題はだいたい以上のようなもの。それまでのCatalystでは出ない類のエラーが多いので、困る人も多かろう。なんらかの手助けになれば幸い。
    このエントリーをはてなブックマークに追加 mixiチェック

JPA #02用「Moose入門」スライドを書いた。長かった・・・。

盛りだくさんな内容になると思いますので、是非参加してください!懇親会というか、飲みもある予定です
    このエントリーをはてなブックマークに追加 mixiチェック

社長業の傍ら理事長業を行いつつ、時々プログラマーをしています。今は来週JPAセミナー#02@大阪用のスライドの調整に飽きたのでブログを書いているわけですが。

さて、来週のJPAセミナー、私はまた泊まりで行くわけですが、今度こそ、今度こそ、誰か飲みませんか!懇親会!懇親会!

5/14 18:30からセミナー、9時前くらいに体が空く予定です。まだ空きがあるっぽいので、未登録な方は是非参加ください。かなりお値打ちなセミナーになるはずです :)

よろしくお願いしますー
    このエントリーをはてなブックマークに追加 mixiチェック

おりょ?動くよ?
use strict; my $stop = 0; $SIG{"INT"} = sub { $stop = 1; print "sigint received\n"; }; while (1) { print "loop start\n"; sleep 3; if ($stop) { print "stop flag is true\n"; exit 1; } print "loop end\n"; }
こんな感じの出力:
daisuke@beefcake-7 ~$ perl hoge.pl loop start ^Csigint received stop flag is true daisuke@beefcake-7 ~$
    このエントリーをはてなブックマークに追加 mixiチェック

突然六本木ヒルズに行くことになり、しかしアテがあったわけでもないので「Slumdog Millionaire」を見てきた。エンディングはまぁ、うんうん、って感じだけど、2時間くらいあるこの映画、全然飽きもせずに楽しめた。おすすめですな。

ところでにーちゃん(大人)の顔が少しマイケルジャクソンに似ている気がした。いや、それだけなんだけど。
    このエントリーをはてなブックマークに追加 mixiチェック

JPAブログを別に作ったのでこっちでJPA関連の事を書く必要がなくなった。万歳。ってことで酒の事でも書こう。

JPA大阪梅田開催の下見で先週大阪に行ってきたわけですが、その夜特に飲む場所とかを決めていなかったのでぐぐってバーを探し出したわけです。一人で居酒屋っぽいのとかはなんなんで、ホテルバーっぽいの探してました。

んで、シガーがおいてあるというのが決め手で梅田の某バーにたどり着いたわけです。この店、別になんも問題はなく、店長が年齢が若い割にしっかりしててよかったんですが、酒の種類が・・・ない。

シェリー、メンタ、ウンダーベルグ、悉く・・・無い。スコッチはオフィシャルものはあるけど、テーピングしてないからちょっと抜けてる。おしい。おしいなぁ。こういうのがあるとシェイカー振ってもらうのもなんとなく警戒してしまう。やる気は感じられたので、是非直していただきたいと思った。

さて、で、渋谷に帰ってきていきつけのBar 30 Clubさんとか、Bar h,さんとかに行くわけですよ。すると、それらの酒が全部あるんすな。素晴らしい。大阪のその店は女性としけ込むのにはいいかもだけど、是非こういう酒好きの心をくすぐる要素をもう少し取り入れていただきたい。大阪で他にも良い店を今度探そう。

で、全然話は飛ぶけれども、Bar 30 Clubさんについてこの間偶然検索したら、「常連さんが幅を効かせてて・・・」「顔を覚えてもらえなかった・・・」とか書いてある書き込みに遭遇したんですが、正直、え、なにそれ?

こういう店は少しずつお互いが歩み寄っていくところであって、いきなりどうこうできるわけないじゃん。俺も話すようになるまでに大分通ったし、酒の好みについても大分伝えたから、今は安心していけるわけで。そういう時間をかけるところにいきつけの店ってのは意味が出てくるとおもうんだけどなぁ。
    このエントリーをはてなブックマークに追加 mixiチェック

このページのトップヘ