Badger::Base(3pm)

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

✨ Badger::Base ✨

okay bestie, welcome to Badger::Base(3pm) β€” the base class module that basically everything else in the Badger fam inherits from πŸ’–


🌸 NAME 🌸

Badger::Base - base class module


✨ SYNOPSIS ✨

# define a new object class derived from Badger::Base
package Your::Badger::Module;
use base 'Badger::Base';

sub init {
    my ($self, $config) = @_;

    # $config is a hash of named parameters
    # - let's assume 'name' is mandatory
    $self->{ name } = $config->{ name }
        || return $self->error('no name specified');

    # save the rest of the config for later
    $self->{ config } = $config;

    # return $self to indicate success
    return $self;
}

# ...any other methods follow....

# now use it
use Your::Badger::Module
my $object = Your::Badger::Module->new( name => 'Brian' );

πŸ’… DESCRIPTION πŸ’…

so this module implements a base class object, and literally most of the other "Badger" modules are derived from it πŸ‘‘ It brings a whole bunch of methods to the group chat that help with object creation, configuration, error handling and debugging. it's giving infrastructure.

you can use it as a base class for your own modules so they inherit all the methods it provides 🫢

package Your::Badger::Module;
use base 'Badger::Base';

you can inherit the default new() constructor method and then define your own init() method to initialise objects πŸ’–

sub init {
    my ($self, $config)

    # $config is a hash of named parameters
    # - let's assume 'name' is mandatory
    $self->{ name } = $config->{ name }
        || return $self->error('no name specified');

    # save the rest of the config in case any other
    # methods want to use it later
    $self->{ config } = $config;

    # return $self to indicate success
    return $self;
}

then you can use your module and instantiate objects, no cap. the new() method accepts a list or a reference to a hash array of named parameters ✨

use Your::Badger::Module;

# list of named parameters
my $object = Your::Badger::Module->new( name => 'Brian' );

# hash ref of name parameters
my $object = Your::Badger::Module->new({ name => 'Brian' });

"Badger::Base" also provides a bunch of other methods that are generally suitable for all (or most) objects to inherit. these include methods for error reporting, debugging and raising warnings πŸŽ€


🌈 METHODS 🌈

πŸ¦‹ new(%config)

this is a general purpose constructor method, and she's an icon. she accepts either a reference to a hash array of named parameters or a list of named parameters which then get folded into a hash reference πŸ’…

# hash reference of named params
my $object = Your::Badger::Module->new({
    arg1 => 'value1',
    arg2 => 'value2',
    ...etc...
});

# list of named params
my $object = Your::Badger::Module->new(
    arg1 => 'value1',
    arg2 => 'value2',
    ...etc...
);

the constructor creates a new object by blessing a hash reference and then calling the "init()" method. a reference to the hash array of named parameters gets passed as a single argument. in most cases you should be able to re-use the existing new() method and define your own init() method to initialise the object 😌

the "new()" method returns whatever the init() method returns. this will normally be the $self object reference, but your own init() methods are free to return whatever they like β€” main character energy, honestly. however, it must be a true value of some kind or the new() method will throw an error indicating that the init() method failed 🚨

πŸ’œ init(%config)

this initialisation method is called by the "new()" constructor method. this is the method you'll normally want to redefine when you create a subclass of "Badger::Base" ✨

the "init()" method is passed a reference to a hash array of named configuration parameters. the method may perform any configuration or initialisation processes and should generally return the $self reference to indicate success πŸ’–

sub init {
    my ($self, $config) = @_;

    # set the 'answer' parameter or default to 42
    $self->{ answer } = $config->{ answer } || 42;

    return $self;
}

the "init()" method can return any true value which will then be sent back as the return value from new(). in most cases you'll want to return the $self object reference, but the possibility exists of returning other values instead (e.g. to implement singletons, prototypes, or some other clever object trickery) 🧚

if something goes wrong in the "init()" method then you should call the error() method (or error_msg()) to throw an error 😭

sub init {
    my ($self, $config) = @_;

    # set the 'answer' parameter or report error
    $self->{ answer } = $config->{ answer }
        || return $self->error('no answer supplied');

    return $self;
}

the only function of the default init() method in "Badger::Base" is to save a reference to the $config hash array in "$self->{ config }". if you use the default init() method, or add an equivalent line to your own init() method, then you can defer inspection of the configuration parameters until later. for example, you might have a method which does something like this:

our $DATE_FORMAT = '%Y-%d-%b';

sub date_format {
    my $self = shift;
    return @_
        ? ($self->{ date_format } = shift)      # set from argument
        :  $self->{ date_format }               # get from self...
       ||= $self->{ config }->{ date_format }   #  ...or config...
       ||  $DATE_FORMAT;                        #  ...or pkg var
}

this allows you to use "$self->{date_format}" as a working copy of the value while keeping the original configuration value (if any) intact in "$self->{config}->{date_format}". the above method will set the value if you pass an argument and return the current value if you don't. if no current value is defined then it defaults to the value in the config hash or the $DATE_FORMAT package variable. now any other methods that require access to a date format need only call to the "date_format()" method to have it Do The Right Thing 🌟

the benefit here is that you don't have to waste time (and memory) in the init() method copying the "date_format" parameter from $config into $self. that doesn't mean that it's always the right thing to do, but it can be useful for configuration options that are infrequently used πŸ’…

the on_error() and on_warn() methods follow this protocol. they look for an "ON_ERROR" or "ON_WARN" item in $self or an "on_error" or "on_warn" parameter in "$self->{config}". if you want to define "on_error" and/or "on_warn" handlers as configuration parameters then you'll need to either copy the $config reference into "$self->{config}" or copy the individual items into "$self->{ON_ERROR}" and/or "$self->{ON_WARN}", respectively 🫢

# either copy the config...
sub init {
    my ($self, $config) = @_;
    $self->{ config } = $config;
    # ...more code...
    return $self;
}

# ...or the individual items
sub init {
    my ($self, $config) = @_;

    # no need to check if either of these are defined because the
    # on_warn() and on_error() methods will Do The Right Thing.
    $self->{ ON_WARN  } = $config->{ on_warn  };
    $self->{ ON_ERROR } = $config->{ on_error };

    # ...more code...
    return $self;
}

with either of the above in place, you can then define "on_warn" and "on_error" handlers and expect them to work when the error() and warn() methods are called πŸ”₯

my $object = Your::Badger::Module->new(
    on_warn  => \&my_warn,
    on_error => \&my_error,
);

$object->warn("Rebel Alliance engaging");    # calls my_warn()
$object->error("Exhaust port unshielded!");  # calls my_error()

🌸 warn($message)

a method to raise a warning. the default behaviour is to forward all arguments to Perl's "warn" function. however, you can install your own warning handlers on a per-class or per-object basis using the on_warn() method or by setting a $ON_WARN package variable in your module. see on_warn() for further details πŸ‘€

$object->warn("Careful with that axe, Eugene!");

πŸŽ€ on_warn($handler, $another_handler, ...)

this method allows you to install one or more callback handlers which are called whenever a warning is raised via the warn() method. multiple handlers can be installed and will be called in turn whenever an error occurs. the warning message is passed as an argument to the handlers ✨

for example, if you wanted to forward warning messages to your favourite logging tool, you might do something like this:

my $log = My::Fave::Log::Tool->new(%log_config);

