NAME

Carp::Proxy - Diagnostic delegation

SYNOPSIS

   use Carp::Proxy;

   fatal 'handler_subroutine', @optional_arguments
       if not $assertion;

   sub handler_subroutine {
       my( $proxy_object, @optional_arguments ) = @_;

       $proxy_object->filled( 'explanation' );
       return;
   }

DESCRIPTION

Error messages in Perl are commonly coded with idioms like:

die 'explanation'
    if not $assertion;

The idiom is attractive when the explanation is simple. If an explanation grows to more than a few words, or if it requires calculation, then the surrounding flow becomes disrupted. The solution, of course, is to offload failing assertions to a subroutine.

Subroutines that perform diagnosis, compose error messages and throw exceptions tend to have repeated code at the beginning and end, with unique content somewhere in the middle. Carp::Proxy proposes a wrapper subroutine, called a Proxy, to factor out the repeated sections.

fatal 'user_subroutine'
    if not $assertion;

Proxys, like fatal(), serve as elaborate, customizable replacements for warn(), die() and members of the Carp:: family like confess(). If we look at warn(), die(), confess() and the others, we notice that they are all just different variations on two themes:

- Add locational context to a user-supplied message.
- Throw some kind of exception.

Carp::Proxy parameterizes the two themes into attributes of an exception object that is created whenever a Proxy is called. The Proxy passes the object to a user-defined "Handler" subroutine which is responsible for constructing the diagnostic message. When the Handler returns, the Proxy optionally adds "Context" (a stacktrace) to the message and performs "Disposition", typically by calling die().

When the object is constructed it captures the state of Perl's error variables, for later examination by the Handler. The object provides methods that aid in message composition. Attributes control message formatting, stacktrace generation and how Disposition will be handled.

The object overloads Perl's stringification operator with a message rendering method, causing uncaught exceptions to be nicely formatted. Exceptions that are caught can be modified and re-thrown.

WE ARE THE 99%

Carp::Proxy has a long list of features, but getting started is easy. All you need for most day-to-day work is the Proxy and two methods:

fatal()   The (default) Proxy.
filled()  Auto-formatting message builder method.
fixed()   Pre-formatted message builder method.

Use fatal() wherever you currently use die(). Your Handler should compose the diagnostic message using the filled() and/or fixed() methods.

SAMPLE OUTPUT

The formatted messages produced by the Proxy start off with a "Banner". The Banner includes a title and the name of the Handler. As the Banner is the first thing seen by users, it is helpful if the Handler name conveys a terse description of the situation.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Fatal: << cannot overwrite >>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Following the Banner are a series of "Sections" containing paragraphs of descriptive text. Each Section is introduced by a "Header" sub-title that is wrapped in *** stars ***.

      *** Description ***
        The destination file already exists.  An attempt was made
        to overwrite it, but the attempt failed.  This might have
        happened because of permission problems, or possibly if
        the destination is not a file (i.e. a directory or link
        or something else).
 
        Either remove the destination manually, or choose a
        different destination.
 
      *** Destination Filename ***
        /home/isaac/muse/content
 
      *** ls(1) output ***
        -r--r--r-- 1 isaac users 21626 Aug  5 17:22 content
 
      *** System Diagnostic ***
        Permission denied
 
      *** Stacktrace ***
        fatal called from line 273 of /usr/bin/cantu
	set_output called from line 244 of /usr/bin/cantu
	main called from line 24 of /usr/bin/cantu

Here is the corresponding Handler code. Note that most of the Handler's body is dedicated to producing message content; there is very little overhead. Handlers are easy to write.

sub cannot_overwrite {
    my( $cp, $filename ) = @_;

    $cp->filled(<<"EOF");
The destination file already exists.  An attempt was made to
overwrite it, but the attempt failed.  This might have happened
because of permission problems, or possibly if the destination
is not a file (i.e. a directory or link or something else).

Either remove the destination manually, or choose a different
destination.
EOF
    $cp->filename( $filename, 'Destination Filename' );
    $cp->fixed( qx{ /bin/ls -ld $filename }, 'ls(1) output' );
    $cp->errno_section;
    return;
}

EXPORTS

Carp::Proxy defines an exception class. Depending on the arguments supplied to use(), Carp::Proxy also generates Proxy subroutines for export. The generation part is important: Carp::Proxy implements the import() method as a function factory.

Proxy customization is performed by supplying a HashRef of attribute => parameter pairs for each desired Proxy. The arguments to use() or import() look like this:

proxy_name1 =>
    {
     attribute => parameter,
     ...
     },

proxy_name2 =>
    {
     attribute => parameter,
     ...
    },
...

If there is only one argument, i.e. a proxy-name key with no corresponding value, then an empty HashRef, {}, where all attributes assume defaults, is implied.

Here are some examples:

   #-----
   # All three of these 'use' statements generate and export a
   # Proxy named fatal() with defaults for all attributes.
   #-----
   use Carp::Proxy;
   use Carp::Proxy 'fatal';
   use Carp::Proxy fatal => {};

   #-----
   # This statement is the same as all of the above, except
   # that now the Proxy is named error() instead of fatal().
   #-----
   use Carp::Proxy 'error';

   #-----
   # Here we export two proxys, oops() and warning(), each with
   # different sets of attributes.
   #-----
   use Carp::Proxy oops    => { context      => 'internals' },
                   warning => { banner_title => 'Warning',
                                disposition  => 'warn'      };

   #----- No exports.  Class definition only.
   use Carp::Proxy ();

