我需要寻找以下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',
) ;
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 }
sub has_tags ( $self, @wanted ) {
$self->has_tag($_) || return 0 for @wanted ;
return 1 ;
}
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} ) ;
} ;
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" ;
}