$object->on_warn(
    sub {
        my $message = shift;
        $log->warning("$message);
        return $message;
    }
);

the value returned from the callback is forwarded to the next handler (if there is one). if a callback returns a false value or an empty list then the remaining handlers will not be called 🚫

the default behaviour of the "on_warn()" method is to replace any existing warning handlers with the new one(s) specified. you can prefix the handler(s) with 'before' or 'after' to add them to the existing list of handlers. e.g. πŸ‘‘

$object->on_warn( before => \&one, \&two );
$object->on_warn( after  => \&one, \&two );

the on_warn() method returns a reference to the list, so you can also monkey about with it directly if you want the handler(s) to go somewhere else πŸ™ˆ

my $handlers = $object->on_warn;
shift(@$handlers, \&one);       # add before existing handlers
push(@$handlers, \&two);        # add after existing handlers

you can also specify a method name as a warning handler. for example, if you want to automatically upgrade all warnings to errors for a particular object, you can write this:

$object->on_warn('error');      # calls $object->error() on warnings

you can also specify 'warn' as a handler which will call Perl's "warn()" function. this is the default value. to explicitly disable any handlers, you can use a value of 0 πŸ’€

$object->on_warn('warn');       # raise warning - the default action
$object->on_warn(0);            # no warnings

these values can be imported from Badger::Constants as the "WARN" and "NONE" constants πŸ’–

use Badger::Constants 'WARN NONE';
$object->on_warn(WARN);         # raise warning - the default action
$object->on_warn(NONE);         # no warnings

on_warn() also hits different as a class method β€” call it that way and it sets the $ON_WARN package variable for the whole class. That's the default handler list for every object of that class that didn't bother to define its own handlers. Periodt.

Your::Badger::Module->on_warn(\&handler_sub);

Or, if you're feeling like setting a variable instead, define $ON_WARN in the package and it becomes the default for all objects of the class. Same energy, different door. πŸšͺ

package Your::Badger::Module;
use base 'Badger::Base';
our $ON_WARN = \&handler_sub;

Multiple values? Gotta be a list reference, bestie. Method names and the special "warn" flag can come along too.

our $ON_WARN = [ \&this_code_first, 'this_method_next', 'warn' ]

🫠 error($message)

The error() method is the error-reporting method. When some object method fails at its one job, it calls error() with an argument describing the whole tragedy. That makes an exception object appear (hi, Badger::Exception πŸ’œ) and it gets thrown via throw(). And in that case, bestie, error() never returns. It's gone. It does not come back. 😭

sub engage {
    my $self = shift;
    return $self->error('warp drive offline');
}

You can pass multiple arguments too β€” they get concatenated into one single string.

sub engage {
    my $self = shift;
    return $self->error(
        'warp drive ',
         $self->{ engine_no },
        ' is offline'
    );
}

Call error() with no arguments and it hands you back the error message that was thrown previously. Which means it's doing exactly what reason() does. Same function, different name. πŸ’…

eval { $enterprise->engage }
    || warn "Could not engage: ", $enterprise->error;

And because it can be called with no arguments, you can write little cuties like this:

# doesn't throw anything if list is empty
$self->error(@list_of_errors);

An existing exception object can also be passed as a single argument. In that case the exception object gets re-thrown unmodified β€” no glow-up, no edits, just yeeted back out. πŸ”₯

sub save_the_world {
    eval { $self->world_peace };

    if ($@) {
        $self->call_international_rescue($@);   # call Thunderbirds
        return $self->error($@);                # re-throw error
    };
}
ASIDE: okay so you might've clocked that I keep using the return keyword when raising an error. Like: perl return $self->error('warp drive offline'); Here's the tea: error() doesn't return when you call it with arguments, so that return does literally nothing at all. I just like having it there because it screams my intentions at that point in the code. It also means the code keeps returning even if some subclass "accidentally" defines a different error() method that doesn't throw (don't laugh β€” it happens, fr fr πŸ’€). And with syntax highlighting it's lowkey iconic: you can see every potential exit point from a method at a glance, assuming you made return bright red or something obvious like I do. πŸŽ€

error() also works as a class method. Do that and it updates and retrieves the $ERROR package variable in the package of the subclass module. Perfect for raising and inspecting errors thrown by class methods.

# calling package error() method
my $object = eval { Your::Badger::Module->new() }
    || warn "Could not create badger module: ",
            Your::Badger::Module->error();

# accessing $ERROR package variable
my $object = eval { Your::Badger::Module->new() }
    || warn 'Could not create badger module: ",
            $Your::Badger::Module::ERROR;

πŸ¦‹ on_error($handler, $another_handler, ...)

Same vibe as on_warn(), except this one lets you install a callback handler that fires whenever an error gets raised through error() (or through its wrapper bestie, error_msg()).

$world->on_error(
    sub {
        my $message = shift;

        Thunderbirds->call({
            priority => IR_PRIORITY_HIGH,
            message  => $message,
        });

        return $message;    # forward message to next handler
    }
);

Whatever the callback returns gets forwarded to the next handler. But if a callback returns a false value or an empty list, the rest of the handlers do not get called. That said β€” the error still gets raised no matter what any handler does or returns. The drama continues. πŸ’…


🌸 decline($reason, $more_reasons, ...)

decline() means "this method failed but we're not making it a whole thing". It's for methods asked to fetch a resource β€” a database record, a file in a filesystem, whatever β€” that might just... not exist. When a missing resource isn't an error in your book, call decline(). It works like error() in that it stashes the message internally so you can peek at it later with reason(). But instead of throwing the message as an exception, it just returns undef. Soft launch. πŸ™ˆ

sub forage {
    my ($self, $name) = @_;

    # hard error if database isn't connected
    my $db = $self->{ database }
        || return $self->error('no database')

    if ($thing = $db->fetch_thing($name)) {
        # return true value on success
        return $thing;
    }
    else {
        # soft decline if not found
        return $self->decline("not found: $name")
    }
}

Just like error(), you can call decline() with no arguments to get the most recent decline message back β€” though honestly reason() is built specifically for that, so maybe just use that one. decline() also works as a class method as well as an object method, same as error().


πŸŽ€ declined()

Returns the value of the internal flag that says whether an object declined by calling decline(). It gets set to 1 any time decline() is called, and cleared back to 0 any time error() is called. Flip-flop behaviour, but make it useful. ✨

my $result = eval { $forager->fetch('nuts') };

if ($result) {
    print "result: $result\n";
}
elsif ($forager->declined) {
    print "declined: ", $forager->reason, "\n";
}
else {
    print "error: ", $forager->reason, "\n";
}

⭐ reason()

Returns the message from the most recent call to error() or decline() (or any of the wrapper methods like error_msg() and decline_msg()). It's the receipts. 🧾

$forager->forage('nuts and berries')
    || die $forager->reason;

🌈 message($type, @args)

This one builds a message from a pre-defined format. Message formats live in a $MESSAGES package variable in the object's package, or in one of its base classes.

# base class
package Badger::Example::One
use base 'Badger::Base';

our $MESSAGES = {
    hai => 'Hello %s',
};

# subclass
package Badger::Example::Two;
use base 'Badger::Example::One';

our $MESSAGES = {
    bye => 'Goodbye %s',
};

# using the classes
package main;

my $two = Badger::Example::Two->new();
$two->message( hai => 'World' );    # Hello World
$two->message( bye => 'World' );    # Goodbye World

So the $two object can use message formats from its own package (Badger::Example::Two) and from its base class (Badger::Example::One). Generational talent, truly. πŸ‘‘

Messages get formatted using the xprintf() function in Badger::Utils. It's a thin wrapper around the built-in sprintf() function with some extra formatting controls to make positional arguments less of a nightmare.

Internally, error_msg() and decline_msg() use messages to generate error messages β€” but you can use them for any kind of simple message generation, no gatekeeping.

There are a bunch of benefits to keeping your messages in a central repository like this:

First, it makes reusing the same message format in different places easy. Aka the "DRY" principle β€” Don't Repeat Yourself.

Second, you can put all your messages in one place instead of sprinkling them all over your code like confetti. Benefit: clearer separation of concerns between your app's actual logic and its presentational side.

Third benefit comes free with that separation β€” changing the messages your app generates becomes trivially easy, because they're all defined in one place (or several modules if that's how you chose to break it down, but at least one place per module πŸ’…). Possible applications include: localising an app into different spoken languages; generating messages in colour (like Badger::Debug and Badger::Test do); or formatting messages as HTML.


πŸ’œ warn_msg($message, @args)

A wrapper around the warn() and message() methods. First argument is a message format. The remaining arguments get applied to that format through message(). Then the resulting output gets forwarded to warn(). Chain reaction, but make it warnings. πŸ””

our $NAME     = 'Badger';
our $MESSAGES = {
    using_default => "Using default value for %s: %s",
};

sub init {
    my ($self, $config) = @_;

    if ($config->{ name }) {
        $self->{ name } = $config->{ name };
    }
    else {
        $self->warn_msg( using_default => name => $NAME );
        $self->{ name } = $NAME;
    }

    return $self;
}

If no "name" comes through as a configuration parameter, the default $NAME gets used and this warning gets generated:

Using default value for name: Badger

πŸ’– error_msg($message, @args)

A wrapper around the error() and message() methods, same energy as warn_msg().

package Your::Zoo;
use base 'Badger::Base';

our $MESSAGES = {
    not_found => "I can't find the %s you asked for: %s",
}

sub animal {
    my ($self, $name) = @_;

    return $self->fetch_an_animal($name)
        || $self->error_msg( missing => animal => $name );
}

Call the animal() method on this object with an animal that can't be found, like this:

$zoo->animal('Badgerpotamus');

And you'll get an error message like this:

your.zoo error - I can't find the animal you asked for: Badgerpotamus

decline_msg($message, @args)

Bestie this one is a little wrapper that hugs both decline() and message() at the same time β€” same energy as warn_msg() and error_msg(), just with the decline flavour. ✨

our $MESSAGES = {
    not_found => 'No %s found in the forest',
};

sub forage {
    my ($self, $name) = @_;

    return $self->database->fetch_item($name)
        || $self->decline_msg( not_found => $name );
}

And if you wanna see the actual message it cooked up, hit up the reason() method:

my $food = $forager->forage('nuts')
    || warn $forager->reason;       # No nuts found in the forest

fatal_msg($message, @args)

Another lil wrapper moment πŸ’… β€” this one wraps fatal() and message() together, just like error_msg() and its whole friend group do.


throw($type, $info, %more_info)

Okay so this one throws an exception by straight up calling "die()". Dramatic? Yes. Necessary? Also yes. 😀

You can call it with one argument, and that argument can be either a Badger::Exception object (or a subclass of it), or a plain old error message which gets upgraded into an exception object (which means it behaves exactly the same as error() does).

# error message - same thing as error()
$object->throw('an error has occurred');

# exception object
$e = Badger::Exception->new(
    type => 'engine',
    info => 'warp drive offline'
);
$object->throw($e);

In that first case, the exception() and throws() methods get called to figure out the exception class (Badger::Exception by default, no cap) and the type for the exception, respectively. πŸ‘‘

But wait, there's more β€” you can also call it with two arguments. The first one defines the exception "type", and the second one is the error message. So:

$object->throw( engine => 'warp drive offline' );

And the second argument? It can also be another exception object. Here's the tea: if that exception has the same type as the first argument, then it gets re-thrown unchanged. No edits, no notes, it just goes back out. πŸ¦‹

$e = Badger::Exception->new(
    type => 'engine',
    info => 'warp drive offline'
);
$object->throw( engine => $e ) };