The no-export form is desirable if you want the class definition for your own purposes, or if Proxy attributes need to be established at runtime. You can invoke import(), at any time, to build Proxy subroutines.

use Carp::Proxy ();
...
Carp::Proxy->import( expire => { end_hook => $log_me });

PROXY INVOCATION

Usage:
   <proxy> $handler, @optional_arguments;

The default proxy_name is fatal so the typical usage looks more like

fatal $handler, @optional_arguments;

$handler is expected to be a string (NOT a CodeRef!) that names a user-defined subroutine.

The Proxy performs the following actions:

1 - Capture the Environment

Perl's error/status variables $ARG, $ERRNO, $CHILD_ERROR and $EVAL_ERROR (also known as $_, $!, $? and $@, respectively) are all captured as soon as possible to preserve their values for examination by $handler.

2 - Create a Carp::Proxy object

The Proxy supplies settings for all the object's required attributes, see ATTRIBUTES. The Proxy also forwards initializations for any attributes supplied by the user (arguments to use() or import()).

3 - Call begin_hook

The begin_hook attribute, if it exists, is called, passing the object as the only argument.

4 - Locate the Handler

Locating the Handler is a complex process; see HANDLER SEARCH. Briefly, the default behavior is to use the first subroutine it finds from the following list of templates. The list is evaluated once for each package in the handler_pkgs attribute.

<package>::_cp_<handler_name>
<package>::_<handler_name>
<package>::<handler_name>
5 - Call Handler

The Handler is called with the object as the first argument. Any arguments passed to the Proxy, beyond the handler-name, are propagated as additional arguments to the Handler.

6 - Add Calling Context (Stacktrace)

The method add_context() is invoked to generate a Section with stacktrace content, as dictated by the context attribute.

7 - Call end_hook

The end_hook attribute, if it exists, is called, passing the object as the only argument.

8 - Perform Disposition

The method perform_disposition() is invoked. Disposition is controlled by the disposition attribute; typically this means passing the Carp::Proxy object to die().

If perform_disposition() returns, rather than throwing, then the returned value is propagated as the return value of the Proxy.

ATTRIBUTES

All Carp::Proxy object attributes have correspondingly named accessors. When the accessors are invoked without arguments, they return the attribute's value. Mutable (Read-Write) attributes have accessors that can be supplied with an argument to set the attribute's value.

Users generally do not create Carp::Proxy objects directly; the Proxy does that for them. The object constructor, new(), requires specification for several of the attributes like eval_error. The Proxy supplies these required attributes, but arguments to use() or import() can override them.

All other attributes invoke a "builder" method to initialize the attribute value if one is not provided. Builder methods are named with a prefix of '_build_'. You can change default values for these attributes with arguments to use() / import(), or by providing custom builder functions in a sub-class.

arg

arg holds the value of Perl's $ARG ($_), as harvested from the invoking environment. This can be handy if you are using Try::Tiny.

Builder: None; new() requires arg specification.
Default: N/A
Domain: Any
Affects: For user convenience; not used by Carp::Proxy
Mutability: Read-Only

as_yaml

The as_yaml attribute is a flag that controls message rendering. When False, message text is derived from the sections attribute; this is the normal mode of operation.

When as_yaml is True message text is a YAML::Dump() of the Carp::Proxy object. Serialization via YAML makes it possible to propagate exceptions up from child processes.

Builder: _build_as_yaml()
Default: 0 (False)
Domain: Boolean
Affects: render_message()
Mutability: Read-Write

The Banner is the first part of the message; banner_title contains the first word(s) in the Banner.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Fatal << handler name >>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-----
   \
    +------ banner_title
Builder: _build_banner_title()
Default: 'Fatal'
Domain: String
Affects: banner()
Mutability: Read-Write

begin_hook

If begin_hook contains a CodeRef then the Proxy will call the CodeRef immediately after constructing the Carp::Proxy object. The object is passed as the only argument.

A begin_hook is a great way to always do some activity at the start of every exception - before any Handler gets control.

Builder: _build_begin_hook()
Default: undef
Domain: undef or a CodeRef
Affects: PROXY-INVOCATION
Mutability: Read-Write

body_indent

body_indent influences the presentation of paragraphs created by the Section creating methods filled() and fixed(). Use body_indent to determine the amount of additional indentation, beyond header_indent, that is applied to Section paragraphs.

Builder: _build_body_indent()
Default: 2
Domain: Non-negative integers
Affects: filled_section() and fixed_section()
Mutability: Read-Write

child_error

child_error holds the value of Perl's $CHILD_ERROR ($?), as harvested from the invoking environment.

Builder: None; new() requires child_error specification.
Default: N/A
Domain: Any
Affects: decipher_child_error()
Mutability: Read-Only

columns

The columns attribute sets the line width target for the Banner and for any filled Sections. Values below about 30 are not practical.

Builder: _build_columns()
Default: 78
Domain: Positive Integers
Affects: banner() and filled_section()
Mutability: Read-Write

context

The context attribute controls the generation of a stacktrace Section.

Builder: _build_context()
Default: 'confess'
Domain:
'none' - No Section generated.
'die' - Describe where Proxy was called.
'croak' - Describe where Proxy's caller was called.
'confess' - Stacktrace, starting with Proxy call.
'internals' - Complete stacktrace with Carp::Proxy guts.
CodeRef - Do it yourself.
Affects: add_context()
Mutability: Read-Write

disposition

The disposition attribute controls how the exception is thrown.

