我似乎在寻找 4 件事。
- 声明异常类型的简单语法。
- 实现 Throwable 的异常。
- 异常对象中的额外功能,例如标签、自定义属性以及将属性传递给构造函数的简化。
- 通过类(全局可用)而不是函数(必须导入到每个模块中)来实例化异常。
Like Exception::Class https://metacpan.org/pod/Exception::Class, Throwable::Factory https://metacpan.org/pod/Throwable::Factory and Throwable::SugarFactory https://metacpan.org/pod/Throwable::SugarFactory提供了用于声明异常类型的简洁语法,但事实证明我可以没有它。Throwable::Factory https://metacpan.org/pod/Throwable::Factory事实上,它拥有我想要的一切,除了异常函数必须在它们使用的同一个文件中声明。它们是一种可抛出-可抛出的异常。我不想要这样。
中的一些额外功能Throwable::Factory https://metacpan.org/pod/Throwable::Factory异常来自 Throwable::Error,它是Throwable https://metacpan.org/pod/Throwable分配。其余的很容易被盗。 Throwable::Error 实际上是一个Moo https://metacpan.org/pod/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" ;
}