2013年02月18日

I wrote too safety exception handling module.
https://metacpan.org/release/Try-Lite

$@ の処理を安全に取り扱う例外処理モジュールとしては Try::Tiny があまりにも有名ですが、別の実装を作ってみました。

アプリケーションを作る時は、よく例外クラスを作ってから die bless {}, $foo_class; みたいな形のオブジェクトの例外を投げて、上流の方で eval で受け取ってから $@ の isa を調べて例外に応じた処理をする事が多いと思います。
ここで問題になるのが、受け取った例外の中から不要な物を、さらに上位に向けて rethrow するわけですが、 rethrow するのを忘れてしまうとクリティカルなバグを発生させる要因となります。

また普通の言語で良くあるような try {} catch (e = IOException) {} catch (e = PermissionException) {} のようなに、例外クラス毎の処理を可読性良く書きたかったいという問題もありました。当然 TryCatch.pm のような、そのものの文法を提供してくれるモジュールもあるのですが、あくまで黒魔術的な実装ではなくて、メンテナンスビリティの事を考えて誰が見てもわかる実装で用意したいという問題もありました。

そして、それらの問題を解決する手段として Try::Lite を書いたのです。

使い方は簡単で

use Try::Lite;

try {
    ...;
    IOException->throw('なんかメッセージ');
} (
    'IOException' => sub {
        say $@;
    }
);
の用に、最初に例外が発生するかもしれないコードブロックを書いてから、その後ろの引き数に受け取りたいクラス名と処理したい関数を渡してあげます。

ISA を見ているので、例外したオブジェクトが継承しているクラスを指定していてもキャッチ可能です。

package Exception;
use parent 'Exception::Tiny';
package IOException;
use parent --norequire, 'Exception';
package main;

try {
    ...;
    IOException->throw('なんかメッセージ');
} (
    'Exception' => sub {
        say $@;
    }
);

クラス名の変わりに * がワイルドカードとして指定出来ます。この場合は die 'aaa' 等の普通の文字列な例外も補足して処理可能になります。

try {
    ...;
    die 'foo';
} (
    '*' => sub {
        say $@;
    }
);

Exception::Tiny との組み合わせ

Try::Lite は blessed な $@ を発生させる例外のコードだったらどんな例外クラスを使ってても良いんですが、 Exception::Tiny との組み合わせ例も書いておきます。

# user table を引いて、レコードが無ければ 404 を返して、それ以外の例外は上流に rethrow させる
package Exception;
use parent 'Exception::Tiny';

...

my $user = try {
    Exception->throw if $error;
    get_user( user_id => $user_id );
} ( 'Exception' => sub { $@ } );
if (Exception->caught($user) {
    return $c->res_404;
}

以下のコードでも同じ事出来るし依存するモジュール減るのですが、 Try::Lite 使っとけば不意の retrhow 漏れが防げるので安全側に実装できるかな。と思います。
my $user = eval {
    Exception->throw if $error;
    get_user( user_id => $user_id );
};
if (my $e = $@) {
    if (Exception->caught($e) {
        return $c->res_404;
    }
    die $e;
}

Posted by Yappo at 2013年02月18日 18:06 | TrackBack | Perl
Comments
Post a comment









Remember personal info?






コメントを投稿する前に↓の場所にnospamと入力してください。