Builder: _build_disposition()
Default: 'die'
Domain:
'return' - No exception thrown; Proxy returns.
'warn' - Carp::Proxy object passed to Perl's warn().
'die' - Carp::Proxy object passed to Perl's die().
CodeRef - Do it yourself.
Affects: perform_disposition()
Mutability: Read-Write

end_hook

If end_hook contains a CodeRef then the Proxy will call the CodeRef just before performing disposition. The Carp::Proxy object is passed as the only argument.

The end_hook is handy for things that you want all Handlers to do as their last action. An example might be writing to a logfile.

Builder: _build_end_hook()
Default: undef
Domain: undef or a CodeRef
Affects: PROXY-INVOCATION
Mutability: Read-Write

eval_error

The eval_error attribute holds the value of Perl's $EVAL_ERROR ($@), as harvested from the invoking environment.

Builder: None; new() requires an eval_error specification
Default: N/A
Domain: Any
Affects: For user convenience; not used by Carp::Proxy
Mutability: Read-Only

exit_code

exit_code is used to set the value harvested by the operating system when a process dies.

Builder: _build_exit_code()
Default: 1
Domain: Integers greater than Zero
Affects: perform_disposition()
Mutability: Read-Write

fq_proxy_name

fq_proxy_name is the fully-qualified proxy-name. This is the Proxy's name, complete with exported package qualifier. This might be useful if a Handler wants to know the parental Proxy.

Builder: None; new() requires fq_proxy_name specification
Default: N/A
Domain: String
Affects: add_context()
Mutability: Read-Write

handler_name

The Proxy saves its first argument, the Handler, in handler_name.

Builder: None; new() requires handler_name specification
Default: N/A
Domain: String
Mutability: Read-Write

handler_pkgs

The search for a Handler subroutine is performed in each of the packages in the ArrayRef handler_pkgs. A copy of proxy_package is automatically appended, by the Proxy after object construction.

Builder: _build_handler_pkgs()
Default: []
Domain: ArrayRef
Mutability: Read-Write

handler_prefix

handler_prefix affects how the search for a Handler is performed. The list of templates that are tried during HANDLER SEARCH is based on handler_prefix.

Builder: _build_handler_prefix()
Default: undef
Domain: undef or String
Affects: HANDLER SEARCH
Mutability: Read-Write

header_indent

Section Headers are indented from the left margin by header_indent spaces.

Builder: _build_header_indent()
Default: 2
Domain: Non-negative Integers
Affects: header(), filled_section() fixed_section()
Mutability: Read-Write

maintainer

The contact_maintainer() method produces a Section that urges the message recipient to contact the maintainer. The Section is created only if the maintainer attribute is non-empty. A string containing an email address and a telephone number works well.

Builder: _build_maintainer()
Default: ''
Domain: String
Affects: contact_maintainer()
Mutability: Read-Write

numeric_errno

The numeric_errno attribute contains the value of Perl's $ERRNO ($!), as harvested from the invoking environment. The value is obtained by evaluating $ERRNO in a numeric context.

Builder: None; new() requires numeric_errno specification
Default: N/A
Domain: Any
Affects: For user convenience; not used by Carp::Proxy
Mutability: Read-Only

pod_filename

The synopsis() method searches for POD in pod_filename.

Builder: _build_pod_filename()
Default: proxy_filename.
Domain: String
Affects: synopsis()
Mutability: Read-Write

proxy_filename

The filename containing the code that requested construction of the Proxy, either by use() or import().

Builder: None; new() requires proxy_filename specification
Default: N/A
Domain: String
Affects: pod_filename.
Mutability: Read-Only

proxy_name

proxy_name contains the name of the Proxy subroutine.

The default proxy_name is 'fatal'.

The only time this attribute is used is when use() or import() are called without arguments. Defining a _build_proxy_name() in a sub class allows you to change the default name.

Builder: _build_proxy_name(); new() requires proxy_name
Default: 'fatal'
Domain: String
Affects: use(), import()
Mutability: Read-Only

proxy_package

The proxy_package attribute is derived from the package that requested construction of the Proxy, either by calling use() or import().

Builder: None; new() requires proxy_package specification
Default: Package of whatever subroutine called use() or import()
Domain: String
Affects: handler_pkgs
Mutability: Read-Only

section_title

The Section-creating methods filled() and fixed(), accept an optional, second argument to be used as the title for the Section. When this optional argument is not supplied, section_title is used instead.

Builder: _build_section_title()
Default: 'Description'
Domain: Non-empty String
Affects: header(), filled(), fixed()
Mutability: Read-Write

sections

The Section-creating methods filled(), fixed() and raw() create Section specifications. Section specifications accumulate in the ArrayRef sections.

Builder: _build_sections()
Default: []
Domain: ArrayRef of section-specifications
Affects: render_message()
Mutability: Read-Write

string_errno

string_errno is a read-only attribute that contains the value of Perl's $ERRNO ($!), harvested from the invoking environment. The value is obtained by evaluating $ERRNO in a string context.

Builder: None; new() requires string_errno specification
Default: N/A
Domain: String
Affects: errno_section()
Mutability: Read-Only

tags

Passing arbitrary data to the catching environment can sometimes be useful. The tags attribute is a HashRef for tag-value pairs of user data. The attribute is completely ignored by the Proxy and by Carp::Proxy methods.

Builder: _build_tags()
Default: {}
Domain: HashRef
Affects: For user convenience; not used by Carp::Proxy
Mutability: Read-Write

METHODS

