NAME
    Message::String - A pragma to declare and organise messaging.

VERSION
    version 0.1.0

SYNOPSIS
    This module helps you organise, identify, define and use messaging
    specific to an application or message domain.

  Using the pragma to define message strings
    The pragma's package name may be used directly:
            # Declare a single message
            use Message::String INF_GREETING => "Hello, World!";
    
            # Declare multiple messages
            use Message::String {
                INF_GREETING  => "I am completely operational, " .
                                 "and all my circuits are functioning perfectly.",
                RSP_DO_WHAT   => "What would you have me do?\n",
                NTC_FAULT     => "I've just picked up a fault in the %s unit.",
                CRT_NO_CAN_DO => "I'm sorry, %s. I'm afraid I can't do that",
            };

    Or, after loading the module, the "message" alias may be used:
            # Load the module
            use Message::String;

            # Declare a single message
            use message INF_GREETING => "Hello, World!";

            # Declare multiple messages
            use message {
                INF_GREETING  => "I am completely operational, " .
                                 "and all my circuits are functioning perfectly.",
                RSP_DO_WHAT   => "What would you have me do?\n",
                NTC_FAULT     => "I've just picked up a fault in the %s unit.",
                CRT_NO_CAN_DO => "I'm sorry, %s. I'm afraid I can't do that",
            };

        (Note: the "message" pragma may be favoured in future examples.)

  Using message strings in your application
    Using message strings in your code is really easy, and you have choice
    about how to do so:

    Example 1
            # Ah, the joyless tedium that is composing strings using constants...
            $name = "Dave";
            print INF_GREETING, "\n";
            print RSP_DO_WHAT;
            chomp(my $response = <STDIN>);
            if ($response =~ /Open the pod bay doors/i) 
            {
                die sprintf(CRT_NO_CAN_DO, $name);
            }
            printf NTC_FAULT . "\n", 'AE-35';

        Using messages this way can sometimes be useful but, on this
        occasion, aptly demonstrates why constants get a bad rap. This
        pattern of usage works fine, though you could just have easily used
        the "constant" pragma, or one of the alternatives.

    Example 2
            $name = 'Dave';
            INF_GREETING;                   # Display greeting (stdout)
            RSP_DO_WHAT;                    # Prompt for response (stdout/stdin)
            if ( /Open the pod bay doors/ ) # Check response; trying $_ but
            {                               # RSP_DO_WHAT->response works, too!
                CRT_NO_CAN_DO($name);       # Throw hissy fit (Carp::croak)
            }
            NTC_FAULT('AE-35');             # Issue innocuous notice (stderr)

    "Message::String" objects take care of things like printing info
    messages to stdout; printing response messages to stdout, and gathering
    input from STDIN; putting notices on stderr, and throwing exceptions for
    critical errors. They do all the ancillary work so you don't have to;
    hiding away oft used sprinklings that make code noisy.

  Exporting message strings to other packages
    It is also possible to have a module export its messages for use by
    other packages. By including "EXPORT" or "EXPORT_OK" in the argument
    list, before your messages are listed, you can be sure that your package
    will export your symbols one way or the other.

    The examples below show how to exports using "EXPORT" and "EXPORT_OK";
    they also demonstrate how to define messages using less onerous string
    catalogues and, when doing so, how to split longer messages in order to
    keep the lengths of your lines manageable:

    Example 1
            package My::App::Messages;
            use Message::String EXPORT => << 'EOF';
            INF_GREETING  I am completely operational,
            ...           and all my circuits are functioning perfectly.
            RSP_DO_WHAT   What would you have me do?\n
            NTC_FAULT     I've just picked up a fault in the %s unit.
            CRT_NO_CAN_DO I'm sorry, %s. I'm afraid I can't do that
            EOF
            1;

            # Meanwhile, back at main::
            use My::App::Messages;                  # No choice. We get everything!

    Example 2
            package My::App::Messages;
            use Message::String EXPORT_OK => << 'EOF';
            INF_GREETING  I am completely operational,
            ...           and all my circuits are functioning perfectly.
            RSP_DO_WHAT   What would you have me do?\n
            NTC_FAULT     I've just picked up a fault in the %s unit.
            CRT_NO_CAN_DO I'm sorry, %s. I'm afraid I can't do that
            EOF
            1;

            # Meanwhile, back at main::
            use My::App::Messages 'INF_GREETING';   # Import what we need

        (Note: you were probably astute enough to notice that, despite the
        HEREDOC marker being enclosed in single quotes, there is a "\n" at
        the end of one of the message definitions. This isn't an error; the
        message formatter will deal with that.)

        It is also possible to place messages in one or more groups by
        including the group tags in the argument list, before the messages
        are defined. Group tags *must* start with a colon (":").

    Example 3
            package My::App::Messages;
            use My::App::Messages;
            use message ':MESSAGES' => {
                INF_GREETING  => "I am completely operational, " .
                                 "and all my circuits are functioning perfectly.",
                RSP_DO_WHAT   => "What would you have me do?\n",
                NTC_FAULT     => "I've just picked up a fault in the %s unit.",
            };
            use message ':MESSAGES', ':ERRORS' => {
                CRT_NO_CAN_DO => "I'm sorry, %s. I'm afraid I can't do that",
            };
            1;

            # Meanwhile, back at main::
            use My::App::Messages ':ERRORS';    # Import the errors
            use My::App::Messages ':MESSAGE';   # Import everything

    Tagging messages causes your module's %EXPORT_TAGS hash to be updated,
    with tagged messages also being added to your module's @EXPORT_OK array.

    There is no expectation that you will make your package a descendant of
    the "Exporter" class. Provided you aren't working in the "main::"
    namespace then the calling package will be made a subclass of "Exporter"
    automatically, as soon as it becomes clear that it is necessary.

  Recap of the highlights
    This brief introduction demonstrates, hopefully, that as well as being
    able to function like constants, message strings are way more
    sophisticated than constants.

    Perhaps your Little Grey Cells have also helped you make a few important
    deductions:

    *   That the name not only identifies, but characterises a message.

    *   That different types of message exist.

    *   That handling is influenced by a message's type.

    *   That messages are simple text, or they may be parameterised.

    *   That calling context matters, particularly void context.

    You possibly have more questions. Certainly, there is more to the story
    and these are just the highlights. The module is described in greater
    detail below.

