JPA セミナー #1 (Session 2 Catalyst 改)

1. Plugin

プラグインの使用を控える
  • 間違った使用方法、実装方法で定義されていることが多い
プラグインの使いどころ
  • リクエストディスパッチのロジックを監視、変更する場合のみ
  • リクエストパラメーターの変換、Catalyst のメソッド実行チェーンの変更を含む
本当に使わない方向で
  • Catalyst::Plugin::* の中にはそもそもプラグインとして実装されるべきでないものが多い
  • コンテキストオブジェクト ($c) にメソッドを追加する実装がそもそも良くない手法
  • 大半のプラグインはモデルとして実装されるべき
  • それ以外はそもそも存在するべきがどうかさえ疑問 (eg.Catalyst::Plugin::Message)
Catalyst::Plugin::Authentication はどうなの?
  • とはいえ、例外はある
  • 認証系とセッション系はプラグインで存在しなければならない意味がある
  • 認証プラグインは一番最初のコントローラーアクションを実行する前にセッションオブジェクトを具現化しておく必要がある
  • Catalyst::Plugin::Authorization はアプリケーションレベルでディスパッチの流れを変える必要があるから--なお以上のことはコントローラーアクションとしても実装可能
  • Root の begin でセッションオブジェクトをインフレートし、ユーザー復元し、ACL 確認も実現できる
$c->user, $c->session
  • Web アプリケーションを実装する上で必要不可欠
  • アプリケーショングローバルで使用
  • プラグインとして実装されることを肯定する必要充分な理由
  • これらのような充分な理由が無い Catalyst プラグインを書いて CPAN に登録すると、Catalyst コアチーム、mst が悲しむ

2. デバッギング

perl 5.10's "Unknown Error"
  • メソッドアトリビュートを読み込む段階での構文エラー発生時によく出るエラー
  • 問題解決のパッチはあるが多くのレポジトリにまだ適応されてない
  • perl -cw で実行すれば構文エラーは明確に表示される
?dump_info=1
  • さまざまな情報を取得できる
  • 環境変数 CATALYST_DEBUG=1 を設定すれば、ソースに -Debug フラグが無くてもデバッグモードで起動する
  • TT なら、[%USE dumper%][%dumper.dump_html(valiable)%] で変数の内容を表示できる
テストを書く
  • Test::WWW::Mechanize::Catalyst で自動テストが大変書きやすくなる
  • Controller のバグ早期発見につながる

3. 設定

config()
package MyApp::Controller::Foo;
__PACKAGE__->config(
	'foo' => 'bar'
);
  • どのコンポーネントも設定用の config アクセサが実装されているが、原則初期化終了前にする
  • 初期化終了後で変更すると予想困難なバグの発生につながる
  • 先行順位を高く設定するには、アプリケーションレベルで設定する
MyApp->config->(
	'Controller::Foo' => {
		'foo' => 'bar'
	}
);
has 'foo' => (
	is  => 'rw',
	isa => 'Str',
);
  • 以前までの Class::Accessor::Fast 方式であれば以下のように
__PACKAGE__->mk_accessors('foo');
$self を使う
  • アクセサを作らないのであれば $self 内でマージされている値を参照することも可能
$self->{'foo'}
  • 同様に myapp_local 設定ファイルに指定があれば、その値がコンポーネントに設定される
  • もしなければ、順番に myapp.conf, アプリケーション, コンポーネント本体の順に適用される値が検索される
これは止めましょう
package Controller::Foo;
__PACKAGE__->config('foo' => 'bar');

sub method : Local {
	my ($self, $c) = @_;
	$c->config->{'Controller::Foo'}->{'foo'};
}
  • 大抵のケースで多分正常に動くが、確実ではない
  • この場合は、 $self->{'foo'} で取り出すのが正しい
package Controller::Foo;
__PACKAGE__->config('foo' => 'bar');

sub COMPONENT {
	my ($self, $c, $config) = @_;
	$config->{'foo'}; # NO!
}
  • 先ほどの例はなんとなく動くが、こちらは多分動かない
常に $self を使おう
  • コンポーネント範囲の設定は $self を利用するのが理想
  • アプリケーションレベルの設定は $c->config から呼び出しても良いが、それも本当にアプリケーション全般に渡って必要な設定に限って使用したほうがよい
Config は使い過ぎでちょうど良い

4. コントローラー

より良いコントローラー
  • 薄ければ薄いほど良い
  • ベースクラスを利用して薄いコントローラーを実装する
ベースクラス
  • 多くのコントローラーは機能ごとに大まかに分類することが出来る
  • 大半は設定から動作をカスタマイズできるように実装することが可能
  • 先ほどの設定の適用方法の延長で、ベースコントローラーから機能を継承しつつ、Model に最大限の機能を委譲することによって、ほとんど空の Controller を実装していくことが可能
設定だけの Controller
  • うまくやれば時には Controller の実装はそれ用の設定を指定するのみのクラスになる
  • DBIC を使って CRUD 操作をするアプリケーションなどはこのような方法で実装することが可能
  • Catalyst::Controller::DBIC::API

5. Chained