The documentation for each method starts off with a 'Usage' description. A description will look something like this:

Usage:
   <void> $cp->append_handler_package( $pkg <, $pkg2 ...>);

The word enclosed in angle-brackets, at the beginning, (Like <void>) attempts to convey the return value. Arguments in angle-brackets are optional, with the ellipsis (...) implying repeatability. $cp is a Carp::Proxy object. $class, if used as the invoker, indicates a class method.

add_context

Usage:
   <void> $cp->add_context();

add_context() creates a Section that contains a stacktrace of where the Proxy was invoked. The context attribute controls whether or not the Section is generated, as well as what kind of stacktrace is produced.

add_context() is called by the Proxy when the Handler returns.

Perl's caller() is used to probe the callstack and report stackframes. Stackframes are rendered on one line if the length would not exceed the value of the columns attribute. Long lines are folded at the filename portion of the stackframe and given body_indent extra spaces of indentation.

The context attribute may take on any of these values:

'none'

The context of 'none' is a request to forego stacktrace generation. No Section is produced.

'die'

The context of 'die' adds a Section containing a single entry. The entry details the source location where the Proxy was invoked. The effect is intended to mimic Perl's behavior when die() is passed a string WITHOUT a trailing newline.

The title for the Section is 'Exception'.

*** Exception ***
  fatal called from line 27 of /home/duane/bin/assim
'croak'

The context of 'croak' adds a Section that identifies the subroutine that invoked the Proxy. The effect is intended to mimic the behavior of Carp::croak(), which assigns blame to the caller.

The title for the Section is 'Exception'.

*** Exception ***
  perform_query called from line 1172 of
    /opt/barkta/linux/v3.7/bin/ReadRecords
'confess'

The context setting of 'confess' creates a multi-line Section. Lines in the Section correspond to stackframes from nearest to outermost, much like the behavior of Carp::confess.

'confess' is the default context for Carp::Proxy objects.

The Section title is 'Stacktrace'.

'internals'

The context setting 'internals' is very similar to the setting 'confess'. Both produce full stacktraces, but 'confess' omits stackframes that originate on behalf of the Proxy. You normally do not want to see Carp::Proxy stackframes, although they might be helpful in debugging a sub-class. 'internals' gives you everything.

The Section title is 'Stacktrace'.

CodeRef

By providing a CodeRef users can completely control context reporting.

The Proxy will make a callback to CodeRef immediately after the Handler returns. The Carp::Proxy object will be passed as the only argument. The CodeRef should create a Section using the filled(), fixed() or raw() methods.

The Carp module from the Perl standard library provides some complex functionality for ignoring stackframes that you may find useful.

append_handler_package

Usage:
   <void> $cp->append_handler_package( $pkg <, $pkg2 ...>);

The attribute handler_pkgs is an ArrayRef. append_handler_package() is sugar to make adding packages to the end of handler_pkgs easier.

append_section

Usage:
   <void> $cp->append_section( $array_ref <, $array_ref2...>);

The sections attribute is an ArrayRef containing child ArrayRefs, one for each Section (like filled(), fixed() etc.). append_section() is sugar to make adding a Section request to the sections attribute, easier. Section requests are added to the end of sections (appended).

Usage:
   <String> $cp->banner();

banner() produces the multi-line introduction to a diagnostic message. The Banner is intended to stand out visually so it fills up the horizontal space from left to right margins. The value of columns dictates the amount of fill needed. The Banner looks something like this:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<banner_title> << <cleansed_handler> >>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In the above template, banner_title is taken directly from the banner_title attribute. cleansed_handler is generated by invoking identifier_presentation() on the handler_name attribute.

call

Usage:
   <void> $cp->call( $handler, @optional_arguments );

The task of Handlers is to create Sections. Handlers can call other Handlers to compose common Sections.

Most Handlers know how to locate their peers because they reside in the same package and have the same prefix conventions. call() can certainly be used to invoke peers, although it might seem like overkill.

call() is useful when Handlers reside in a hierarchy of packages and you need a full search. call() is also the only way to invoke builtin Handlers.

$cp->call( '*assertion_failure*', $description, \%status_vars );

call() runs the algorithm described in HANDLER SEARCH.

contact_maintainer

Usage:
   <void> $cp->contact_maintainer();

If the maintainer attribute is non-empty then a Section containing the maintainer string is created. No Section is created if maintainer is empty.

This works well if maintainer contains contact info.

*** Please contact the maintainer ***
  Your Name  your.name@help.org   (123)456-7890

decipher_child_error

Usage:
   <void> $cp->decipher_child_error();
   -or-
   <void> $cp->decipher_child_error( $child_error );

Perl's $CHILD_ERROR ($?) encodes several bits of information about how a child process terminates, see the perlvar documentation on $CHILD_ERROR for details. decipher_child_error() unpacks the various bits of information in $CHILD_ERROR and converts them into a filled() Section. Examples:

*** Process Succeeded ***
  The child process completed normally (exit code 0).

*** Process returns failing status ***
  The child process terminated with an exit code of 14.

*** Process terminated by signal ***
  The child process was terminated by SIGSEGV (signal 11).

If a $child_error argument is provided then the argument value is deciphered, otherwise the value held in the child_error attribute is used.

directory

Usage:
   <void> $cp->directory( $dir <, $title >);

The directory() method creates a fixed() Section. The optional $title, if supplied, forms the title for the Section, otherwise 'Directory' is used as the title.

Output from Cwd::abs_path() is used to form the body of the Section.