DESCRIPTION
    The "Message::String" pragma and its alias ("message") are aimed at the
    programmer who wishes to organise, identify, define, use (or make
    available for use) message strings specific to an application or other
    message domain. "Message::String" objects are not unlike constants, in
    fact, they may even be used like constants; they're just a smidge more
    helpful.

    Much of a script's lifetime is spent saying stuff, asking for stuff,
    maybe even complaining about stuff; but, most important of all, they
    have to do meaningful stuff, good stuff, the stuff they were designed to
    do.

    The trouble with saying, asking for, and complaining about stuff is the
    epic amount of repeated stuff that needs to be done just to do that kind
    of stuff. And that kind of stuff is like visual white noise when it's
    gets in the way of understanding and following a script's flow.

    We factor out repetetive code into reusable subroutines, web content
    into templates, but we do nothing about our script's messaging. Putting
    up with broken strings, quotes, spots and commas liberally peppered
    around the place as we compose and recompose strings doesn't seem to
    bother us.

    What if we could organise our application's messaging in a way that kept
    all of that noise out of the way? A way that allowed us to access
    messages using mnemonics but have useful, sensible and standard things
    happen when we do so. This module attempts to provide the tooling to do
    just that.

METHODS
    "Message::String" objects are created and injected into the symbol table
    during Perl's compilation phase so that they are accessible at runtime.
    Once the import method has done its job there is very little that may be
    done to meaningfully alter the identity, purpose or destiny of messages.

    A large majority of this module's methods, including constructors, are
    therefore notionally and conventionally protected. There are, however, a
    small number of public methods worth covering in this document.

  Public Methods
   import
        message->import();
        message->import( @options, @message_group, ... );
        message->import( @options, \%message_group, ... );
        message->import( @options, \@message_group, ... );
        message->import( @options, $message_group, ... );

    The "import" method is invoked at compile-time, whenever a "use message"
    or "use Message::String" directive is encountered. It processes any
    options and creates any requested messages, injecting message symbols
    into the caller's symbol table.

    Options

    "void"
        Makes the "void" operator available for use in the calling module.
        Since the active aspects of message handling are only triggered in
        void context, it provides an extra level of comfort to developers
        who are unsure whether a statement will be executed in the correct
        context.

        The "void" operator is essential if testing with messages.

        The "void" operator is provided by "Syntax::Feature::Void".

    "EXPORT"
        Ensures that the caller's @EXPORT list includes the names of
        messages defined in the following group.

            # Have the caller mandate that these messages be imported:
            #
            use message EXPORT => { ... };

    "EXPORT_OK"
        Ensures that the caller's @EXPORT_OK list includes the names of
        messages defined in the following group. The explicit use of
        "EXPORT_OK" is not necessary when tag groups are being used and its
        use is implied.

            # Have the caller make these messages importable individually and
            # upon request:
            #
            use message EXPORT_OK => { ... };

    ":*EXPORT-TAG*"
        One or more export tags may be listed, specifying that the following
        group of messages is to be added to the listed tag group(s). Any
        necessary updates to the caller's %EXPORT_TAGS hash and @EXPORT_OK
        array are made. The explicit use of "EXPORT_OK" is unnecessary since
        its use is implied.

        Tags may listed as separately or together in the same compound
        strings, though must be prefixed with a colon (":").

            # Grouping messages with a single tag:
            #
            use message ':FOO' => { ... };

            # Four valid ways to group messages with multiple tags:
            #
            use message ':FOO',':BAR' => { ... };
            use message ':FOO, :BAR' => { ... };
            use message ':FOO :BAR' => { ... };
            use message ':FOO:BAR' => { ... };

            # Gilding-the-lily; not wrong, but not necessary:
            #
            use message ':FOO', EXPORT_OK => { ... };

    Tag groups and other export options have no effect if the calling
    package is called "main::".

    If the calling package hasn't already been declared a subclass of
    "Exporter" then the "Exporter" package is loaded and the caller's @ISA
    array will be updated to include it as the first element.

    (To do: I should try to make this work with "Sub::Exporter".)

    Defining Messages

    A message is comprised of two tokens:

    The Message Identifier
        The message id should contain no whitespace characters, consist only
        of upper- and/or lowercase letters, digits, the underscore, and be
        valid as a Perl subroutine name. The id should *ideally* be unique;
        at the very least, it must be unique to the package in which it is
        defined.

        As well as naming a message, the message id is also used to
        determine the message type and severity. Try to organise your
        message catalogues using descriptive and consistent naming and type
        conventions.

        (Read the section about "MESSAGE TYPES" to see how typing works.)

    The Message Template
        The template is the text part of the message. It could be a simple
        string, or it could be a "sprintf" format complete with one or more
        parameter placeholders. A message may accept arguments, in which
        case "sprintf" will merge the argument values with the template to
        produce the final output.

    Messages are defined in groups of one or more key-value pairs, and the
    "import" method is quite flexible about how they are presented for
    processing.

    As a flat list of key-value pairs.
            use message 
                INF_GREETING  => "I am completely operational, " .
                                 "and all my circuits are functioning perfectly.",
                RSP_DO_WHAT   => "What would you have me do?\n",
                NTC_FAULT     => "I've just picked up a fault in the %s unit.",
                CRT_NO_CAN_DO => "I'm sorry, %s. I'm afraid I can't do that";

    As an anonymous hash, or hash reference.
            use message { 
                INF_GREETING  => "I am completely operational, " .
                                 "and all my circuits are functioning perfectly.",
                RSP_DO_WHAT   => "What would you have me do?\n",
                NTC_FAULT     => "I've just picked up a fault in the %s unit.",
                CRT_NO_CAN_DO => "I'm sorry, %s. I'm afraid I can't do that",
            };

    As an anonymous array, or array reference.
            use message [ 
                INF_GREETING  => "I am completely operational, " .
                                 "and all my circuits are functioning perfectly.",
                RSP_DO_WHAT   => "What would you have me do?\n",
                NTC_FAULT     => "I've just picked up a fault in the %s unit.",
                CRT_NO_CAN_DO => "I'm sorry, %s. I'm afraid I can't do that",
            ];

    As a string (perhaps using a HEREDOC).
            use message << 'EOF';
            INF_GREETING  I am completely operational,
            ...           and all my circuits are functioning perfectly.
            RSP_DO_WHAT   What would you have me do?\n
            NTC_FAULT     I've just picked up a fault in the %s unit.
            CRT_NO_CAN_DO I'm sorry, %s. I'm afraid I can't do that
            EOF

        When defining messages in this way, longer templates may be
        broken-up (as shown on the third line of the example above) by
        placing one or more dots (".") where a message id would normally
        appear. This forces the text fragment on the right to be appended to
        the template above, separated by a single space. Similarly, the
        addition symbol ("+") may be used in place of dot(s) if a newline is
        desired as the separator. This is particularly helpful when using
        PerlTidy and shorter line lengths.

    Multiple sets of export options and message groups may be added to the
    same import method's argument list:

        use message ':MESSAGES, :MISC' => (
            INF_GREETING  => "I am completely operational, " .
                             "and all my circuits are functioning perfectly.",
            RSP_DO_WHAT   => "What would you have me do?\n",
        ), ':MESSAGES, :NOTICES' => (
            NTC_FAULT     => "I've just picked up a fault in the %s unit.",
        ), ':MESSAGES, :ERRORS' => (
            CRT_NO_CAN_DO => "I'm sorry, %s. I'm afraid I can't do that",
        );

    When a message group has been processed any export related options that
    are currently in force will be reset; no further messages will be marked
    as exportable until a new set of export options and messages is added to
    the same directive.

    Pay attention when defining messages as simple lists of key-value pairs,
    as any new export option(s) will punctuate a list of messages up to that
    point and they will be processed as a complete group.

    The message parser will also substitute the following escape sequences
    with the correct character shown in parentheses:

    *   "\n" (newline)

    *   "\r" (linefeed)

    *   "\t" (tab)

    *   "\a" (bell)

    *   "\s" (space)

   id
        MESSAGE_ID->id;

    Gets the message's identifier.

   level
        MESSAGE_ID->level( $severity_int );
        MESSAGE_ID->level( $long_or_short_type_str );
        $severity_int = MESSAGE_ID->level;

    Sets or gets a message's severity level.

    The severity level is always returned as an integer value, while it may
    be set using an integer value or a type code (long or short) with the
    desired value.

    Example
            # Give my notice a higher severity, equivalent to a warning.

            NTC_FAULT->level(4);
            NTC_FAULT->level('W');
            NTC_FAULT->level('WARNING');

    (See "MESSAGE TYPES" for more informtion about typing.)

   output
        $formatted_message_str = MESSAGE_ID->output;

    Returns the formatted text produced last time a particular message was
    used, or it returnd "undef" if the message hasn't yet been issued. The
    message's "output" value would also include the values of any parameters
    passed to the message.

    Example
            # Package in which messages are defined.
            #
            package My::App::MsgRepo;
            use Message::String EXPORT_OK => {
                NTC_FAULT => 'I've just picked up a fault in the %s unit.',
            };

            1;

            # Package in which messages are required.
            #
            use My::App::MsgRepo qw/NTC_FAULT/;
            use Test::More;

            NTC_FAULT('AE-35');     # The message is issued...

            # Some time later...
            diag NTC_FAULT->output; # What was the last reported fault again?

            # Output:
            # I've just picked up a fault in the AE-35 unit.

   readmode
        MESSAGE_ID->readmode( $mode_str );
        MESSAGE_ID->readmode( $mode_int );
        $mode_int = MESSAGE_ID->readmode;

    Uses "Term::ReadKey" to set the terminal driver mode when getting the
    response from "STDIN". The terminal driver mode is restored to its
    "normal" state after the input is complete.

    Ostensibly, this method is intended for use with Type R (Response)
    messages, specifically to switch off TTY echoing for password entry. You
    should, however, never need to use explicitly if the text *"password"*
    is contained within the message's template, as its use is implied.

    Example
            RSP_MESSAGE->readmode('noecho');

   response
        $response_str = MESSAGE_ID->response;

    Returns the input given in response to the message last time it was
    used, or it returns "undef" if the message hasn't yet been isssued.

    The "response" accessor is only useful with Type R (Response) messages.

    Example
            # Package in which messages are defined.
            #
            package My::App::MsgRepo;
            use Message::String EXPORT_OK => {
                INF_GREETING => 'Welcome to the machine.',
                RSP_USERNAME => 'Username: ',
                RSP_PASSWORD => 'Password: ',
            };

            # Since RSP_PASSWORD is a response and contains the word "password",
            # the response is not echoed to the TTY.
            #
            # RSP_PASSWORD->readmode('noecho') is implied.

            1;

            # Package in which messages are required.
            #
            use My::App::MsgRepo qw/INF_GREETING RSP_USERNAME RSP_PASSWORD/;
            use DBI;

            INF_GREETING;       # Pleasantries
            RSP_USERNAME;       # Prompt for and fetch username
            RSP_PASSWORD;       # Prompt for and fetch password

            $dbh = DBI->connect( 'dbi:mysql:test;host=127.0.0.1',
                RSP_USERNAME->response, RSP_PASSWORD->response )
              or die $DBI::errstr;

   severity
        MESSAGE_ID->severity( $severity_int );
        MESSAGE_ID->severity( $long_or_short_type_str );
        $severity_int = MESSAGE_ID->severity;

    (An alias for the "level" method.)

   template
        MESSAGE_ID->template( $format_or_text_str );
        $format_or_text_str = MESSAGE_ID->template;

    Sets or gets the message template. The template may be a plain string of
    text, or it may be a "sprintf" format containing parameter placeholders.

    Example
            # Redefine our message templates.

            INF_GREETING->template('Ich bin völlig funktionsfähig, und alle meine '
                . 'Schaltungen sind perfekt funktioniert.');
            CRT_NO_CAN_DO->template('Tut mir leid, %s. Ich fürchte, ich kann das '
                . 'nicht tun.');
    
            # Some time later...
    
            INF_GREETING;
            CRT_NO_CAN_DO('Dave');

   to_string
        $output_or_template_str = MESSAGE_ID->to_string;

    Gets the string value of the message. If the message has been issued
    then you get the message output, complete with any message parameter
    values. If the message has not yet been issued then the message template
    is returned.

    Message objects overload the stringification operator ("") and it is
    this method that will be called whenever the string value of a message
    is required.

    Example
            print INF_GREETING->to_string . "\n"; 
    
            # Or, embrace your inner lazy:

            print INF_GREETING . "\n";

   type
        MESSAGE_ID->type( $long_or_short_type_str );
        $short_type_str = MESSAGE_ID->type;

    Gets or sets a message's type characteristics, which includes its
    severity level.

    Example
            # Check my message's type

            $code = NTC_FAULT->type;    # Returns "N"

            # Have my notice behave more like a warning.

            NTC_FAULT->type('W');
            NTC_FAULT->type('WARNING');

   verbosity
        MESSAGE_ID->type( $severity_int );
        MESSAGE_ID->type( $long_or_short_type_str );
        $severity_int = MESSAGE_ID->verbosity;

    Gets or sets the level above which messages will not be issued. Messages
    above this level may still be generated and their values are still
    usable, but they are silenced.

    *You cannot set the verbosity level to a value lower than a standard
    Type E (Error) message.*

    Example
            # Only issue Alert, Critical, Error and Warning messages.

            message->verbosity('WARNING');  # Or ...
            message->verbosity('W');        # Or ...
            message->verbosity(4);

   overloaded ""
        $output_or_template_str = MESSAGE_ID;

    Message objects overload Perl's *stringify* operator, calling the
    "to_string" method.