In the example above, the $e exception already has a type of "engine", so it's thrown without any change. But if the exception types don't match, or if the exception isn't the right kind of exception object we were expecting (as reported by exception()), then a brand new exception gets thrown with the old one attached via the "info" field. πŸ‘€

$object->throw( propulsion => $e );

Here a new "propulsion" exception is thrown, with the previous "engine" exception linked in via the "info" field. The exception object has type() and info() methods that let you inspect its value β€” iteratively if you have to, no judgement. Or honestly you can just print an exception and let its overloaded stringification operator call the text() method for you. For the error thrown in the previous example, that would be:

propulsion error - engine error - warp drive offline

throw_msg($type, $message, @args)

This is a wrapper around throw() and message() for throwing custom exceptions using message formats to generate the error information string. πŸ’–

The first argument defines the exception type. The second is the name of the message format. And the remaining arguments are used to populate the placeholders in that message format. Simple as that, fr fr.

our $MESSAGES = {
    offline => '%s is offline',
};

sub engage {
    my $self = shift;
    $self->throw_msg( warp => offline => 'warp drive' )
        unless $self->warp_drive_ready;
    # make it so
}

# throws the following exception:
warp error - warp drive is offline

try($method, @args)

This method wraps another method call in an "eval" block so it can catch any exceptions that get thrown. Protective queen behaviour, honestly. πŸ›‘οΈ