errno_section

Usage:
   <void> $cp->errno_section( <$title> );

A filled Section is created using the contents of the string_errno attribute. If $title is not provided then 'System Diagnostic' is used as the Header title.

No Section is created if the string_errno attribute is empty.

filename

Usage:
   <void> $cp->filename( $file <, $title >);

The filename() method creates a fixed() Section. The optional $title, if supplied, forms the title for the Section, otherwise 'Filename' is used as the title.

Output from Cwd::abs_path() is used to form the body of the Section.

filled

Usage:
   <void> $cp->filled( $content <, $title >);

filled() creates a Section. The Section is introduced with a header() containing $title. The body of the Section is produced by reformatting $content into paragraphs of length-limited lines/

If $title is not supplied, or if it is undef, then the section_title attribute is used in its place. If $title is an empty string then no Header is produced. This makes it easy to chain together Fixed and Filled Sections under the same Header.

Any spaces at the beginning of each paragraph in $content sets the relative indentation for the whole paragraph. Each paragraph may have different indentations.

Paragraphs are reformatted by splitting them into words, on whitespace, then building up new lines. Each line starts with spaces corresponding to the sum of header_indent, body_indent and any paragraph-specific indentation. Lines are then filled with words to achieve a target line width. The target width is given by the columns attribute.

In actuality, all the filled() method does is to add a request for a "filled_section" onto the end of the sections list. The actual processing is performed by filled_section() when render_message() traverses the sections list. What this means is that the settings for attributes like section_title, columns, header_indent and body_indent only come into play when render_message() is run.

See filled_section() for details.

filled_section

Usage:
   <String> $cp->filled_section( $content, $title );

filled_section() is not usually invoked directly by users. render_message() invokes filled_section() as it traverses the list held in the sections attribute.

$content is expected to be a string. If $content is an empty string then no Section is produced - an empty string is returned.

$title is converted into a section-title using header().

$content is split into paragraphs wherever there are two or more consecutive newlines, more specifically using this regex:

/(?: \r? \n ){2,}/x

Each paragraph is examined for leading whitespace. This leading whitespace is processed by converting tabs into spaces on eight-column boundarys. The converted whitespace forms the supplemental indentation for the paragraph.

New paragraphs are formed a line at a time by starting with an indentation amount corresponding to the sum of header_indent, body_indent and any supplemental indentation. Words from the old paragraph are added to the line so long as the line length does not exceed columns. At least one word is always added, even if columns is exceeded.

Any trailing whitespace is removed. Output paragraphs are joined with a blank line. The returned string is the concatenation of the section title, the paragraphs and a trailing blank line.

Override filled_section() in a sub-class, rather than filled(), if you want different filling behavior.

fixed

Usage:
   <void> $cp->fixed( $content <, $title >);

fixed() creates a Section. The Section is introduced with a header() containing $title. The body of the Section is formed by retaining the formatting already present in $content.

If $title is not supplied, or if it is undef, then the section_title attribute is used in its place. If $title is an empty string then no Header is included. This makes it easy to chain together Fixed and Filled Sections under the same Header.

Each line in $content is indented by a constant amount corresponding to the header_indent plus the body_indent. Tabs in $content are folded into spaces to preserve column alignment before the indentation is prepended. Trailing whitespace on each line is replaced with an appropriate line terminator for the platform. $content is otherwise unmolested. Almost WYSIWYG.

In actuality, all the fixed() method does is to add a request for a "fixed_section" onto the end of the sections list. The actual processing is performed by the fixed_section() method when the render_message() method traverses the sections list. What this means is that the settings for attributes like section_title, header_indent and body_indent only matter at the time render_message() is run.

See fixed_section() for details.

fixed_section

Usage:
   <String> $cp->fixed_section( $content, $title );

fixed_section() is not usually invoked directly by users. render_message() invokes fixed_section() as it traverses the list in the sections attribute.

$content is expected to be a string. If $content is the empty string then no Section is generated and an empty string is returned.

$title is converted into a Section title string using header().

$content is split into lines on newline ("\n") characters for processing. Trailing whitespace is removed. Embedded tabs are converted to the equivalent number of spaces assuming eight character boundarys. Indentation corresponding to the sum of header_indent and body_indent is added to the beginning of each line. Lines are joined with platform-appropriate line termination.

Trailing whitespace is removed, the section-title is prepended and a single blank line is added to the end.

Usage:
   <String> $cp->header( $title );

header() produces an introductory line for a Section of paragraphs. The line is indented from the left margin by header_indent spaces. The line is formed using the following template:

<indent>*** <$title> ***

The intent is to provide an introductory heading for Section paragraphs.

*** Description ***
  The database server is refusing connections.

If $title is undef then the section_title attribute is used in its place. Passing an empty string ('') for title causes header() to omit Header generation. In this case an empty string is returned.

header() is called by the Section creating methods filled_section() and fixed_section().

Subclass Carp::Proxy and override header() for a different look.

identifier_presentation

Usage:
   <String> $class->identifier_presentation( $name );

The Banner reads better when words in the handler_name are separated by spaces rather than underscores (_). Likewise with camelCasedIdentifiers.

Underscores are replaced by single spaces everywhere they occur. Spaces are inserted everywhere character-case changes from lower to upper, and upper-case characters are folded to lower-case. The following are example conversions:

'no_user_credentials'  => 'no user credentials'
'nonexistentRecord'    => 'nonexistent record'

Sub-class Carp::Proxy and override identifier_presentation() if you want a different convention.