MESSAGE TYPES
    Messages come in an nine great flavours, each identified by a
    single-letter type code. A message's type represents the severity of the
    condition that would cause the message to be issued:

   Type Codes
        Type  Alt   Level /   Type
        Code  Type  Priority  Description
        ----  ----  --------  ---------------------
        A     ALT      1      Alert
        C     CRT      2      Critical
        E     ERR      3      Error
        W     WRN      4      Warning
        N     NTC      5      Notice
        I     INF      6      Info
        D     DEB      7      Debug (or diagnostic)
        R     RSP      1      Response
        M     MSG      6      General message

  How messages are assigned a type
    When a message is defined an attempt is made to discern its type by
    examining it for a series of clues:

    Step 1: check for a suffix matching "/:([DRAWNMICE])$/"
        The *type override* suffix spoils the fun by removing absolutely all
        of the guesswork from the process of assigning type characteristics.
        It is kind of ugly but removes absolutely all ambiguity. It is
        somewhat special in that it does not form part of the message's
        identifier, which is great if you have to temporarily re-type a
        message but don't want to hunt down and change every occurrence of
        its use.

        This suffix is a great substitute for limited imaginative faculties
        when naming messages.

    Step 2: check for a suffix matching "/[_\d]([WINDCREAM])$/"
        This step, like the following three steps, uses information embedded
        within the identifier to determine the type of the message. Since
        message ids are meant to be mnemonic, at least some attempt should
        be made by message authors to convey purpose and meaning in their
        choice of id.

    Step 3: check for a prefix matching "/^([RANCIDMEW])[_\d]/"
    Step 4: check for a suffix matching "/(*ALTERNATION*)$/", where the
    alternation set is comprised of long type codes (see "Long Type Codes").
    Step 5: check for a prefix matching "/^(*ALTERNATION*)/", where the
    alternation set is comprised of long type codes (see "Long Type Codes").
    Step 6: as a last resort the message is characterised as Type-M (General
    Message).

   Long Type Codes
    In addition to having a single-letter type code, longer type code aliase
    may be used to describe their types. In fact, the public interface often
    allows for the use of the longer type code aliases where a type code may
    be used for reasons of clarity.

    We can use one of this package's protected methods ("_types_by_alias")
    to not only list the type code aliases but to reveal type code
    equivalence:

        use Test::More;
        use Data::Dumper::Concise;
        use Message::String;
    
        diag Dumper( { message->_types_by_alias } );
    
        # {
        #   ALERT => "A",
        #   ALR => "A",
        #   ALT => "A",
        #   CRIT => "C",
        #   CRITICAL => "C",
        #   CRT => "C",
        #   DEB => "D",
        #   DEBUG => "D",
        #   DGN => "D",
        #   DIAGNOSTIC => "D",
        #   ERR => "E",
        #   ERROR => "E",
        #   FATAL => "C",
        #   FTL => "C",
        #   INF => "I",
        #   INFO => "I",
        #   INP => "R",
        #   INPUT => "R",
        #   MESSAGE => "M",
        #   MISC => "M",
        #   MSC => "M",
        #   MSG => "M",
        #   NOT => "N",
        #   NOTICE => "N",
        #   NTC => "N",
        #   OTH => "M",
        #   OTHER => "M",
        #   OTR => "M",
        #   PRM => "R",
        #   PROMPT => "R",
        #   RES => "R",
        #   RESPONSE => "R",
        #   RSP => "R",
        #   WARN => "W",
        #   WARNING => "W",
        #   WNG => "W",
        #   WRN => "W"
        # }

  Changing a message's type
    Under exceptional conditions it may be necessary to alter a message's
    type, and this may be achieved in one of three ways:

    1. *Permanently,* by choosing a more suitable identifier.
        This is the cleanest way to make such a permanent change, and has
        only one disadvantage: you must hunt down code that uses the old
        identifier and change it. Fortunately, "grep" is our friend and
        constants are easy to track down.

    2. *Semi-permanently,* by using a type-override suffix.
            # Change NTC_FAULT from being a notice to a response, so that it 
            # blocks for input. We may still use the "NTC_FAULT" identifier.

            use message << 'EOF';
            NTC_FAULT:R   I've just picked up a fault in the %s unit.
            EOF

        Find the original definition and append the type-override suffix,
        which must match regular expression "/:[CREWMANID]$/", obviously
        being careful to choose the correct type code. This has a cosmetic
        advantage in that the suffix will be effective but not be part of
        the the id. The disadvantage is that this can render any forgotten
        changes invisible, so don't forget to change it back when you're
        done.

    3. *Temporarily,* at runtime, using the message's "type" mutator:
            # I'm debugging an application and want to temporarily change
            # a message named APP234I to be a response so that, when it displays,
            # it blocks waiting for input -
    
            APP234I->type('R');         # Or, ...
            APP234I->type('RSP');       # Possibly much clearer, or ...
            APP234I->type('RESPONSE');  # Clearer still