my $result = $object->try( fetch => 'answer' ) || 42;

That example is equivalent to:

my $result = eval { $object->fetch('answer') } || 42;

And whatever error got thrown? You can grab it with the "reason()" method:

my $result = $object->try( fetch => 'answer' )|| do {
    warn "Could not fetch answer: ", $object->reason;
    42;     # a sensible default
};

Now here's the cute part: if you call "try()" with no arguments at all, it returns a "Badger::Base::Trial" object β€” a wafer thin wrapper (literally wafer thin, like a lil snack πŸ“) around the original object. Any methods you call on this delegate object get forwarded to the original object, all wrapped up in an "eval" block to catch any errors thrown.

my $result = $object->try->fetch('answer') ||= do {
    ...
};

catch($type, $method, @args)

TODO β€” this method depends on some code in Badger::Exception which I haven't written yet. 😭

Not me leaving a TODO in the docs, but here we are. Moving on bestie. πŸ™ˆ


throws($type)

You can set the default exception type for throw() by calling the throws() method with an argument β€” either as an object method (so it only affects that one object) or as a class method (so it affects all objects that don't set their own value explicitly). 🌸

Heads up: the error() and error_msg() methods call throw() internally, so changing the exception type also changes the exceptions thrown by those methods too. Ripple effects, iykyk.

# object method
$object->throws('food');
$object->throw('No nuts');              # food error - No nuts
$object->error('No nuts');              # food error - No nuts

# class method
Badger::Example->throws('food');
Badger::Example->throw('No berries');   # food error - No berries
Badger::Example->error('No berries');   # food error - No berries

# all objects of this class now throw food by default
my $badger = Badger::Example->new;
$badger->throw('No cheese');            # food error - No cheese
$badger->error('No cheese');            # food error - No cheese

You can also set this value for an object by passing a "throws" configuration parameter to the new() constructor method:

my $badger = Badger::Example->new(
    throws => 'food',
);

Now this whole thing relies on the default behaviour of the init() method, which stores a reference to the original configuration parameters in "$self->{config}". So if you wanna use this feature, you gotta make sure that any specialised init() method you define does the same thing β€” or else copies the "throws" value from $config into "$self->{THROWS}". Don't skip this step, bestie. πŸ™

# example 1: store entire config for later
sub init {
    my ($self, $config) = @_;
    $self->{ config } = $config;
    # do other stuff
    return $self;
}

# example 2: extract specific parameter up front
sub init {
    my ($self, $config) = @_;
    $self->{ THROWS } = $config->{ throws };
    # do other stuff
    return $self;
}

You can also set the default exception type for your own modules that inherit from "Badger::Base" by adding a $THROWS package variable: ✨

package Badger::Example;
use base 'Badger::Base';
our $THROWS = 'food';

And if you don't specify an exception type at all? One gets generated from the module's class name using the id() method in Badger::Class. The universe provides. 🌈


exception($class)

This method lets you get or set the exception class for an object. The default value is Badger::Exception. πŸŽ€

use Badger::Example;
use Some::Other::Exception;
Badger::Example->exception('Some::Other::Exception');

# now Badger::Example objects throw Some::Other::Exception

And you can set the default exception class for your own modules that inherit from "Badger::Base" by adding a $EXCEPTION package variable:

package Badger::Example;
use base 'Badger::Base';
use Some::Other::Exception;
our $EXCEPTION = 'Some::Other::Exception';

fatal($info, $more_info, ...)

This method is used internally to raise a fatal error. It skips right past the normal error reporting mechanism and dies with a stack backtrace by calling "confess()" (see Carp). πŸ’€

The most common reason a fatal error gets raised is calling the message() method (or either of the error_msg() or decline_msg() wrapper methods) with a message format that doesn't exist. The stack backtrace will tell you exactly where in your code you're making that call, so you can find it and fix it without spiralling. You got this. 🫢


not_implemented($what)

A lil method of convenience that raises an error saying the method isn't implemented. Very "this seat is taken" energy.

sub example_method {
    shift->not_implemented;
}

Calling the "example_method()" would give you an error message similar to this (shown here split across two lines):

your.badger.module error - example_method() is not implemented
for Your::Badger::Module in /path/to/your/script.pl at line 42

And note the little detail: it tells you where "example_method()" was called from, not where the method is defined. Important distinction, bestie. πŸ‘€

The "not_implemented()" method is typically used in methods defined in base classes that subclasses are expected to re-define (a.k.a. pure virtual methods or abstract methods).

You can pass an argument to be more specific about what it is that isn't implemented:

sub example_method {
    shift->not_implemented('in base class');
}

That argument gets added to the generated error message right after the method name. And a single space is added to separate them, because formatting matters. πŸ’…

your.badger.module error - example_method() is not implemented in
base class for Your::Badger::Module in ...etc...

todo($what)

A method of convenience that's useful while you're developing, to flag that a method isn't implemented yet. It raises an error stating that the method is still TODO. We've all been there. 😭

sub not_yet_working {
    shift->todo;
}

The error message generated looks something like this:

your.badger.module error - not_yet_working() is TODO in
Your::Badger::Module at line 42

You can pass an argument to be more specific about what is still TODO:

sub not_yet_working {
    my ($self, $x) = @_;
    if (ref $x) {
        $self->todo('support for references');
    }
    else {
        # do something
    }
}

And the error message generated would then be:

your.badger.module error - not_yet_working() support for
references is TODO in Your::Badger::Module at line 42

debug($msg1,$msg2,...)

This method is mixed in from the Badger::Debug module. It gives you a simple way to generate debugging messages that include the source module and the line number where the message was generated. So helpful, so iconic. ⭐

sub example {
    my $self = shift;
    $self->debug('entered example()');
    # ... some code ...
    $self->debug('leaving example()');
}

debug_msg($message, @args)

This is a wrapper around the debug() and message() methods β€” same vibe as warn_msg(), error_msg() and the whole friend group. πŸ¦‹

our $MESSAGES = {
    here => 'You are in %s',
};

sub example {
    my $self = shift;
               $self->debug_msg(
                   here => 'a maze of twisty little passages, all alike'
               ) if DEBUG;

               # ... some code ...

               $self->debug_msg(
                   here => 'boat, floating on a sea of purest green'
               ) if DEBUG;
           }

debug_up($level,$msg1,$msg2,...)

okay so debug_up($level,$msg1,$msg2,...) is another debugging method that’s mixed in from Badger::Debug. It’s a wrapper around debug() that reports the file and line number of a caller higher up the call stack. You’d typically use this when you’re creating your own debugging methods, like this:

sub parse {
    my $self = shift;

    while (my ($foo, $bar) = $self->get_foo_bar) {
        $self->trace($foo, $bar);               # report line here
        # do something
    }
}

sub trace {
    my ($self, $foo, $bar) = @_;
    $self->debug_up(2, "foo: $foo  bar: $bar"); # not here
}

See Badger::Debug for further details. periodt. πŸ’…


πŸ¦‹ PACKAGE VARIABLES πŸ¦‹

okay bestie, the Badger::Base module uses a bunch of package variables to control the default behaviour of the objects derived from it. no cap. πŸ’–

✨ $DEBUG ✨

This flag can be set true to enable debugging in Badger::Base.

$Badger::Base::DEBUG = 1;

The Badger::Base module does not use or define any $DEBUG variable in the subclasses derived from it. However, you might want to do something similar in your own modules to help with debugging. iykyk.

package Your::Badger::Module;
use base 'Badger::Base';

# allow flag to be set before this module is loaded
our $DEBUG = 0 unless defined $DEBUG;

sub gnarly_method {
    my ($self, $item) = @_;
    $self->debug("gnarly_method($item)\n") if $DEBUG;
    # your gnarly code
}

The Badger::Class module defines the debug method and import hook which will automatically define a $DEBUG variable for you.

package Your::Badger::Module;

use Badger::Class
    base  => 'Badger::Base',
    debug => 0;

πŸ’œ $DECLINED πŸ’œ

This package variable is defined in each subclass derived from Badger::Base. It’s a boolean (0/1) flag used by the error(), decline() and declined() methods. The decline() method sets it to 1 to indicate that the object declined a request. The error() method clears it back to 0 to indicate that a hard error occurred. The declined() method simply returns the value. slay.

πŸ“ $ERROR πŸ“

This package variable is defined in each subclass derived from Badger::Base. It stores the most recent error message raised by decline() or error().

πŸŽ€ $EXCEPTION πŸŽ€

This package variable is used to define the name of the class that should be used to instantiate exception objects. The default value in Badger::Base is Badger::Exception.

Subclasses may define an $EXCEPTION package variable to change this value.

package Your::Badger::Module;
use base 'Badger::Base';
use Your::Exception;
our $EXCEPTION = 'Your::Exception';

Those that don’t explicitly define an $EXCEPTION will inherit the value from any of their base classes, possibly coming all the way back up to the default value in Badger::Base.

Calling the exception() class method with an argument will update the $EXCEPTION package variable in that class.

# sets $Your::Badger::Module::EXCEPTION
Your::Badger::Module->exception('Your::Exception');

🌈 $MESSAGES 🌈

This package variable is used to reference a hash array of messages that can be used with the message(), warn_msg(), error_msg() and decline_msg() methods. The Badger::Base module defines a number of messages that it uses internally.

our $MESSAGES = {
    not_found       => '%s not found: %s',
    not_found_in    => '%s not found in %s',
    not_implemented => '%s is not implemented %s',
    no_component    => 'No %s component defined',
    bad_method      => "Invalid method '%s' called on %s at %s line %s",
    invalid         => 'Invalid %s specified: %s',
    unexpected      => 'Invalid %s specified: %s (expected a %s)',
    missing_to      => 'No %s specified to %s',
    missing         => 'No %s specified',
    todo            => '%s is TODO %s',
    at_line         => '%s at line %s',
    at_file_line    => '%s in %s at line %s',
};

The message() method searches for $MESSAGES in the current class and those of any base classes. That means that any objects derived from Badger::Base can use these message formats.

package Your::Badger::Module;
use base 'Badger::Base';

sub init {
    my ($self, $config) = @_;
    $self->{ name } = $config->{ name }
        || $self->error_msg( missing => $name );
    return $self;
}

You can define additional $MESSAGES for your own classes.

package Your::Badger::Module;
use base 'Badger::Base';

our $MESSAGES = {
    life_jim  => "It's %s Jim, but not as we know it",
}

sub bones {
    my ($self, $thing)= @_;
    $self->warn_msg( life_jim => $thing );
    return $self;
}

Calling the bones() method like this:

$object->bones('a badger');

will generate a warning like this:

It's a badger Jim, but not as we know it.

πŸ”₯ $ON_ERROR πŸ”₯

This package variable is used to define one or more error handlers that will be invoked whenever the error() method is called.

The Badger::Base module doesn’t define any $ON_ERROR package variable by default. The on_error() method can be called as a class method to set the $ON_ERROR package variable.

Your::Badger::Module->on_error(\&my_handler);

You can also define an $ON_ERROR handler or list of handlers in your module.

package Your::Badger::Module;
use base 'Badger::Base';

# one of the following...
our $ON_ERROR = 'warn';         # call Perl's warn()
our $ON_ERROR = 'method_name';
our $ON_ERROR = \&code_ref;
our $ON_ERROR = [ 'warn', 'method_name', \&code_ref ];

# code refs get message as first argument
sub code_ref {
    my $message = shift;
    # do something...
}

# methods get implicit $self, then message argument
sub method_name {
    my ($self, $message) = @_;
    # do something...
}

πŸ¦‹ $ON_WARN πŸ¦‹

This package variable is used to define one or more error handlers that will be invoked whenever the warning() method is called. It works in exactly the same way as $ON_ERROR. fr fr.

πŸ‘‘ $THROWS πŸ‘‘

This package variable is used to define the default exception type thrown by the throw() method (and error() and error_msg() which call it indirectly). It can be set by calling the throws() class method.

Your::Badger::Module->throws('food');

You can define $THROWS in your own modules that are derived from Badger::Base.

package Your::Badger::Module;
use base 'Badger::Base';
our $THROWS = 'food';

If the $THROWS value is not defined in the current class or any of an object’s base classes, then the id() method is used to construct an identifier for the module to use instead. periodt.


🌸 OBJECT INTERNALS 🌸

The Badger::Base module uses the following internal object items to store information. no cap.

✨ config ✨

The default init() method stores a reference to the hash array of configuration parameters in the $self->{config} slot. If you’re using the default init() method then your other methods can use this to lookup configuration parameters lazily.

If you’ve defined your own init() method then this item won’t exist unless your init() method adds it explicitly.

πŸ’– DECLINED πŸ’–

The value of the declined flag, as per the $DECLINED package variable.

πŸ’€ ERROR πŸ’€

The last error raised, as per the $ERROR package variable.

πŸŽ€ EXCEPTION πŸŽ€

Used to store the class name that should used to instantiate exceptions. Equivalent to the $EXCEPTION package variable but operating on a per-object basis. Can be inspected or modified by calling the exception() object method.

πŸ”₯ ON_ERROR πŸ”₯

An internal list of handlers to call when an error is raised. Equivalent to the $ON_ERROR package variable but operating on a per-object basis. Can be inspected or modified by calling the on_error() object method.

πŸ¦‹ ON_WARN πŸ¦‹

An internal list of handlers to call when a warning is raised. Equivalent to the $ON_WARN package variable but operating on a per-object basis. Can be inspected or modified by calling the on_warn() object method.

πŸ‘‘ THROWS πŸ‘‘

Used to store the exception type that the object should throw. Equivalent to the $THROWS package variable but operating on a per-object basis. Can be inspected or modified by calling the throws() object method.


πŸ’œ AUTHOR πŸ’œ

Andy Wardley <http://wardley.org/> πŸ’–

Copyright (C) 1996-2009 Andy Wardley. All Rights Reserved.

This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

and that’s the whole vibe, bestie πŸ’– this page is Badger::Base(3pm), from perl v5.36.0, dated 2023-08-28. slay. πŸ’…βœ¨

πŸ“¦ where this page comes from