custom::failures(3pm)

πŸ“¦
version 0.004 Β· 4 versions
, in debian bookworm, debian trixie, ubuntu noble, ubuntu resolute, ubuntu stonking

πŸ’– custom::failures β€” the manual, but make it make sense πŸ’–

okay bestie, so this is custom::failures, a page from the User Contributed Perl Documentation section of the manual (that's the 3pm bit, iykyk). buckle up, it's giving exception-hierarchy-glamour ✨


🌸 NAME 🌸

custom::failures β€” a minimalist, customized exception hierarchy generator. that's the whole vibe. tiny module, big energy. πŸ’…


⭐ VERSION ⭐

version 0.004 β€” yes, she's early-days, and yes, she still ate. πŸŽ€


πŸ¦‹ SYNOPSIS πŸ¦‹

here's the shape of it, no cap:

package MyApp::failure;

use custom::failures qw/io::file io::network/;

# customize failure methods…

that's it. that's the whole intro. now let's get into the lore. πŸ’–


🌈 DESCRIPTION 🌈

so this module works like failures, except β€” and this is the main character moment β€” it lets you define a customized exception hierarchy whenever you need a custom namespace, extra attributes, or just some customized object behaviors. πŸ‘‘

here's the tea on why that matters: because failure classes have an @ISA 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 the default "failure" behaviors. so you can just... slide your own version in. iconic. ✨

there are two methods that might be useful to override:

  • message
  • throw

both of them get described further, below. keep scrolling bestie 🫢


πŸŽ€ USAGE πŸŽ€

πŸ’œ Defining a custom failure hierarchy πŸ’œ

package MyApp::failure;

use custom::failures qw/foo::bar/;

this will define a failure class hierarchy under the calling package's namespace. the following diagram shows the classes that will be created β€” and the arrows denote 'is-a' relationships, so read them like "this one is a that one":

MyApp::failure::foo::bar --> failure::foo::bar
       |                        |
       V                        V
MyApp::failure::foo      --> failure::foo
       |                        |
       V                        V
MyApp::failure           --> failure

aww look at them, all linked up. family. πŸ₯ΉπŸ’•

alternatively β€” if you want a different namespace for the hierarchy β€” do it this way:

use custom::failures 'MyApp::Error' => [ 'foo::bar' ];

that will create the following classes and relationships:

MyApp::Error::foo::bar --> failure::foo::bar
       |                        |
       V                        V
MyApp::Error::foo      --> failure::foo
       |                        |
       V                        V
MyApp::Error           --> failure

and here's the slay part: 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. dual citizenship, honestly. πŸ›‚βœ¨

use Safe::Isa; # for $_isa
try {
    MyApp::failure::foo::bar->throw;
}
catch {
    if ( $_->$_isa( "failure::foo" ) ) {
        # handle it here
    }
};

πŸ“ Adding custom attributes πŸ“

failure classes are implemented with Class::Tiny, so adding attributes is trivially easy β€” like, embarrassingly easy:

package MyApp::failure;

use custom::failures qw/foo::bar/;

use Class::Tiny qw/user/;

this adds a "user" attribute to "MyApp::failure" and all its subclasses, so it can be set in the argument to "throw":

MyApp::failure::foo->throw( { msg => "Ouch!", user => "me" } );
🚨 important, bestie: be sure to load "Class::Tiny" after you load "custom::failures" so that your @ISA is already set up. order matters here fr fr. don't do it backwards, it will not go well 😭

🧚 Overriding the "message" method 🧚

overriding "message" lets you modify how the error string is produced. the "message" method takes a string (typically just the "msg" field) and returns a string. it should not produce or append stack trace information β€” that is done during object stringification, so stay in your lane, queen. πŸ’…

call "SUPER::message" if you want the standard error text prepended ("Caught $class: ...").

for example, if you want to use String::Flogger to render messages:

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) );
}

then you can pass strings or array references or code references as the "msg" for "throw":

MyApp::failure->throw( "just a string"               );
MyApp::failure->throw( [ "show some data %s", $ref ] );
MyApp::failure->throw( sub { call_expensive_sub() }  );

because the "message" method is only called during stringification (unless you call it yourself, miss independent πŸ™ˆ), the failure class type can be checked before any expensive rendering is done. lazy in the best way possible. πŸ’…βœ¨


πŸ”₯ Overriding the "throw" method πŸ”₯

overriding "throw" lets you modify the arguments you can provide, or just ensure that a trace is included. it can take whatever arguments you want and should call "SUPER::throw" with a hash reference to actually throw the error.

for example, to capture the filename associated with file errors:

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 'msg' and 'filename'
}

later you could use it like this:

MyApp::failure::file->throw( opening => $some_file );

chef's kiss, honestly πŸ‘¨β€πŸ³πŸ’‹


🌟 Using BUILD 🌟

"Class::Tiny" supports "BUILD", so you can also use that to do things with failure objects when thrown. this example logs exceptions as they are built:

use Log::Any qw/$log/;

sub BUILD {
    my ($self) = @_;
    $log->error( $self->message );
}

by using "message" instead of stringifying $self, we log the message but not the trace (if any). smart, tidy, no messy trace crumbs everywhere. πŸ§ΉπŸ’–


πŸ‘‘ AUTHOR πŸ‘‘

David Golden <dagolden@cpan.org> β€” ty for your service, king πŸ«‘πŸ’–


this software is Copyright (c) 2013 by David Golden.

this is free software, licensed under:

The Apache License, Version 2.0, January 2004

and that's the whole page, bestie!! πŸ’• this doc came from perl v5.36.0 and it's dated 2022-12-07, and the page itself is custom::failures(3pm) β€” now go forth and throw responsibly βœ¨πŸ¦‹πŸŽ€

πŸ“¦ where this page comes from