import

Usage:
   <void> $class->import( <%attrs_by_proxy>);

import() accepts specifications for Proxy construction. Specifications take the form of a proxyname and a hashref of attribute initializations.

proxyname1 => {
               attributeA => initial_valueA,
               attributeB => initial_valueB,
               ...
              }

Any number of proxyname, hashref pairs may be specified; a proxy subroutine will be constructed for each pair.

If there is only one argument it is taken to be a proxyname introducing an empty hashref. If there are no arguments then it is assumed that the builder-specified default for the proxy_name attribute ('fatal'), should be used for the proxyname and an empty hashref used for the attribute initializations.

import() probes the callstack to determine the package and filename of the user code that called import(). import() uses these values to create a hash containing the attributes proxy_filename, proxy_name proxy_package and fq_proxy_name. Any supplied attributes are added to the hash. The builtin handler *configuration* returns a reference to this hash.

list_handler_packages

Usage:
   <list> $cp->list_handler_packages();

list_handler_packages() is sugar that dereferences the handler_pkgs attribute (an ArrayRef) and returns the contents.

list_sections

Usage:
   <list> $cp->list_sections();

The sections attribute is an ArrayRef. list_sections() is sugar to return all the elements of sections.

new

Usage:
   <Carp::Proxy object> $class->new
       ( arg            => harvested $_,
         eval_error     => harvested $@,
         fq_proxy_name  => 'package::subname',
         handler_name   => 'name of handler',
         numeric_errno  => harvested 0 + $!,
         proxy_filename => 'filename',
         proxy_name     => 'subname',
         proxy_package  => 'package',
         string_errno   => harvested '' . $!,
         < attribute    => value ...>
       );

new() is normally called by the Proxy, so this documentation is only useful if you are using the object for your own purposes. There are a large number of required attribute-value pairs. Specification for any additional attributes is supported. Builder methods are invoked for all unspecified attributes.

There is some inconsistency around the proxy_name attribute. The proxy_name is required by new() even though it has a builder method. The builder is for use by import(), which invokes it if needed, and passes the result to new().

perform_disposition

Usage:
   <Scalar> $cp->perform_disposition();

The disposition attribute determines the final actions of the Proxy, which are carried out by perform_disposition(). Valid settings for disposition are:

'warn'

A disposition of 'warn' causes perform_disposition() to do this:

warn $cp;
return ();
'die'

A disposition of 'die' causes perform_disposition() to do this:

$ERRNO = $cp->exit_code;
die $cp;

See Perl's die() for an explanation of propagating $ERRNO into the exit code for the process.

'die' is the default disposition.

'return'

The disposition of 'return' is unusual; it signifies a desire to abort the whole death-by-proxy process. perform_disposition does this:

return $cp;
CodeRef

The user can take control of disposition by supplying a CodeRef for disposition. In this case, the behavior of perform_disposition() is:

return $cp->disposition->( $cp );

prepend_handler_package

Usage:
   <void> $cp->prepend_handler_package( $pkg <, $pkg2...>);

The attribute handler_pkgs is an ArrayRef. prepend_handler_package() is sugar to make adding packages to the front of handler_pkgs easier.

prepend_section

Usage:
   <void> $cp->prepend_section( $array_ref <, $array_ref2...>);

The sections attribute is an ArrayRef containing child ArrayRefs, one for each Section (like filled(), fixed() etc.). prepend_section() is sugar to make adding a Section request to the sections attribute, easier. Section requests are added to the front of sections (prepended).

raw

Usage:
   <void> $cp->raw( $content );

raw() provides an alternative to fixed() and filled() for composing diagnostic Sections.

In effect, raw() creates a Section containing only $content. You are completely responsible for the final appearance of the Section; there is no Header, no trailing blank line, no indentation and no platform appropriate line termination.

In actuality, all the raw() method does is to add a request for a raw Section onto the sections list; the actual processing is performed by the raw_section() method when the render_message() traverses sections.

See raw_section() for details.

raw_section

Usage:
   <String> $cp->raw_section( $content );

raw_section() is not usually invoked directly by users. render_message() invokes raw_section() as it traverses the list in the sections attribute.

raw_section() does nothing; the returned string is simply a copy of $content.

render_message

Usage:
   <String> $cp->render_message();

The behavior of render_message() is dependent on the setting of the attribute as_yaml. If as_yaml is False, which is the default, then render_message() walks the list of section-specifications stored in the sections attribute, executing each one in turn. The return value is formed by concatenating each of the results.

The sections attribute, an ArrayRef, is expected to contain any number of ArrayRef elements. Each child ArrayRef must have at least one element: the name of a method to be invoked. Any remaining elements are passed to the invoked method as arguments. For example, a sections specification that looks like this:

[
 [ 'filled_section', 'content1', 'title1' ],
 [ 'filled_section', 'content2', 'title2' ],
]

Results in the execution of something like this:

my $buffer = $cp->banner();

$buffer .= $cp->filled_section( 'content1', 'title1' );
$buffer .= $cp->filled_section( 'content2', 'title2' );

return $buffer;

The sections list is unchanged by the traversal, so render_message() may be invoked repeatedly. Settings for attributes like banner_title, columns, section_title, header_indent and body_indent can be changed between invocations to vary the message format.

Changing attributes like context, which are referenced during the generation of Section specifications, have no effect.

If as_yaml is True then we return a string that is a YAML dump of the Carp::Proxy object, something like this:

return YAML::XS::Dump( $cp );