WHISTLES, BELLS & OTHER DOODADS
  Customising message output
   Embedding timestamps
        MESSAGE_ID->_default_timestamp_format($strftime_format_str);
        MESSAGE_ID->_type_timestamp($type_str, '');
        MESSAGE_ID->_type_timestamp($type_str, 1);
        MESSAGE_ID->_type_timestamp($type_str, $strftime_format_str);
        MESSAGE_ID->_type_timestamp('');
        MESSAGE_ID->_type_timestamp(1);

   Embedding type information
        MESSAGE_ID->_type_tlc($type_str, '');
        MESSAGE_ID->_type_tlc($type_str, $three_letter_code_str);

   Embedding the message id
        MESSAGE_ID->_type_id($type_str, $bool);
        MESSAGE_ID->_type_id($bool);

ACKNOWLEDGEMENTS
    Standing as we all do from time to time on the shoulders of giants:

    Dave Rolsky*, et al.*
        For DateTime

    Eric Brine
        For Syntax::Feature::Void.

    Graham Barr*, et al.*
        For Scalar::Util and Sub::Util

    Jens Reshack*, et al.*
        For List::MoreUtils.

    Jonathon Stowe & Kenneth Albanowski
        For Term::ReadKey.

    Ray Finch
        For Clone

    Robert Sedlacek*, et al.*
        For namespace::clean

AUTHOR
    Iain Campbell <cpanic@cpan.org>

COPYRIGHT AND LICENSE
    This software is copyright (c) 2015 by Iain Campbell.

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

