суббота, 19 мая 2012 г.

Try::Tiny и диспетчеризация исключений

Проблема 
Многие используют Try::Tiny. Все в этом модуле хорошо, только не хватает вот чего-то вроде:
try {
   die Class1->new();
}
catch('Class1') {
   # Catch all exceptions of Class1
}
catch('Class2') {
   # Catch all exceptions of Class2
}
catch {
   # Catch other exceptions
}
К сожалению, сделать такой синтаксис, не убрав "::Tiny" с названия модуля, не так просто. Кроме того, переписывать Try::Tiny вообще нет никакого желания.

Решение 1
Немного подумав я пришел к такому варианту:

try {
   die Class1->new();
}
catch {
    when ('Class1') {
        # Catch all exceptions of Class1     
    }
    when ('Class2') {
        # Catch all exceptions of Class1
    }
    default {
       # Catch other exceptions        
    }
}
Чтобы такой синтаксис заработал необходимо перегрузить "~~" ("Smart match") для ваших объектов исключений. Вот пример реализации
package Exception::Base;
use overload
'~~' => sub { $_[0]->isa( $_[1] ) },
fallback => 1;
package Exception1;
use base 'Exception::Base';
sub new { bless {}, shift }
package Exception2;
use base 'Exception::Base';
sub new { bless {}, shift }
package Exception3;
use base 'Exception::Base';
sub new { bless {}, shift }
package main;
use Try::Tiny;
use v5.10;
test_exception( Exception1->new() );
test_exception( Exception2->new() );
test_exception( Exception3->new() );
test_exception("Special text\n");
test_exception("Text exception\n");
sub test_exception {
my $e = shift;
try {
die $e;
}
catch {
when ('Exception1') {
say ref($_) . " matches Exception1";
}
when ('Exception2') {
say ref($_) . " matches Exception2";
}
when ('Exception::Base') {
say ref($_) . " matches Exception::Base";
}
when (/Special/) {
chomp;
say qq{"$_" matches qr/Special/};
}
default {
chomp;
say qq{"$_" matches default};
}
};
}

Решение 2
Если нет возможности перегрузить "~~", то можно пойти другим путем, добавить некий хелпер, например "e"
# ...
test_exception( Exception1->new() );
test_exception( Exception2->new() );
test_exception( Exception3->new() );
test_exception("Special text\n");
test_exception("Text exception\n");
use Scalar::Util qw/blessed/;
sub e($) {
my $class = shift;
my $e = $_;
return unless blessed($e);
return $e->isa($class);
}
sub test_exception {
my $e = shift;
try {
die $e;
}
catch {
when ( e 'Exception1') {
say ref($_) . " matches Exception1";
}
when ( e 'Exception2' ) {
say ref($_) . " matches Exception2";
}
when ( e 'Exception::Base' ) {
say ref($_) . " matches Exception::Base";
}
when (/Special/) {
chomp;
say qq{"$_" matches qr/Special/};
}
default {
chomp;
say qq{"$_" matches default};
}
};
}
Важно!!! Нужно помнить, что код после блока "default" не будет выполнен!

0 коммент.:

Отправить комментарий

Не забудьте добавить себя в постоянные читатели и включить уведомления о новых комментариях, либо воспользуйтесь RSS каналом ;)