The intent here is to use YAML to serialize all aspects of the Carp::Proxy object. Assuming that we have a disposition setting of die, our serialized object will be written out to STDERR, where it can be captured by a parent process and reconstituted. The reconstituted object can be examined, or augmented with parental context and rethrown.

synopsis

Usage:
   <void> $cp->synopsis( %optional_supplements );

The synopsis() method employs Pod::Usage::pod2usage() to create a Section from the user's POD. User POD is located by searching in the pod_filename attribute.

The call to pod2usage() is passed a HashRef with the following options:

-input   => $cp->pod_filename,
-output  => <filehandle>,
-exitval => 'NOEXIT',
-verbose => 0,

This set of options causes pod2usage() to format the SYNOPSIS portion of the user's POD. Any key-value pairs in %optional_supplements are appended to the contents of the HashRef, allowing you to override or supplement these defaults.

Example

Internally, Carp::Proxy uses synopsis() to extract sections from this POD document when composing diagnostics. If, for instance, you supply a negative value as the setting for body_indent you get an exception. The text of the exception is generated using something like this:

$cp->synopsis( -verbose  => 99,
               -sections => ["ATTRIBUTES/body_indent"],
             );

The resulting diagnostic looks something like this:

   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   Oops << negative body indentation >>
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     *** Description ***
       The requested setting of '-1' for the 'body_indent'
       attribute is not allowed.

     *** Synopsis ***
         body_indent:
           *body_indent* influences the presentation of paragraphs
           created by the Section creating methods filled() and
           fixed(). Use *body_indent* to determine the amount of
           additional indentation, beyond header_indent, that is
           applied to Section paragraphs.

           Builder: _build_body_indent()
           Default: 2
           Domain: Non-negative integers
           Affects: filled_section() and fixed_section()
           Mutability: Read-Write

     *** Stacktrace ***
       ...

See 'perldoc Pod::Usage' and 'perldoc Pod::Select' for details about using -verbose and -sections.

usage

Usage:
   <void> $cp->usage();

usage() examines the callstack, to find the invoker - the subroutine that invoked the Proxy. A pass through the HANDLER SEARCH algorithm is made to see if it can find a subroutine with this name:

usage_<invoker>

In the default configuration this means that these three subroutine names are tested for existence:

<package>::_cp_usage_<invoker>
<package>::_usage_<invoker>
<package>::usage_<invoker>

Just like the search for a Handler, the settings for handler_prefix and handler_pkgs influence the where and what of the search for a usage subroutine.

If none of the attempts finds an existing subroutine then the next entry in the callstack (i.e. the invoker of the invoker) is tried. The progression up the callstack continues until there are no more stackframes. At this point the algorithm gives up and throws a "no usage documentation" exception.

The search sounds complex, but the intent is simple: public subroutines and methods can call utilities, say to validate incoming arguments, and these utilities can call Proxys to throw exceptions. When the Handler invokes usage() we eventually find a usage message associated with the public subroutine.

#----- We want this to be called for help with 'my_func()'
sub _usage_my_func {
    my( $cp ) = @_;

    $cp->fixed( 'Usage: <num> my_func( val );', 'Usage' );
    $cp->filled( 'my_func() returns blah blah blah.', '' );
}

sub my_func {
    my( $val ) = @_;

    fatal 'missing_argument', 'val'
        if not defined $val;
    ...
}

#----- Reusable by anyone that defines their own _usage_
sub _cp_missing_argument {
    my( $cp, $name ) = @_;

    $cp->filled("The argument '$name' is missing, or undef.");
    $cp->usage;
}

Other subroutines, besides my_func(), can throw fatal exceptions with the 'missing_argument' Handler. The diagnostic will be customized appropriately for each one.

The invoker-upward aspect of the search means that my_func(), instead of calling fatal() directly, could have called an arg-checking utility, which called another utility etc., which finally called fatal(). The search would have eventually located _usage_my_func().

HANDLER SEARCH

A Proxy invocation contains, as its first argument, a string that will become the handler_name attribute. The string 'no_such_author' is used to establish handler_name in this example:

fatal 'no_such_author', $who;

The Proxy calls the Handler to build up the diagnostic message, but first it must locate the requested subroutine.

The search for a Handler subroutine is made in the packages found in handler_pkgs. Users can specify a list of packages to search by supplying the tagged list to use() or import().

package main;
use Carp::Proxy fatal => { handler_pkgs => [qw( Support Common )]};

You can also sub-class Carp::Proxy and override _build_handler_pkgs() to return an ArrayRef of the desired packages.

The Proxy always appends a copy of proxy_package to handler_pkgs after object construction. proxy_package is the package that issued the use(), or made the call to import(). In the above example handler_pkgs becomes:

[qw( Support Common main )]

The subroutine that is the target of the search is influenced by the setting of handler_prefix. When the handler_prefix attribute is undef, the Proxy builds three templates from handler_name. The first subroutine that exists is used as the Handler.

<package>::_cp_<handler_name>
<package>::_<handler_name>
<package>::<handler_name>

If handler_prefix is not undef then only one template is tried:

<package>::<handler_prefix><handler_name>

If a Handler subroutine is not found by the template search then a check is made to see if handler_name matches one of the Carp::Proxy builtin Handlers. The builtin Handlers are surrounded by '*' characters since those are guaranteed not to collide with user Handlers.

*assertion_failure*
*internal_error*
*configuration*

See BUILTIN HANDLERS for a description of their functionality.

