.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.43) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "custom::failures 3pm" .TH custom::failures 3pm "2022-12-07" "perl v5.36.0" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" custom::failures \- Minimalist, customized exception hierarchy generator .SH "VERSION" .IX Header "VERSION" version 0.004 .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& package MyApp::failure; \& \& use custom::failures qw/io::file io::network/; \& \& # customize failure methods… .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" This module works like failures but lets you define a customized exception hierarchy if you need a custom namespace, additional attributes, or customized object behaviors. .PP Because failure classes have an \f(CW@ISA\fR chain and Perl by default uses depth-first-search to resolve method calls, you can override behavior anywhere in the custom hierarchy and it will take precedence over default \f(CW\*(C`failure\*(C'\fR behaviors. .PP There are two methods that might be useful to override: .IP "\(bu" 4 message .IP "\(bu" 4 throw .PP Both are described further, below. .SH "USAGE" .IX Header "USAGE" .SS "Defining a custom failure hierarchy" .IX Subsection "Defining a custom failure hierarchy" .Vb 1 \& package MyApp::failure; \& \& use custom::failures qw/foo::bar/; .Ve .PP This will define a failure class hierarchy under the calling package's namespace. The following diagram show the classes that will be created (arrows denote 'is\-a' relationships): .PP .Vb 7 \& MyApp::failure::foo::bar \-\-> failure::foo::bar \& | | \& V V \& MyApp::failure::foo \-\-> failure::foo \& | | \& V V \& MyApp::failure \-\-> failure .Ve .PP Alternatively, if you want a different namespace for the hierarchy, do it this way: .PP .Vb 1 \& use custom::failures \*(AqMyApp::Error\*(Aq => [ \*(Aqfoo::bar\*(Aq ]; .Ve .PP That will create the following classes and relationships: .PP .Vb 7 \& MyApp::Error::foo::bar \-\-> failure::foo::bar \& | | \& V V \& MyApp::Error::foo \-\-> failure::foo \& | | \& V V \& MyApp::Error \-\-> failure .Ve .PP By having custom classes also inherit from a standard namespace, you can throw a custom error class that will still be caught in the standard namespace: .PP .Vb 9 \& use Safe::Isa; # for $_isa \& try { \& MyApp::failure::foo::bar\->throw; \& } \& catch { \& if ( $_\->$_isa( "failure::foo" ) ) { \& # handle it here \& } \& }; .Ve .SS "Adding custom attributes" .IX Subsection "Adding custom attributes" Failure classes are implemented with Class::Tiny, so adding attributes is trivially easy: .PP .Vb 1 \& package MyApp::failure; \& \& use custom::failures qw/foo::bar/; \& \& use Class::Tiny qw/user/; .Ve .PP This adds a \f(CW\*(C`user\*(C'\fR attribute to \f(CW\*(C`MyApp::failure\*(C'\fR and all its subclasses so it can be set in the argument to \f(CW\*(C`throw\*(C'\fR: .PP .Vb 1 \& MyApp::failure::foo\->throw( { msg => "Ouch!", user => "me" } ); .Ve .PP Be sure to load \f(CW\*(C`Class::Tiny\*(C'\fR \fBafter\fR you load \f(CW\*(C`custom::failures\*(C'\fR so that your \f(CW@ISA\fR is already set up. .ie n .SS "Overriding the ""message"" method" .el .SS "Overriding the \f(CWmessage\fP method" .IX Subsection "Overriding the message method" Overriding \f(CW\*(C`message\*(C'\fR lets you modify how the error string is produced. The \f(CW\*(C`message\*(C'\fR method takes a string (typically just the \f(CW\*(C`msg\*(C'\fR field) and returns a string. It should not produce or append stack trace information. That is done during object stringification. .PP Call \f(CW\*(C`SUPER::message\*(C'\fR if you want the standard error text prepended (\f(CW"Caught $class: ..."\fR). .PP For example, if you want to use String::Flogger to render messages: .PP .Vb 1 \& package MyApp::failure; \& \& use custom::failures qw/foo::bar/; \& use String::Flogger qw/flog/; \& \& sub message { \& my ( $self, $msg ) = @_; \& return $self\->SUPER::message( flog($msg) ); \& } .Ve .PP Then you can pass strings or array references or code references as the \f(CW\*(C`msg\*(C'\fR for \f(CW\*(C`throw\*(C'\fR: .PP .Vb 3 \& MyApp::failure\->throw( "just a string" ); \& MyApp::failure\->throw( [ "show some data %s", $ref ] ); \& MyApp::failure\->throw( sub { call_expensive_sub() } ); .Ve .PP Because the \f(CW\*(C`message\*(C'\fR method is only called during stringification (unless you call it yourself), the failure class type can be checked before any expensive rendering is done. .ie n .SS "Overriding the ""throw"" method" .el .SS "Overriding the \f(CWthrow\fP method" .IX Subsection "Overriding the throw method" Overriding \f(CW\*(C`throw\*(C'\fR lets you modify the arguments you can provide or ensure that a trace is included. It can take whatever arguments you want and should call \f(CW\*(C`SUPER::throw\*(C'\fR with a hash reference to actually throw the error. .PP For example, to capture the filename associated with file errors: .PP .Vb 1 \& package MyApp::failure; \& \& use custom::failures qw/file/; \& \& use Class::Tiny qw/filename/; \& \& sub throw { \& my ( $class, $msg, $file ) = @_; \& my $args = { \& msg => $msg, \& filename => $file, \& trace => failures\->croak_trace, \& }; \& $self\->SUPER::throw( $args ); \& } \& \& sub message { \& # do something with \*(Aqmsg\*(Aq and \*(Aqfilename\*(Aq \& } .Ve .PP Later you could use it like this: .PP .Vb 1 \& MyApp::failure::file\->throw( opening => $some_file ); .Ve .SS "Using \s-1BUILD\s0" .IX Subsection "Using BUILD" \&\f(CW\*(C`Class::Tiny\*(C'\fR supports \f(CW\*(C`BUILD\*(C'\fR, so you can also use that to do things with failure objects when thrown. This example logs exceptions as they are built: .PP .Vb 1 \& use Log::Any qw/$log/; \& \& sub BUILD { \& my ($self) = @_; \& $log\->error( $self\->message ); \& } .Ve .PP By using \f(CW\*(C`message\*(C'\fR instead of stringifying \f(CW$self\fR, we log the message but not the trace (if any). .SH "AUTHOR" .IX Header "AUTHOR" David Golden .SH "COPYRIGHT AND LICENSE" .IX Header "COPYRIGHT AND LICENSE" This software is Copyright (c) 2013 by David Golden. .PP This is free software, licensed under: .PP .Vb 1 \& The Apache License, Version 2.0, January 2004 .Ve