基本的な考え方
  • 経路を繋げていく
  • 経路を分けることにより、ログを取ったり、後々変更が容易に
  • Action != URI
  • URI とは公開される URI であって、内部で呼び出されるメソッド自体と関係している必要はない
  • 内部で使用されているだけのメソッド類が URI 定義に影響を及ぼすのは変な感じ
実際に使用されるケース
  • auto アクションは各コンポーネントで1回ずつ実行されるが、Chained の場合、経路で繋げた全てのアクションで実行することが可能
  • Chained に切り替えてから一回も auto を使ったことが無い
  • 実際の Chained の使い方は github にあるサンプルで
  • http://github.com/jshirley/ (catalyst-example-chained)

6. デプロイ方式

External FastCGI
  • ゼロ・ダウンタイム
  • unix ソケットだと、ひとつのソケットで複数アプリケーションを同時に待機させることが可能
  • 新しいバージョンをデプロイする際に、古いバージョンを停止する前に、新しいバージョンを起動すればダウンタイム無しで交換が可能
  • アプリケーションを再起動するのに Web サーバーを再起動する必要がない
  • mod_perl を使わなければいけない用途をちゃんと分かっていないのなら、mod_perl を使う必要はない
HTTP::Prefork
  • C関数にできるだけのことを割り当てているため軽くて安定
  • だが今のところ高負荷もしくは認知度があるサイトでの事例は無い
  • unix ソケットの裏技は使えない
  • ポートを分けることによる分散などでダウンタイムを最低限に抑えることはできる

7. ログ

Catalyst::Log::Log4perl
  • Log::Log4perl の応用性と機能をそのまま $c->log から使用可能
  • Catalyst::Log 以外のログオブジェクトを使う場合、極力標準の debug, info, warning, error, fatal を使うことを推奨
  • 基本的に必要な機能は全て得られるし、それ以上を使うメリットはさほどない
  • 標準メソッドのみを使えば、ログオブジェクトを変更しても、アプリケーション側を変更する必要はない
$c->_dump()
  • あまり知られていない
  • 標準ログメソッドではないのでドキュメントには書いてない
  • Data::Dump を使用して構造体を簡単に読めるように
  • Catalyst::Log と Catalyst::Log::Log4perl の両方に実装
  • 出力レベルは info

8. Action('Classes')

Catalyst 5.7 でアクションクラスとして実装されているものは 5.8 では遥かに便利な Role として再実装されている
Catalyst::Controller::ActionRole
  • ActionClass の概念を Role と入れ替えることができる
  • Role とは、「あるオブジェクトの特徴・動作を表現する」ためのものなので Catalyst の Action を Role で実装するのは自然なこと
sub action : Does('moo')
  • Role の概念をまだ理解してなかったり、使ったことがなければとりあえず今は無視
  • 気になったら Moose::Role を参照のこと
ActionClass は Action の動作内容を Catalyst 外のメソッドにラップすることができる
Catalyst::Action::REST
  • アクションの名前と HTTP リクエストのメソッドを検証し、適切な REST 操作が可能なメソッドに処理を割り振ることが可能
使い時
  • meta と思われる副アクションが本アクション以外に実行する必要のある時に有効
  • 実装例は、アクセスされたページのプロパティを抜き出してスタッシュに保存するなど
  • これは Model でも実装できるが、Action の Role を実装すると構文がすっきりしつつ、リクエストディスパッチにカスタムロジックを注入できるようになる

9. local::lib

Vendor Perl = Not Good
  • ベンダーの提供する Perl に依存すると、ベンダーが管理する Perl のバージョンや、彼らが混入させるバグ等に縛られることもある
  • 酷いわけではないが、良くも無い
  • local::lib の初期導入のコストの低さとメリットを考えると使わない理由がそれほどない
Makefile.PL を使う
  • 管理をきちんとすれば、依存関係のあるモジュール類のインストールやエンドユーザー環境での動作も簡単に保障できる
  • local::lib と一緒に使えばインストールされたパッケージが実際にアプリケーションが使っているパッケージだということも保障できる
  • 複数ユーザーのシステムで同じアプリケーションを複数バージョン運用することも可能
アプリケーション権限はユーザー権限で
  • 極力 root で実行しない
  • 依存関係も root を必要とするべきではない
  • External FastCGI を local::lib でデプロイすれば、サーバー設定以外のアプリケーションのデプロイと実行は root 権限が全くなくても行うことが可能
perl -Mlocal::lib
  • .bashrc や .cshrc に適用する環境変数を表示する
  • $HOME に perl5/ ディレクトリを作成してくれる
  • あとは cpan 経由でインストールすれば自分専用のディレクトリにモジュールがインストールされるようになる
  • 失敗した場合は rm -Rf $HOME/perl5 するだけ
  • 初期設定では $HOME/perl5 だが、それ以外のパスでも複数運用可能

10. 参加

Catalyst に貢献するには
  • 簡単
  • 大半は技術力が必要というわけではない
  • ドキュメントの提供
  • テスト
  • ブログ、宣伝活動
アプリケーションを公開する
  • アプケーション増 = 開発者増
  • 作ったときの経験を語る
  • よりよい開発者がより多く集まる
  • Perl が終わったという意見が浸透してる理由
    • 新しいサイトが Perl を使っていることについてオープンに語らないから
    • Vox は Catalyst で開発されていることを知らない人は多い