Finally, if a suitable Handler is not found by any of the above searches the Proxy concludes that you forgot to define a Handler. In response, the Proxy attempts to shame you into compliance by throwing an exception of its own:

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Oops << embarrassed developers >>
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      *** Description ***
        There was an error.  The developers caught the error and
        attempted to pass diagnosis off to a handler.  Unfortunately
        they forgot to define the handler.  Now there are two
        errors.  You should complain!

      *** Please contact the maintainer ***
        your.name@support.org   555-1212

      *** Missing Handler ***
        handler_name:   no_credentials
        handler_pkgs:   main
        handler_prefix: (undef)

      *** Stacktrace ***
        fatal called from line 443 of /usr/local/bin/hibs
	validate_user called from line 510 of /usr/local/bin/hibs
	cmdline called from line 216 of /usr/local/bin/hibs
	main called from line 17 of /usr/local/bin/hibs

BUILTIN HANDLERS

These are handler subroutines that come with Carp::Proxy.

internal_error

Usage:
   <void> fatal '*internal_error*', @strings;

The '*internal_error*' Handler can be used to promote warnings to errors or to turn miscellaneous die() exceptions to full Carp::Proxy exceptions. The typical use is to trap $SIG{__DIE__} or $SIG{__WARN__}.

   use English;

   $SIG{__DIE__} = sub{

       fatal '*internal_error*', @_
           if not $EXCEPTIONS_BEING_CAUGHT;
   };

A Filled Section is generated from the string interpolation of @strings. In the above example, the argument is the message that was passed to die(), like "Illegal division by zero". A contact_maintainer() Section is also added.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Fatal: << internal error >>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  *** Description ***
    Illegal division by zero at ./combine_decks line 27.

  *** Please contact the maintainer ***
    your.name@support.org   555-1212

  *** Stacktrace ***
    ...

assertion_failure

Usage:
   <void> fatal '*assertion_failure*', $description <, $hashref>;

If a failing assertion is indicative of a programmer fault then the primary audience for a diagnostic message will be a maintainer rather than an end user. Maintainers are most often helped by knowledge of the surrounding state. The builtin Handler *assertion_failure* attempts to be a generic Handler, useful for transmitting state to maintainers.

Using *assertion_failure* frees the programmer from having to write a Handler. The tradeoff is that some ability to customize the diagnostic is lost and the invocation syntax is more cluttered. The tradeoff can be reasonable for events that are rarely triggered, especially if it encourages programmers to add more assertions.

'*assertion_failure*' produces a Filled Section with some boilerplate containing the supplied $description.

Also included is a Fixed Section which contains a YAML dump of $hashref. This works best when the HashRef keys act as informational names (tag=>value pairs) to convey state. YAML is nice here because it does a great job of serializing complex data structures.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Fatal: << assertion failure >>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  *** Description ***
    An assertion has failed.  This indicates that the internal
    state of the program is corrupt.

    <$description>

  *** Please contact the maintainer ***
    your.name@support.org   555-1212

  *** Salient State (YAML) ***
    ---
    failure: 'unmatched case'
    index: 27
    selection: 'brunch'

  *** Stacktrace ***
    ...

configuration

Usage:
   <HashRef> fatal '*configuration*';

The '*configuration*' Handler is unconventional in that no exception is thrown. Instead, a reference to an internal hash is returned to the calling environment. Any changes to the referenced hash affect all future Proxy invocations.

Proxy configuration is established when a Proxy is created - either during use() or import(). Configuration consists of attribute => parameter pairs that are supplied by the user.

use Carp::Proxy ( warning => { banner_title => 'Warning',
                               disposition  => 'warn'      });

In the above snippet, banner_title and disposition, are internally held in a closure-based hash that persists across all invocations of the Proxy. The *configuration* Handler causes the Proxy to return a reference to this internal hash.

Here is an example of wanting to change Proxy behavior after Proxy creation:

   #----- fatal() does NOT throw an exception here...
   my $config = fatal '*configuration*';

   $config->{ disposition } = \&GUI::as_dialog;

As alluded to above, we want our GUI program to use conventional STDERR based messages during initialization, but once the GUI is up we want future messages to go to a dialog widget.

BUGS AND LIMITATIONS

Please report any bugs or feature requests to bug-carp-proxy at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Carp-Proxy. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

DEPENDENCIES

Core dependencies (come with Perl)

Config
Cwd
English
overload
Pod::Usage

External dependencies (install from CPAN)

Moose
Readonly
Sub::Name
YAML::XS

SUPPORT

You can find documentation for this module with the perldoc command.

perldoc Carp::Proxy

You can also look for information at:

SEE ALSO

perldoc perlvar

The section on $CHILD_ERROR describes information packing when a child process terminates. This is used by decipher_child_error().

perldoc -f die

The documentation on Perl's die() details how the exit code for a process depends on $ERRNO and $CHILD_ERROR.

perldoc Pod::Usage

The synopsis() method calls pod2usage() to format the SYNOPSIS section from user POD.

perldoc YAML::XS

The as_yaml attribute produces a YAML Dump of the Carp::Proxy object so that it can be reconstituted later.

The *assertion_failure* builtin Handler produces a Section containing YAML Dump of a user HashRef.

perldoc Carp

The 'croak' and 'confess' concepts were originated by Carp. If you are making a Do-it-yourself CodeRef for context then Carp's longmess() or longmess_heavy() may prove useful.

LICENSE AND COPYRIGHT

Copyright 2014 Paul Liebert.

This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.

See http://dev.perl.org/licenses/ for more information.