Plackでの500ページ表示
Bloged at : 2011-04-10 10:03:05
Plackを利用して、エラーページ(500) を表示する際には、Plack::Middleware::ErrorDocument を利用すればよい。ただし、このミドルウエアーは正常なデータレスポンスを500のコード等で返した際にのみ実行されるので、アプリケーション側で例外的なエラーが発生した場合には拾ってくれない。そういったケースの場合は、Plack::Middleware::HTTPExceptions と併用して利用すれば、例外エラーを正常な500コードのレスポンスに変換してくれるのでうまくいく。
この際に気をつけなければならないのは順番で、HTTPExceptionsを $appに近い側で呼ばなければならない。
builder {
enable "ErrorDocument",
500 => '/uri/errors/500.html', 404 => '/uri/errors/404.html',
subrequest => 1;
enable "HTTPExceptions";
$app;
};
この際に気をつけなければならないのは順番で、HTTPExceptionsを $appに近い側で呼ばなければならない。
エラー標準化&ロジック共有化モジュールAplon
Bloged at : 2011-02-12 22:09:27
目的
- エラー処理の標準化を行う
- ロジックを共有しやすいようにする
概要
Webのコントローラに直接ロジックを書くと、使い回しがしづらい&Web依存(リクエスト/レスポンス等処理)が発生してしまうという問題の対応として、ロジック周りを外に出すという設計が一般的に行われます。Aplonはこのロジック部分の実装の標準化を行いつつ、実装の手助けを行い、また、作成したロジックを再>利用することを可能にします。
チュウトリアル
基本
ロジック関数を作る
ユーザIDを渡すとユーザのデータが取得できる関数を試しに作ってみます。
package Aplon::Model::User;
use Mouse;
extends 'Aplon';
sub lookup {
my $self = shift;
my $id = shift;
if($id =~ /^[0-9]+$/ ){
if($self->aplon_user('ADMIN_USER') ){
return {
name => 'tomohiro',
password => '1111',
};
}else {
return {
name => 'tomohiro',
};
}
}
else {
$self->abort_with('not_found');
}
}
__PACKAGE__->meta->make_immutable();
no Mouse;
1;
ロジック関数を実行する
my $model = Aplon::Model::User->new;
eval {
my $user = $model->lookup( 32 );
};
if( my $error_obj = $@){
# エラー処理。
print Dumper $error_obj->error_keys;
}
利用ユーザで処理を変更する
aplon_user*1で利用ユーザを変更することにより、たとえばadminページではstatus = 0のデータを取得できる等の切り分けを簡単に行うことができます。
my $model = Aplon::Model::User->new({ aplon_user => 'ADMIN_USER' });
eval {
my $user = $model->lookup( 32 ); # passwordも取得できる
};
if( my $error_obj = $@){
# エラー処理
print Dumper $error_obj->error_keys;
};
Data::FormValidatorを利用する
Validateモジュール作成(Aplon::Validator::Data::Form::Validator)
Aplon::Validator::Data::Form::Validatorを利用することにより、簡単に Data::Form::Validatorと連動することができます。Aplon::Validator::Data::Form::Validatorだけではconstraintsの設定がありませんので、以下のように拡張する必要があります。
package MyApp::Validator::DFV;
use Mouse::Role;
with 'Aplon::Validator::Data::Form::Validator';
use Data::FormValidator::Constraints qw(:closures);
sub DFV_constraint_methods {
my $self = shift;
return +{
status => qr/^[01]$/,
name => [
FV_max_length(10),
qr/^[a-zA-Z0-9_]+$/,
qr/^[a-zA-Z_]+$/, # multi invalid test
],
}
}
sub DFV_constraint_method_regexp_map {
my $self = shift;
return +{
qr/_id$/ => qr/^[0-9]+$/,
}
}
1;
Modelモジュールで利用する
package MyApp::Model::User;
use Mouse;
extends 'Aplon';
with 'MyApp::Validator::DFV';
sub profiles {
my $self = shift;
return {
get_name => {
required => [qw/name/],
optional => [qw/status/],
}
}
}
sub get_name {
my $self = shift;
my $args = shift;
my $results = $self->assert_with($args);
if($results->valid->{status}){
return $results->valid->{name};
}
else {
$self->abort_with($results,'not_found');
}
}
__PACKAGE__->meta->make_immutable();
no Mouse;
1;
実行する
eval {
$model->get_name({ name => 'tomohiro.teranishi', status => 1 })
};
if(my $error_obj = $@ ){
# do error thing with $error_obj
}
同じ用な感じでAplon::Validator::FormValidator::LazyWay を利用することができます。さらに詳しくは https://github.com/tomyhero/p5-Aplon/tree/master/t 配下を見ていただければと。
エラーオブジェクトについて
特に複雑なものではなく、以下の情報がエラーがあった際に格納される感じです。
エラーメッセージ等は、error_keysから作成することができると思います。例えばこんな感じです。
package Aplon::Error;
use Mouse;
has 'code' => ( is => 'rw' , default => 'ERROR' );
has 'missing' => (is => 'rw', default => sub { [] } );
has 'invalid' => (is => 'rw', default => sub { {} } );
has 'valid' => (is => 'rw', default => sub { {} } );
has 'custom_invalid' => (is => 'rw', default => sub { [] } );
has 'error_keys' => ( is => 'rw' , default => sub { [] } );
__PACKAGE__->meta->make_immutable();
no Mouse;
1;
CRUDを実装する
実装してないです。lookup/search/remove/add/update 等をMixInするだけで利用できる感じの実装とかです。
全員に汎用的なのを作るのは難しいので、Project毎や、ORM毎等に作るかたちになります。
汎用的なロジックを作る
作ってないんですが、Aplon::Model::Twitter::OAuth, Aplon::Model::OpenID 等認証部分が特に汎用的に作りやすいきがします。
Appホームディレクトリ取得モジュールApp::Home
Bloged at : 2011-01-24 23:01:52
アプリケーション作成時には、システムの配備情報を扱うために、アプリケーションホームディレクトリを取得する必要が、必ずでてきます。そのソースをフレームワークを作る毎に作成するのがめんどくさいので App::Home というのをさっき暇つぶしに作成し,githubに上げました。もしかしたらすでにそういうモジュールがあるのかもしれません。
まず継承する
利用する。get()メソッドしか使い道はないです。Path::Class::Dir のobjectを返します。また、シングルトンで実装されているので、一度呼べば後速いですし、同じ値を返すことを約束しています。
Homeディレクトリ取得ロジックは、ikebeさんの作成したPicklesのPickles::Configのhomeを見つけるロジックがいい感じなので、そこからソースを盗んで実装しています。環境変数を探して見つからなければ、アプリケーション::Homeライブラリの配備位置から取得する感じになっています。
homeディレクトリを見つけるという単純なことなのですが、フレームワークを作成する際には必ず必要になる重要な機能です。お家は大切なのでたまには親孝行しようということでした。
利用方法
まず継承する
package MyApp::Home; use parent 'App::Home'; 1;
利用する。get()メソッドしか使い道はないです。Path::Class::Dir のobjectを返します。また、シングルトンで実装されているので、一度呼べば後速いですし、同じ値を返すことを約束しています。
my $home = MyApp::Home->get();
Home発見ロジック
Homeディレクトリ取得ロジックは、ikebeさんの作成したPicklesのPickles::Configのhomeを見つけるロジックがいい感じなので、そこからソースを盗んで実装しています。環境変数を探して見つからなければ、アプリケーション::Homeライブラリの配備位置から取得する感じになっています。
あとがき
homeディレクトリを見つけるという単純なことなのですが、フレームワークを作成する際には必ず必要になる重要な機能です。お家は大切なのでたまには親孝行しようということでした。