我似乎在寻找 4 样东西。
- 声明异常类型的简单语法。
- 实现 Throwable 的异常。
- 异常对象中的额外功能,例如标签、自定义属性以及将属性传递给构造函数的简化。
- 通过类(全局可用)而不是函数(必须将其导入每个模块)来实例化异常。
像Exception::Class、Throwable::Factory 和Throwable::SugarFactory 一样,提供了一种用于声明异常类型的简明语法,但事实证明我可以不用它。 Throwable::Factory 实际上拥有我想要的一切,除了必须在它们使用的同一个文件中声明异常函数。它们是一种可抛弃的可抛出异常。我不想要那个。
Throwable::Factory 异常中的一些额外功能来自 Throwable::Error,它是 Throwable 分发的一部分。其余的很容易窃取。 Throwable::Error 实际上是一个Moo 类,所以我们有一个赢家。
我可以将所有异常类放在一个文件中,然后通过应用顶部的use 加载它。异常层次结构继承自 Throwable::Error 作为基类。因为这些是 Moo 类,所以将自定义访问器添加到特定类是微不足道的。我可以从 Throwable::Factory 中剪切/粘贴我喜欢的额外功能。
package MyApp::Exceptions ;
use strict ;
use warnings ;
use Throwable::Error ;
use Types::Standard qw( Str ) ;
use Moo ;
use namespace::clean ;
use feature qw(signatures) ;
no warnings qw(experimental::signatures) ;
extends 'Throwable::Error' ;
with 'Role::Identifiable::HasTags' ;
has description => (
is => 'ro',
isa => Str,
required => 1,
default => 'Generic exception',
) ;
# stack_trace() and message() inherited from Throwable::Error
sub error ($self) { $self->message }
sub package ($self) { $self->stack_trace->frame(0)->package }
sub file ($self) { $self->stack_trace->frame(0)->filename }
sub line ($self) { $self->stack_trace->frame(0)->line }
# sugar for ::HasTags
sub has_tags ( $self, @wanted ) {
$self->has_tag($_) || return 0 for @wanted ;
return 1 ;
}
# support shorthand instantiation eg Foo->throw($message, attr => $val);
around BUILDARGS => sub {
my ( $orig, $class, @args ) = @_ ;
return +{} unless @args ;
return $class->$orig(@args) if @args == 1 ;
unshift @args, 'message' if @args % 2 ;
return $class->$orig( {@args} ) ;
} ;
# ----- enduser exception classes -----
package SystemError ;
use Types::Standard qw( Int ) ;
use Moo ;
extends 'MyApp::Exceptions' ;
has code => ( is => 'ro', isa => Int->where('$_ >= 0'), default => 1 ) ;
has '+description' => ( default => 'A system error' ) ;
package FileError ;
use Types::Standard qw( InstanceOf ) ;
use Moo ;
extends 'SystemError' ;
has '+code' => ( default => 2 ) ;
has '+description' => ( default => 'A file error' ) ;
has file => ( is => 'ro', required => 1, isa => InstanceOf['Path::Tiny'] ) ;
1 ;
只要在某个地方,我说过use MyApp::Exceptions;,现在我可以在任何地方说:
use Nice::Try ;
try {
something() or SystemError->throw("Problem trying to do something",
code => 7,
tags => [qw(something broke)],
) ;
}
catch ( SystemError $e where { $_->has_tags(qw(something broke)) }) {
fix_it($e) ;
}
catch ( SystemError $e where { $_->has_tag('something') }) {
repair_it($e) ;
}
catch ( FileError $e ) {
warn sprintf "Problem doing something() with file %s: %s",
$e->file->basename, $e->message ;
}
catch ( $e ) {
die "Give up! $e" ;
}