File Coverage

blib/lib/Message/String.pm
Criterion Covered Total %
statement 24 24 100.0
branch n/a
condition n/a
subroutine 8 8 100.0
pod n/a
total 32 32 100.0


line stmt bran cond sub pod time code
1 1     1   92473 use strict;
  1         3  
  1         37  
2 1     1   6 use warnings;
  1         2  
  1         201  
3              
4             package Message::String;
5             our $VERSION = '0.1.7'; # VERSION
6             # ABSTRACT: A pragma to declare and organise messaging.
7 1     1   1075 use Clone ( 'clone' );
  1         4024  
  1         88  
8 1     1   1426 use DateTime ();
  1         183719  
  1         52  
9 1     1   13 use List::MoreUtils ( 'distinct' );
  1         3  
  1         25  
10 1     1   689 use Scalar::Util ( 'reftype' );
  1         2  
  1         72  
11 1     1   931 use Sub::Util ( 'set_subname' );
  1         281  
  1         207  
12 1     1   694 use Syntax::Feature::Void;
  1         7625  
  1         15  
13             use Term::ReadKey;
14             use namespace::clean;
15             use overload ( fallback => 1, '""' => 'to_string' );
16              
17             BEGIN {
18             # Set up "messages" pragma as a "Message::String" alias.
19             *message:: = *Message::String::;
20              
21             # ... and prevent Perl from having a hissy-fit the first time
22             # a "use message ..." directive is encountered.
23             $INC{'message.pm'} = "(set by @{[__PACKAGE__]})";
24              
25             # We're eating-our-own-dog-food at the end of this module, but we
26             # will still need these three subroutines declaring before we can
27             # use them.
28             sub C_EXPECT_HAREF_OR_KVPL;
29             sub C_BAD_MESSAGE_ID;
30             sub C_MISSING_TEMPLATE;
31              
32             # Messages come in eight basic flavours (or types):
33             #
34             # A (Severity 1: Alert)
35             # C (Severity 2: Critical)
36             # E (Severity 3: Error)
37             # W (Severity 4: Warning)
38             # N (Severity 5: Notice)
39             # I (Severity 6: Info)
40             # D (Severity 7: Diagnostic, or Debug)
41             # R (Severity 1: Response, or Prompt)
42             # M (Severity 6: Other, or Miscellaneous)
43             #
44             # Listed in that order for no other reason than it spells DINOCREW,
45             # which is kind of sad but easy to remember. Messages are handled
46             # in different ways and according to type and some of the more
47             # important type characteristics are defined in this table:
48             #
49             # level
50             # The verbosity or severity level. By default these align with
51             # syslog message levels, with the exception of package-spefic
52             # types 'M' and 'R'.
53             # timestamp
54             # Embed a timestamp in formatted message. May be '0' (No - default),
55             # '1' (Yes, using default "strftime" format), or a custom "strftime"
56             # format string.
57             # tlc
58             # Nothing quite as nice as Tender Love and Care, but the three-letter
59             # code that can be embedded in the formatted message (e.g. 'NTC'
60             # would, by default, be rendered as '*NTC*').
61             # id
62             # A boolean determining whether or not the message identifer is
63             # embedded withing the text of the formatted message.
64             # issue
65             # A reference to the method that the issuer will use to get the
66             # rendered message out into the cold light of day.
67             # aliases
68             # A reference to a list of longer codes that the message constructor
69             # will fallback to when attempting to discern the message's type from
70             # its identifier. It first tries to determine if the message id is
71             # suffixed by a type code following a dash, digit or underscore. Then
72             # it checks for a type code followed by a dash, digit, or underscore.
73             # If neith of those checks is conclusive, it then checks to see if the
74             # id ends or begins with one of the type aliases listed in this table,
75             # and if that is also inconclisove then 'M' (Other) is assumed.
76             #<<<
77             my $types = {
78             A => {
79             level => 1, timestamp => 0, tlc => '', id => 1,
80             issue => \&_alert,
81             aliases => [qw/ALT ALR ALERT/]
82             },
83             C => {
84             level => 2, timestamp => 0, tlc => '', id => 1,
85             issue => \&_crit,
86             aliases => [qw/CRT CRITICAL CRIT FATAL FTL/]
87             },
88             E => {
89             level => 3, timestamp => 0, tlc => '', id => 0,
90             issue => \&_err,
91             aliases => [qw/ERR ERROR/]
92             },
93             W => {
94             level => 4, timestamp => 0, tlc => '', id => 0,
95             issue => \&_warning,
96             aliases => [qw/WRN WARNING WNG WARN/]
97             },
98             N => {
99             level => 5, timestamp => 0, tlc => '', id => 0,
100             issue => \&_notice,
101             aliases => [qw/NTC NOTICE NOT/]
102             },
103             I => {
104             level => 6, timestamp => 0, tlc => '', id => 0,
105             issue => \&_info,
106             aliases => [qw/INF INFO/]
107             },
108             D => {
109             level => 7, timestamp => 0, tlc => '', id => 0,
110             issue => \&_diagnostic,
111             aliases => [qw/DEB DEBUG DGN DIAGNOSTIC/]
112             },
113             R => {
114             level => 1, timestamp => 0, tlc => '', id => 0,
115             issue => \&_prompt,
116             aliases => [qw/RSP RESPONSE RES PROMPT PRM INPUT INP/]
117             },
118             M => {
119             level => 6, timestamp => 0, tlc => '', id => 0,
120             issue => \&_other,
121             aliases => [qw/MSG MESSAGE OTHER MISC OTH OTR MSC/]
122             },
123             };
124             #>>>
125              
126             # _initial_types
127             # In list context, returns the initial list of message type codes
128             # as an array.
129             # In scalar context, returns the initial list of message type codes
130             # as a string suitable for use in a Regex character class ([...]).
131             my @base_types = sort { $a cmp $b } keys %$types;
132             my $base_types = join '', @base_types;
133              
134             sub _initial_types
135             {
136             return wantarray ? @base_types : $base_types;
137             }
138              
139             # _types
140             # Some of our methods require access to data presented in the message
141             # types table, defined above (see "$types"), either to manipulate it
142             # or simply to use the values. Many of these methods may be used as
143             # class and instance methods ('_type_level', '_type_id', to name two
144             # of them). Most of the time, this table is the single source of
145             # truth, that is unless AN INSTANCE attempts to use one of those
146             # methods to modifiy the data. Under those specific circumstances,
147             #  the the message instance's gets its own copy of the type table
148             # loaded into its 'types' attribute before being modified --
149             # copy on write semantics, if you will -- and that data, not the global
150             # data, is used by that instance. That local data is purged if the
151             # instance ever changes its message type. It is the job of this method
152             # to copy (if required) the data required by an instance and/or return
153             # that data as an instance's view of its context, or to return the a
154             # reference to the global data.
155             sub _types
156             {
157             my ( $invocant, $bool_copy ) = @_;
158             return $types unless ref $invocant;
159             return $types unless $bool_copy || exists $invocant->{types};
160             $invocant->{types} = clone( $types )
161             unless exists $invocant->{types};
162             return $invocant->{types};
163             }
164              
165             # _reset
166             # If called as an instance method, restores the instance to a reasonably
167             # pristine state.
168             # If called as a class method, restores the global type data to its
169             # pristine state.
170             my $types_backup = clone( $types );
171              
172             sub _reset
173             {
174             my ( $invocant ) = @_;
175             if ( ref $invocant ) {
176             for my $key ( keys %$invocant ) {
177             delete $invocant->{$key}
178             unless $key =~ m{^(?:template|level|type|id)$};
179             }
180             my $type = $invocant->type;
181             $type = 'M'
182             unless defined( $type ) && exists $types->{$type};
183             $invocant->level( $types->{$type}{level} );
184             }
185             else {
186             $types = clone( $types_backup );
187             }
188             return $invocant;
189             }
190              
191             # _message_types
192             # In list context, returns the current list of message type codes
193             # as an array.
194             # In scalar context, returns the current list of message type codes
195             # as a string suitable for use in a Regex character class ([...]).
196             sub _message_types
197             {
198             my ( $invocant ) = @_;
199             my $types = $invocant->_types;
200             my @types = sort { $a cmp $b } keys %$types;
201             return @types
202             if wantarray;
203             return join '', @types;
204             }
205              
206             # _type_level
207             # Inspect or change the "level" setting (verbosity level) for a
208             # message type.
209             # * Be careful when calling this as an instance method as copy-on-
210             # write semantics come into play (see "_types" for more information).
211             sub _type_level
212             {
213             my ( $invocant, $type, $value ) = @_;
214             if ( @_ > 1 && defined( $type ) ) {
215             my $types = $invocant->_types( @_ > 2 );
216             $type = uc( $type );
217             if ( @_ > 2 ) {
218             return $invocant
219             if !ref( $invocant ) && $type =~ m{^[ACEW]$};
220             $types->{$type}{level}
221             = ( 0 + $value ) || $types->{$type}{level};
222             $invocant->level( $types->{ $invocant->{type} }{level} )
223             if ref $invocant;
224             return $invocant;
225             }
226             return $types->{$type}{level}
227             if exists $types->{$type};
228             }
229             return undef;
230             }
231              
232             # _type_id
233             # Inspect or change the "id" setting (whether the id appears in the
234             # formatted text) for a message type.
235             # * Be careful when calling this as an instance method as copy-on-
236             # write semantics come into play (see "_types" for more information).
237             sub _type_id
238             {
239             my ( $invocant, $type, $value ) = @_;
240             if ( @_ > 1 && defined( $type ) ) {
241             my $types = $invocant->_types( @_ > 2 );
242             $type = uc( $type );
243             if ( @_ > 2 ) {
244             $types->{$type}{id} = !!$value;
245             return $invocant;
246             }
247             if ( $type eq '1' || $type eq '0' || $type eq '' ) {
248             $types->{$_}{id} = !!$type for keys %$types;
249             return $invocant;
250             }
251             return $types->{$type}{id}
252             if exists $types->{$type};
253             }
254             return undef;
255             }
256              
257             # _type_timestamp
258             # Inspect or change the "timestamp" setting (whether and how the time
259             # appears in the formatted text) for a message type.
260             # * Be careful when calling this as an instance method as copy-on-
261             # write semantics come into play (see "_types" for more information).
262             sub _type_timestamp
263             {
264             my ( $invocant, $type, $value ) = @_;
265             if ( @_ > 1 && defined( $type ) ) {
266             my $types = $invocant->_types( @_ > 2 );
267             $type = uc( $type );
268             if ( @_ > 2 ) {
269             $types->{$type}{timestamp} = $value || '';
270             return $invocant;
271             }
272             if ( $type eq '1' || $type eq '0' || $type eq '' ) {
273             $types->{$_}{timestamp} = $type for keys %$types;
274             return $invocant;
275             }
276             return $types->{$type}{timestamp}
277             if exists $types->{$type};
278             }
279             return undef;
280             }
281              
282             # _type_tlc
283             # Inspect or change the "tlc" setting (whether and what three-letter code
284             # appears in the formatted text) for a message type.
285             # * Be careful when calling this as an instance method as copy-on-
286             # write semantics come into play (see "_types" for more information).
287             sub _type_tlc
288             {
289             my ( $invocant, $type, $value ) = @_;
290             if ( @_ > 1 && defined( $type ) ) {
291             my $types = $invocant->_types( @_ > 2 );
292             $type = uc( $type );
293             if ( @_ > 2 ) {
294             $value ||= '';
295             $value = substr( $value, 0, 3 )
296             if length( $value ) > 3;
297             $types->{$type}{tlc} = $value;
298             return $invocant;
299             }
300             return $types->{$type}{tlc}
301             if exists $types->{$type};
302             }
303             return undef;
304             }
305              
306             # _type_aliases
307             # Inspect or change the "aleiases" setting for a message type.
308             # * Be careful when calling this as an instance method as copy-on-
309             # write semantics come into play (see "_types" for more information).
310             sub _type_aliases
311             {
312             my ( $invocant, $type, $value ) = @_;
313             if ( @_ > 1 && defined( $type ) ) {
314             my $types = $invocant->_types( @_ > 2 );
315             $type = uc( $type );
316             if ( @_ > 2 ) {
317             my $tlc = $invocant->_type_tlc( $type );
318             $value = []
319             unless $value;
320             $value = [$value]
321             unless ref $value;
322             $types->{$type}{aliases} = $value;
323             return $invocant;
324             }
325             if ( exists $types->{$type} ) {
326             return @{ $types->{$type}{aliases} } if wantarray;
327             return $types->{$type}{aliases};
328             }
329             }
330             return wantarray ? () : undef;
331             }
332              
333             # _types_by_alias
334             # In list context, returns a hash of aliases and their correspondin
335             # message type codes.
336             sub _types_by_alias
337             {
338             my ( $invocant ) = @_;
339             my $types = $invocant->_types;
340             my %long_types;
341             for my $type ( keys %$types ) {
342             %long_types
343             = ( %long_types, map { $_ => $type } @{ $types->{$type}{aliases} } );
344             $long_types{ $types->{$type}{tlc} } = $type
345             if $types->{$type}{tlc};
346             }
347             return wantarray ? %long_types : \%long_types;
348             }
349              
350             # _update_type_on_id_change
351             # Check or change whether or not message types are set automatically
352             # when message ids are set. The cascade is enabled by default.
353             my $auto_type = 1;
354              
355             sub _update_type_on_id_change
356             {
357             my ( $invocant, $value ) = @_;
358             return $auto_type
359             unless @_ > 1;
360             $auto_type = !!$value;
361             return $invocant;
362             }
363              
364             my $auto_level = 1;
365              
366             # _update_level_on_type_change
367             # Check or change whether or not message levels are set automatically
368             # when message types are set. The cascade is enabled by default.
369             sub _update_level_on_type_change
370             {
371             my ( $invocant, $value ) = @_;
372             return $auto_level
373             unless @_ > 1;
374             $auto_level = !!$value;
375             return $invocant;
376             }
377              
378             # _minimum_verbosity
379             # Returns the minimum verbosity level, always the same level as
380             # error messages.
381             my $min_verbosity = __PACKAGE__->_type_level( 'E' );
382              
383             sub _minimum_verbosity {$min_verbosity}
384              
385             # _verbosity
386             # Returns the current verbosity level, which is greater than or
387             # equal to the severity level of all messages to be issued.
388             my $cur_verbosity = __PACKAGE__->_type_level( 'D' );
389              
390             sub verbosity
391             {
392             my ( $invocant, $value ) = @_;
393             return $cur_verbosity
394             unless @_ > 1;
395             if ( $value =~ /^\d+$/ ) {
396             $cur_verbosity = 0 + $value;
397             }
398             else {
399             my $types = $invocant->_types;
400             $value = uc( $value );
401             if ( length( $value ) > 1 ) {
402             my $long_types = $invocant->_types_by_alias;
403             $value = $long_types->{$value} || 'D';
404             }
405             $value = $types->{$value}{level}
406             if index( $invocant->_message_types, $value ) > -1;
407             $cur_verbosity = 0 + ( $value || 0 );
408             }
409             $cur_verbosity = $min_verbosity
410             if $cur_verbosity < $min_verbosity;
411             return $invocant;
412             }
413              
414             # _default_timestamp_format
415             # Check or change the default timestamp format.
416             my $timestamp_format = '%a %x %T';
417              
418             sub _default_timestamp_format
419             {
420             my ( $invocant, $value ) = @_;
421             return $timestamp_format
422             unless @_ > 1;
423             $timestamp_format = $value || '';
424             return $invocant;
425             }
426              
427             # _alert
428             # The handler used by the message issuer ("issue") to deliver
429             # an "alert" message.
430             sub _alert
431             {
432             my ( $message ) = @_;
433             @_ = $message->{output};
434             require Carp;
435             goto &Carp::confess;
436             }
437              
438             # _crit
439             # The handler used by the message issuer ("issue") to deliver
440             # a "critical" message.
441             sub _crit
442             {
443             my ( $message ) = @_;
444             @_ = $message->{output};
445             require Carp;
446             goto &Carp::confess;
447             }
448              
449             # _err
450             # The handler used by the message issuer ("issue") to deliver
451             # an "error" message.
452             sub _err
453             {
454             my ( $message ) = @_;
455             @_ = $message->{output};
456             require Carp;
457             goto &Carp::croak;
458             }
459              
460             # _warning
461             # The handler used by the message issuer ("issue") to deliver
462             # a "warning" message.
463             sub _warning
464             {
465             my ( $message ) = @_;
466             @_ = $message->{output};
467             require Carp;
468             goto &Carp::carp;
469             }
470              
471             # _notice
472             # The handler used by the message issuer ("issue") to deliver
473             # a "notice" message.
474             sub _notice
475             {
476             my ( $message ) = @_;
477             print STDERR "$message->{output}\n";
478             return $message;
479             }
480              
481             # _info
482             # The handler used by the message issuer ("issue") to deliver
483             # an "info" message.
484             sub _info
485             {
486             my ( $message ) = @_;
487             print STDOUT "$message->{output}\n";
488             return $message;
489             }
490              
491             # _diagnostic
492             # The handler used by the message issuer ("issue") to deliver
493             # a "diagnostic" message.
494             #
495             # Diagnostic messages are, by default, issueted using a TAP-friendly
496             # prefix ('# '), making them helpful in test modules.
497             sub _diagnostic
498             {
499             my ( $message ) = @_;
500             print STDOUT "# $message->{output}\n";
501             return $message;
502             }
503              
504             # _prompt
505             # The handler used by the message issuer ("issue") to deliver
506             # a "response" message.
507             #
508             # Response messages are displayed and will block until a response
509             # is received from stdin. The response is accessible via the
510             # message's response method and, initially, also via Perl's "$_"
511             # variable.
512             *Message::String::INPUT = \*STDIN;
513              
514             sub _prompt
515             {
516             my ( $message ) = @_;
517             print STDOUT "$message->{output}";
518             ReadMode( $message->readmode, \*Message::String::INPUT );
519             chomp( $message->{response} = );
520             ReadMode( 'normal', \*Message::String::INPUT );
521             $_ = $message->{response};
522             return $message;
523             }
524              
525             # _other
526             # The handler used by the message issuer ("issue") to deliver
527             # any other type of message.
528             sub _other
529             {
530             my ( $message ) = @_;
531             print STDOUT "$message->{output}\n";
532             return $message;
533             }
534              
535             # _should_be_issued
536             # Returns 1 if the issuer should go ahead and issue to an
537             # issueter to deliver the message.
538             # Returns 0 if the issuer should just quietly return the
539             # message object.
540             #
541             # Messages are normally issueted (a) in void context (i.e. it is
542             # clear from their usage that the message should "do" something), and
543             # (b) if the message severity level is less than or equal to the
544             # current verbosity level.
545             sub _should_be_issued
546             {
547             my ( $message, $wantarray ) = @_;
548             return 0 if defined $wantarray;
549             return 0 if $message->verbosity < $message->_type_level( $message->type );
550             return 1;
551             }
552              
553             # _issue
554             # The message issuer. Oversees formatting, decision as to whether
555             # to issue, or return message object, and how to issue.
556             sub _issue
557             {
558             my ( $message ) = &_format; # Simply call "_format" using same "@_"
559             return $message unless $message->_should_be_issued( wantarray );
560             my $types = $message->_types;
561             my $type = $message->type;
562             my $issue_using = $types->{$type}{issue}
563             if exists $types->{$type};
564             $issue_using = \&_other unless $issue_using;
565             @_ = $message;
566             goto &$issue_using;
567             }
568              
569             # _format
570             # Format the message's "output" attribute ready for issue.
571             sub _format
572             {
573             my ( $message, @args ) = @_;
574             my $txt = '';
575             $txt .= $message->_message_timestamp_text
576             if $message->_type_timestamp( $message->type );
577             $txt .= $message->_message_tlc_text
578             if $message->_type_tlc( $message->type );
579             $txt .= $message->_message_id_text
580             if $message->_type_id( $message->type );
581             if ( @args ) {
582             $txt .= sprintf( $message->{template}, @args );
583             }
584             else {
585             $txt .= $message->{template};
586             }
587             $message->output( $txt );
588             return $message;
589             }
590              
591             # _message_timestamp_text
592             # Returns the text used to represent time in the message's output.
593             sub _message_timestamp_text
594             {
595             my ( $message ) = @_;
596             my $timestamp_format = $message->_type_timestamp( $message->type );
597             my $time = DateTime->now;
598             return $time->strftime( $message->_default_timestamp_format ) . ' '
599             if $timestamp_format eq '1';
600             return $time->strftime( $timestamp_format ) . ' ';
601             }
602              
603             # _message_tlc_text
604             # Returns the text used to represent three-letter type code in the
605             # message's output.
606             sub _message_tlc_text
607             {
608             my ( $message ) = @_;
609             my $tlc = $message->_type_tlc( $message->type );
610             return sprintf( '*%s* ', uc( $tlc ) );
611             }
612              
613             # _prepend_message_id
614             # Returns the text used to represent the identity of the message
615             # being output.
616             sub _message_id_text
617             {
618             my ( $message ) = @_;
619             return sprintf( '%s ', uc( $message->id ) );
620             }
621              
622             # id
623             # Set or get the message's identity. The identity must be a valid Perl
624             # subroutine identifier.
625              
626             my %bad_identifiers = map +( $_, 1 ), qw/
627             BEGIN INIT CHECK END DESTROY
628             AUTOLOAD STDIN STDOUT STDERR ARGV
629             ARGVOUT ENV INC SIG UNITCHECK
630             __LINE__ __FILE__ __PACKAGE__ __DATA__ __SUB__
631             __END__ __ANON__
632             /;
633              
634             sub id
635             {
636             my ( $message, $value ) = @_;
637             return $message->{id}
638             unless @_ > 1;
639             my $short_types = $message->_message_types;
640             my $type;
641             if ( $value =~ m{(^.+):([${short_types}])$} ) {
642             ( $value, $type ) = ( $1, $2 );
643             }
644             C_BAD_MESSAGE_ID( $value )
645             unless $value && $value =~ /^[\p{Alpha}_\-][\p{Digit}\p{Alpha}_\-]*$/;
646             C_BAD_MESSAGE_ID( $value )
647             if exists $bad_identifiers{$value};
648             if ( $message->_update_type_on_id_change ) {
649             if ( $type ) {
650             $message->type( $type );
651             }
652             else {
653             if ( $value =~ /[_\d]([${short_types}])$/ ) {
654             $message->type( $1 );
655             }
656             elsif ( $value =~ /^([${short_types}])[_\d]/ ) {
657             $message->type( $1 );
658             }
659             else {
660             my %long_types = $message->_types_by_alias;
661             my $long_types = join '|',
662             sort { length( $b ) <=> length( $a ) } keys %long_types;
663             if ( $value =~ /(${long_types})$/ ) {
664             $message->type( $long_types{$1} );
665             }
666             elsif ( $value =~ /^(${long_types})/ ) {
667             $message->type( $long_types{$1} );
668             }
669             else {
670             $message->type( 'M' );
671             }
672             }
673             }
674             }
675             $message->{id} = $value;
676             return $message;
677             } ## end sub id
678             } ## end BEGIN
679              
680             # _export_messages
681             # Oversees the injection of message issuers into the target namespace.
682             #
683             # If messages are organised into one or more tag groups, then this method
684             # also ensuring that the target namespace is an Exporter before updating
685             # the @EXPORT_OK, %EXPORT_TAGS in that namespace with details of the
686             # messages being injected. To be clear, messages must be grouped before
687             # this method stomps over the target namespace's @ISA, @EXPORT_OK, and
688             # %EXPORT_TAGS.
689             #
690             # The "main" namespace is an exception in that it never undergoes any
691             # Exporter-related updates.
692             sub _export_messages
693             {
694             no strict 'refs';
695             my ( $package, $params ) = @_;
696             my ( $ns, $messages, $export_tags, $export_ok, $export )
697             = @{$params}{qw/namespace messages export_tags export_ok export/};
698             for my $message ( @$messages ) {
699             $message->_inject_into_namespace( $ns );
700             }
701             $package->_refresh_namespace_export_tags( $ns, $export_tags, $messages )
702             if ref( $export_tags ) && @$export_tags;
703             $package->_refresh_namespace_export_ok( $ns, $messages )
704             if $export_ok;
705             $package->_refresh_namespace_export( $ns, $messages )
706             if $export;
707             return $package;
708             }
709              
710             # _inject_into_namespace_a_message
711             # Clone the issuer and inject an appropriately named clone into
712             # the tartget namespace. Cloning helps avoid the pitfalls associated
713             # with renaming duplicate anonymous code references.
714             sub _inject_into_namespace
715             {
716             no strict 'refs';
717             my ( $message, $ns ) = @_;
718             my ( $id, $type ) = @{$message}{ 'id', 'type' };
719             my $sym = "$ns\::$id";
720             $sym =~ s/-/_/g;
721             # Clone the issuer, otherwise naming the __ANON__ function could
722             # be a little dicey!
723             my $clone = sub {
724             # Must "close over" message to clone.
725             @_ = ( $message, @_ ); # Make sure we pass the message on
726             goto &_issue; # ... and keep the calling frame in-tact!
727             };
728             # Name and inject the message issuer
729             *$sym = set_subname( $sym => $clone );
730             # Record the message provider and rebless the message
731             $message->_provider( $ns )->_rebless( "$ns\::Message::String" );
732             return $message;
733             }
734              
735             # _refresh_namespace_export
736             # Updates the target namespace's @EXPORT, adding the names of any
737             # message issuers.
738             sub _refresh_namespace_export
739             {
740             no strict 'refs';
741             my ( $package, $ns, $messages ) = @_;
742             return $package
743             unless $package->_ensure_namespace_is_exporter( $ns );
744             my @symbols = map { $_->{id} } @$messages;
745             @{"$ns\::EXPORT"}
746             = distinct( @symbols, @{"$ns\::EXPORT"} );
747             return $package;
748             }
749              
750             # _refresh_namespace_export_ok
751             # Updates the target namespace's @EXPORT_OK, adding the names of any
752             # message issuers.
753             sub _refresh_namespace_export_ok
754             {
755             no strict 'refs';
756             my ( $package, $ns, $messages ) = @_;
757             return $package
758             unless $package->_ensure_namespace_is_exporter( $ns );
759             my @symbols = map { $_->{id} } @$messages;
760             @{"$ns\::EXPORT_OK"}
761             = distinct( @symbols, @{"$ns\::EXPORT_OK"} );
762             return $package;
763             }
764              
765             # _refresh_namespace_export_tags
766             # Updates the target namespace's %EXPORT_TAGS, adding the names of any
767             # message issuers.
768             sub _refresh_namespace_export_tags
769             {
770             no strict 'refs';
771             my ( $package, $ns, $export_tags, $messages ) = @_;
772             return $package
773             unless $package->_ensure_namespace_is_exporter( $ns );
774             return $package
775             unless ref( $export_tags ) && @$export_tags;
776             my @symbols = map { $_->{id} } @$messages;
777             for my $tag ( @$export_tags ) {
778             ${"$ns\::EXPORT_TAGS"}{$tag} = []
779             unless defined ${"$ns\::EXPORT_TAGS"}{$tag};
780             @{ ${"$ns\::EXPORT_TAGS"}{$tag} }
781             = distinct( @symbols, @{ ${"$ns\::EXPORT_TAGS"}{$tag} } );
782             }
783             return $package;
784             }
785              
786             # _ensure_namespace_is_exporter
787             # Returns 0 if the namespace is "main", and does nothing else.
788             # Returns 1 if the namespace is not "main", and prepends "Exporter" to the
789             # target namespace @ISA array.
790             sub _ensure_namespace_is_exporter
791             {
792             no strict 'refs';
793             my ( $invocant, $ns ) = @_;
794             return 0 if $ns eq 'main';
795             require Exporter;
796             unshift @{"$ns\::ISA"}, 'Exporter'
797             unless $ns->isa( 'Exporter' );
798             return 1;
799             }
800              
801             # _provider
802             # Sets or gets the package that provided the message.
803             sub _provider
804             {
805             my ( $message, $value ) = @_;
806             return $message->{provider}
807             unless @_ > 1;
808             $message->{provider} = $value;
809             return $message;
810             }
811              
812             # _rebless
813             # Re-blesses a message using its id as the class name, and prepends the
814             # message's old class to the new namespace's @ISA array.
815             #
816             # Optionally, the developer may pass a sequence of method-name and code-
817             # reference pairs, which this method will set up in the message's new
818             # namespace. This crude facility allows for existing methods to be
819             # overriddden on a message by message basis.
820             #
821             # Though not actually required by any of the code in this module, this
822             # method has been made available to facilitate any special treatment
823             # a developer may want for a particular message.
824             sub _rebless
825             {
826             no strict 'refs';
827             my ( $message, @pairs ) = @_;
828             my $id = $message->id;
829             my $class;
830             if ( @pairs % 2 ) {
831             $class = shift @pairs;
832             }
833             else {
834             $class = join( '::', $message->_provider, $id );
835             }
836             push @{"$class\::ISA"}, ref( $message )
837             unless $class->isa( ref( $message ) );
838             while ( @pairs ) {
839             my $method = shift @pairs;
840             my $coderef = shift @pairs;
841             next unless $method && !ref( $method );
842             next unless ref( $coderef ) && ref( $coderef ) eq 'CODE';
843             my $sym = "$id\::$method";
844             *$sym = set_subname( $sym, $coderef );
845             }
846             return bless( $message, $class );
847             }
848              
849             # readmode
850             # Set or get the message's readmode attribute. Typically, only Type R
851             # (Response) messages will set this attribute.
852             sub readmode
853             {
854             my ( $message, $value ) = @_;
855             return exists( $message->{readmode} ) ? $message->{readmode} : 0
856             unless @_ > 1;
857             $message->{readmode} = $value || 0;
858             return $message;
859             }
860              
861             # response
862             # Set or get the message's response attribute. Typically, only Type R
863             # (Response) messages will set this attribute.
864             sub response
865             {
866             my ( $message, $value ) = @_;
867             return exists( $message->{response} ) ? $message->{response} : undef
868             unless @_ > 1;
869             $message->{response} = $value;
870             return $message;
871             }
872              
873             # output
874             # Set or get the message's output attribute. Typically, only the message
875             # formatter ("_format") would set this attribute.
876             sub output
877             {
878             my ( $message, $value ) = @_;
879             return exists( $message->{output} ) ? $message->{output} : undef
880             unless @_ > 1;
881             $message->{output} = $value;
882             return $message;
883             }
884              
885             # to_string
886             # Stringify the message. Return the "output" attribute if it exists and
887             # it has been defined, otherwise return the message's formatting template.
888             # The "" (stringify) operator for the message's class has been overloaded
889             # using this method.
890             sub to_string
891             {
892             return $_[0]{output};
893             }
894              
895             # template
896             # Set or get the message's formatting template. The template is any valid
897             # string that might otherwise pass for a "sprintf" format.
898             sub template
899             {
900             my ( $message, $value ) = @_;
901             return $message->{template}
902             unless @_ > 1;
903             C_MISSING_TEMPLATE( $message->id )
904             unless $value;
905             $message->{template} = $value;
906             return $message;
907             }
908              
909             # type
910             # The message's 1-character type code (A, N, I, C, E, W, M, R, D).
911             sub type
912             {
913             my ( $message, $value ) = @_;
914             return $message->{type}
915             unless @_ > 1;
916             my $type = uc( $value );
917             if ( length( $type ) > 1 ) {
918             my $long_types = $message->_types_by_alias;
919             $type = $long_types->{$type} || 'M';
920             }
921             if ( $message->_update_level_on_type_change ) {
922             my $level = $message->_type_level( $type );
923             $level = $message->_type_level( 'M' )
924             unless defined $level;
925             $message->level( $level );
926             }
927             delete $message->{types}
928             if exists $message->{types};
929             $message->{type} = $type;
930             return $message;
931             }
932              
933             # level
934             # The message's severity level.
935             sub level
936             {
937             my ( $message, $value ) = @_;
938             return $message->{level} unless @_ > 1;
939             if ( $value =~ /\D/ ) {
940             my $type = uc( $value );
941             if ( length( $type ) > 1 ) {
942             my $long_types = $message->_types_by_alias;
943             $type = $long_types->{$type} || 'M';
944             }
945             $value = $message->_type_level( $type );
946             $value = $message->_type_level( 'M' )
947             unless defined $value;
948             }
949             $message->{level} = $value;
950             return $message;
951             }
952              
953             BEGIN { *severity = \&level }
954              
955             # _new_from_string
956             # Create one or more messages from a string. Messages are separated by
957             # newlines. Each message consists of a message identifier and a formatting
958             # template, which are themselves separated by one or more spaces or tabs.
959             sub _new_from_string
960             {
961             my ( $invocant, $string ) = @_;
962             my @lines;
963             for my $line ( grep { m{\S} && m{^[^#]} }
964             split( m{\s*\n\s*}, $string ) )
965             {
966             my ( $id, $text ) = split( m{[\s\t]+}, $line, 2 );
967             if ( @lines && $id =~ m{^[.]+$} ) {
968             $lines[-1] =~ s{\z}{ $text}s;
969             }
970             elsif ( @lines && $id =~ m{^[+]+$} ) {
971             $lines[-1] =~ s{\z}{\n$text}s;
972             }
973             else {
974             push @lines, ( $id, $text );
975             }
976             }
977             return $invocant->_new_from_arrayref( \@lines );
978             }
979              
980             # _new_from_arrayref
981             # Create one or more messages from an array. Each element of the array is
982             # an array of two elements: a message identifier and a formatting template.
983             sub _new_from_arrayref
984             {
985             my ( $invocant, $arrayref ) = @_;
986             return $invocant->_new_from_hashref( {@$arrayref} );
987             }
988              
989             # _new_from_hashref
990             # Create one or more messages from an array. Each element of the array is
991             # an array of two elements: a message identifier and a formatting template.
992             sub _new_from_hashref
993             {
994             my ( $invocant, $hashref ) = @_;
995             return map { $invocant->_new( $_, $hashref->{$_} ) } keys %$hashref;
996             }
997              
998             # _new
999             # Create a new message from message identifier and formatting template
1000             # arguments.
1001             sub _new
1002             {
1003             my ( $class, $message_id, $message_template ) = @_;
1004             $class = ref( $class ) || $class;
1005             my $message = bless( {}, $class );
1006             $message->id( $message_id );
1007             s{\\n}{\n}g,
1008             s{\\r}{\r}g,
1009             s{\\t}{\t}g,
1010             s{\\a}{\a}g,
1011             s{\\s}{ }g for $message_template;
1012             $message->template( $message_template );
1013              
1014             if ( $message->type eq 'R' && $message->template =~ m{password}si ) {
1015             $message->readmode( 'noecho' );
1016             }
1017             return $message;
1018             }
1019             # import
1020             # Import new messages into the caller's namespace.
1021             sub import
1022             {
1023             my ( $package, my @args ) = @_;
1024             if ( @args ) {
1025             my ( @tags, @messages, $export, $export_ok );
1026             my $caller = caller;
1027             while ( @args ) {
1028             my $this_arg = shift( @args );
1029             my $ref_type = reftype( $this_arg );
1030             if ( $ref_type ) {
1031             if ( $ref_type eq 'HASH' ) {
1032             push @messages, __PACKAGE__->_new_from_hashref( $this_arg );
1033             }
1034             elsif ( $ref_type eq 'ARRAY' ) {
1035             push @messages, __PACKAGE__->_new_from_arrayref( $this_arg );
1036             }
1037             else {
1038             C_EXPECT_HAREF_OR_KVPL;
1039             }
1040             $package->_export_messages(
1041             { namespace => $caller,
1042             messages => \@messages,
1043             export_tags => \@tags,
1044             export_ok => $export_ok,
1045             export => $export,
1046             }
1047             ) if @messages;
1048             @tags = ();
1049             @messages = ();
1050             undef $export;
1051             undef $export_ok;
1052             }
1053             else {
1054             if ( $this_arg eq 'EXPORT' ) {
1055             if ( @messages ) {
1056             $package->_export_messages(
1057             { namespace => $caller,
1058             messages => \@messages,
1059             export_tags => \@tags,
1060             export_ok => $export_ok,
1061             export => $export,
1062             }
1063             );
1064             @messages = ();
1065             @tags = ();
1066             }
1067             $export = 1;
1068             undef $export_ok;
1069             }
1070             elsif ( $this_arg eq 'EXPORT_OK' ) {
1071             if ( @messages ) {
1072             $package->_export_messages(
1073             { namespace => $caller,
1074             messages => \@messages,
1075             export_tags => \@tags,
1076             export_ok => $export_ok,
1077             export => $export,
1078             }
1079             );
1080             @messages = ();
1081             @tags = ();
1082             }
1083             $export_ok = 1;
1084             undef $export;
1085             }
1086             elsif ( substr( $this_arg, 0, 1 ) eq ':' ) {
1087             ( my $tag = substr( $this_arg, 1 ) ) =~ s/(?:^\s+|\s+$)//;
1088             my @new_tags = split m{\s*[,]?\s*[:]}, $tag;
1089             push @tags, @new_tags;
1090             $package->_export_messages(
1091             { namespace => $caller,
1092             messages => \@messages,
1093             export_tags => \@tags,
1094             export_ok => $export_ok,
1095             export => $export,
1096             }
1097             ) if @messages;
1098             @messages = ();
1099             $export_ok = 1;
1100             undef $export;
1101             }
1102             elsif ( $this_arg eq 'void' ) {
1103             Syntax::Feature::Void->import( 'void' );
1104             }
1105             else {
1106             if ( @args ) {
1107             push @messages, __PACKAGE__->_new( $this_arg, shift( @args ) );
1108             }
1109             else {
1110             push @messages, __PACKAGE__->_new_from_string( $this_arg );
1111             }
1112             }
1113             } ## end else [ if ( $ref_type ) ]
1114             } ## end while ( @args )
1115             if ( @messages ) {
1116             $package->_export_messages(
1117             { namespace => $caller,
1118             messages => \@messages,
1119             export_tags => \@tags,
1120             export_ok => $export_ok,
1121             export => $export,
1122             }
1123             );
1124             }
1125             } ## end if ( @args )
1126             return $package;
1127             } ## end sub import
1128              
1129             use message {
1130             C_EXPECT_HAREF_OR_KVPL =>
1131             'Expected list of name-value pairs, or reference to an ARRAY or HASH of the same',
1132             C_BAD_MESSAGE_ID => 'Message identifier "%s" is invalid',
1133             C_MISSING_TEMPLATE => 'Message with identifier "%s" has no template'
1134             };
1135              
1136             1;
1137              
1138             =pod
1139              
1140             =encoding utf8
1141              
1142             =head1 NAME
1143              
1144             Message::String - A pragma to declare and organise messaging.
1145              
1146             =head1 VERSION
1147              
1148             version 0.1.7
1149              
1150             =head1 SYNOPSIS
1151              
1152             This module helps you organise, identify, define and use messaging
1153             specific to an application or message domain.
1154              
1155             =head2 Using the pragma to define message strings
1156              
1157             =over
1158              
1159             =item The pragma's package name may be used directly:
1160              
1161             # Declare a single message
1162             use Message::String INF_GREETING => "Hello, World!";
1163            
1164             # Declare multiple messages
1165             use Message::String {
1166             INF_GREETING => "I am completely operational, " .
1167             "and all my circuits are functioning perfectly.",
1168             RSP_DO_WHAT => "What would you have me do?\n",
1169             NTC_FAULT => "I've just picked up a fault in the %s unit.",
1170             CRT_NO_CAN_DO => "I'm sorry, %s. I'm afraid I can't do that",
1171             };
1172              
1173             =item Or, after loading the module, the C alias may be used:
1174              
1175             # Load the module
1176             use Message::String;
1177              
1178             # Declare a single message
1179             use message INF_GREETING => "Hello, World!";
1180              
1181             # Declare multiple messages
1182             use message {
1183             INF_GREETING => "I am completely operational, " .
1184             "and all my circuits are functioning perfectly.",
1185             RSP_DO_WHAT => "What would you have me do?\n",
1186             NTC_FAULT => "I've just picked up a fault in the %s unit.",
1187             CRT_NO_CAN_DO => "I'm sorry, %s. I'm afraid I can't do that",
1188             };
1189              
1190             (B: the C pragma may be favoured in future examples.)
1191              
1192             =back
1193              
1194             =head2 Using message strings in your application
1195              
1196             Using message strings in your code is really easy, and you have choice about
1197             how to do so:
1198              
1199             =over
1200              
1201             =item B
1202              
1203             # Ah, the joyless tedium that is composing strings using constants...
1204             $name = "Dave";
1205             print INF_GREETING, "\n";
1206             print RSP_DO_WHAT;
1207             chomp(my $response = );
1208             if ($response =~ /Open the pod bay doors/i)
1209             {
1210             die sprintf(CRT_NO_CAN_DO, $name);
1211             }
1212             printf NTC_FAULT . "\n", 'AE-35';
1213              
1214             Using messages this way can sometimes be useful but, on this occasion, aptly
1215             demonstrates why constants get a bad rap. This pattern of usage works fine,
1216             though you could just have easily used the C pragma, or one of
1217             the alternatives.
1218              
1219             =item B
1220              
1221             $name = 'Dave';
1222             INF_GREETING; # Display greeting (stdout)
1223             RSP_DO_WHAT; # Prompt for response (stdout/stdin)
1224             if ( /Open the pod bay doors/ ) # Check response; trying $_ but
1225             { # RSP_DO_WHAT->response works, too!
1226             CRT_NO_CAN_DO($name); # Throw hissy fit (Carp::croak)
1227             }
1228             NTC_FAULT('AE-35'); # Issue innocuous notice (stderr)
1229              
1230             =back
1231              
1232             C objects take care of things like printing info messages
1233             to stdout; printing response messages to stdout, and gathering input from
1234             STDIN; putting notices on stderr, and throwing exceptions for critical
1235             errors. They do all the ancillary work so you don't have to; hiding away
1236             oft used sprinklings that make code noisy.
1237              
1238             =head2 Exporting message strings to other packages
1239              
1240             It is also possible to have a module export its messages for use by other
1241             packages. By including C or C in the argument list,
1242             before your messages are listed, you can be sure that your package will
1243             export your symbols one way or the other.
1244              
1245             The examples below show how to export using C and C; they
1246             also demonstrate how to define messages using less onerous string catalogues
1247             and, when doing so, how to split longer messages in order to keep the lengths
1248             of your lines manageable:
1249              
1250             =over
1251              
1252             =item B
1253              
1254             package My::App::Messages;
1255             use Message::String EXPORT => << 'EOF';
1256             INF_GREETING I am completely operational,
1257             ... and all my circuits are functioning perfectly.
1258             RSP_DO_WHAT What would you have me do?\n
1259             NTC_FAULT I've just picked up a fault in the %s unit.
1260             CRT_NO_CAN_DO I'm sorry, %s. I'm afraid I can't do that
1261             EOF
1262             1;
1263              
1264             # Meanwhile, back at main::
1265             use My::App::Messages; # No choice. We get everything!
1266              
1267             =item B
1268              
1269             package My::App::Messages;
1270             use Message::String EXPORT_OK => << 'EOF';
1271             INF_GREETING I am completely operational,
1272             ... and all my circuits are functioning perfectly.
1273             RSP_DO_WHAT What would you have me do?\n
1274             NTC_FAULT I've just picked up a fault in the %s unit.
1275             CRT_NO_CAN_DO I'm sorry, %s. I'm afraid I can't do that
1276             EOF
1277             1;
1278              
1279             # Meanwhile, back at main::
1280             use My::App::Messages 'INF_GREETING'; # Import what we need
1281              
1282             (B: you were probably astute enough to notice that, despite the HEREDOC
1283             marker being enclosed in single quotes, there is a C<\n> at the end of one
1284             of the message definitions. This isn't an error; the message formatter will
1285             deal with that.)
1286              
1287             It is also possible to place messages in one or more groups by including
1288             the group tags in the argument list, before the messages are defined. Group
1289             tags I start with a colon (C<:>).
1290              
1291             =item B
1292              
1293             package My::App::Messages;
1294             use My::App::Messages;
1295             use message ':MESSAGES' => {
1296             INF_GREETING => "I am completely operational, " .
1297             "and all my circuits are functioning perfectly.",
1298             RSP_DO_WHAT => "What would you have me do?\n",
1299             NTC_FAULT => "I've just picked up a fault in the %s unit.",
1300             };
1301             use message ':MESSAGES', ':ERRORS' => {
1302             CRT_NO_CAN_DO => "I'm sorry, %s. I'm afraid I can't do that",
1303             };
1304             1;
1305              
1306             # Meanwhile, back at main::
1307             use My::App::Messages ':ERRORS'; # Import the errors
1308             use My::App::Messages ':MESSAGE'; # Import everything
1309              
1310             =back
1311              
1312             Tagging messages causes your module's C<%EXPORT_TAGS> hash to be updated,
1313             with tagged messages also being added to your module's C<@EXPORT_OK> array.
1314              
1315             There is no expectation that you will make your package a descendant of the
1316             C class. Provided you aren't working in the C namespace
1317             then the calling package will be made a subclass of C automatically,
1318             as soon as it becomes clear that it is necessary.
1319              
1320             =head2 Recap of the highlights
1321              
1322             This brief introduction demonstrates, hopefully, that as well as being able
1323             to function like constants, message strings are way more sophisticated than
1324             constants.
1325              
1326             Perhaps your Little Grey Cells have also helped you make a few important
1327             deductions:
1328              
1329             =over
1330              
1331             =item * That the name not only identifies, but characterises a message.
1332              
1333             =item * That different types of message exist.
1334              
1335             =item * That handling is influenced by a message's type.
1336              
1337             =item * That messages are simple text, or they may be parameterised.
1338              
1339             =item * That calling context matters, particularly B context.
1340              
1341             =back
1342              
1343             You possibly have more questions. Certainly, there is more to the story
1344             and these are just the highlights. The module is described in greater
1345             detail below.
1346              
1347             =head1 DESCRIPTION
1348              
1349             The C pragma and its alias (C) are aimed at the
1350             programmer who wishes to organise, identify, define, use (or make available
1351             for use) message strings specific to an application or other message
1352             domain. C objects are not unlike constants, in fact, they
1353             may even be used like constants; they're just a smidge more helpful.
1354              
1355             Much of a script's lifetime is spent saying stuff, asking for stuff, maybe
1356             even complaining about stuff; but, most important of all, they have to do
1357             meaningful stuff, good stuff, the stuff they were designed to do.
1358              
1359             The trouble with saying, asking for, and complaining about stuff is the
1360             epic amount of repeated stuff that needs to be done just to do that kind
1361             of stuff. And that kind of stuff is like visual white noise when it's
1362             gets in the way of understanding and following a script's flow.
1363              
1364             We factor out repetetive code into reusable subroutines, web content into
1365             templates, but we do nothing about our script's messaging. Putting up with
1366             broken strings, quotes, spots and commas liberally peppered around the place
1367             as we compose and recompose strings doesn't seem to bother us.
1368              
1369             What if we could organise our application's messaging in a way that kept
1370             all of that noise out of the way? A way that allowed us to access messages
1371             using mnemonics but have useful, sensible and standard things happen when
1372             we do so. This module attempts to provide the tooling to do just that.
1373              
1374             =head1 METHODS
1375              
1376             C objects are created and injected into the symbol table
1377             during Perl's compilation phase so that they are accessible at runtime. Once
1378             the import method has done its job there is very little that may be done to
1379             meaningfully alter the identity, purpose or destiny of messages.
1380              
1381             A large majority of this module's methods, including constructors, are
1382             therefore notionally and conventionally protected. There are, however, a
1383             small number of public methods worth covering in this document.
1384              
1385             =head2 Public Methods
1386              
1387             =head3 import
1388              
1389             message->import();
1390             message->import( @options, @message_group, ... );
1391             message->import( @options, \%message_group, ... );
1392             message->import( @options, \@message_group, ... );
1393             message->import( @options, $message_group, ... );
1394              
1395             The C method is invoked at compile-time, whenever a C
1396             or C directive is encountered. It processes any options
1397             and creates any requested messages, injecting message symbols into
1398             the caller's symbol table.
1399              
1400             B
1401              
1402             =over
1403              
1404             =item C
1405              
1406             Makes the C operator available for use in the calling module. Since
1407             the active aspects of message handling are only triggered in void context,
1408             it provides an extra level of comfort to developers who are unsure whether
1409             a statement will be executed in the correct context.
1410              
1411             The C operator is B if testing with messages.
1412              
1413             The C operator is provided by C>.
1414              
1415             =item C
1416              
1417             Ensures that the caller's C<@EXPORT> list includes the names of messages
1418             defined in the following group.
1419              
1420             # Have the caller mandate that these messages be imported:
1421             #
1422             use message EXPORT => { ... };
1423              
1424             =item C
1425              
1426             Ensures that the caller's C<@EXPORT_OK> list includes the names of messages
1427             defined in the following group. The explicit use of C is not
1428             necessary when tag groups are being used and its use is implied.
1429              
1430             # Have the caller make these messages importable individually and
1431             # upon request:
1432             #
1433             use message EXPORT_OK => { ... };
1434              
1435             =item C<:I>
1436              
1437             One or more export tags may be listed, specifying that the following group
1438             of messages is to be added to the listed tag group(s). Any necessary updates
1439             to the caller's C<%EXPORT_TAGS> hash and C<@EXPORT_OK> array are made. The
1440             explicit use of C is unnecessary since its use is implied.
1441            
1442             Tags may be listed separately or together in the same string. Regardless of
1443             how they are presented, each tag must start with a colon (C<:>).
1444              
1445             # Grouping messages with a single tag:
1446             #
1447             use message ':FOO' => { ... };
1448              
1449             # Four valid ways to group messages with multiple tags:
1450             #
1451             use message ':FOO',':BAR' => { ... };
1452             use message ':FOO, :BAR' => { ... };
1453             use message ':FOO :BAR' => { ... };
1454             use message ':FOO:BAR' => { ... };
1455              
1456             # Gilding-the-lily; not wrong, but not necessary:
1457             #
1458             use message ':FOO', EXPORT_OK => { ... };
1459              
1460             =back
1461              
1462             Tag groups and other export options have no effect if the calling package
1463             is C.
1464              
1465             If the calling package hasn't already been declared a subclass of C
1466             then the C package is loaded and the caller's C<@ISA> array will
1467             be updated to include it as the first element.
1468              
1469             (B: I should try to make this work with C>.)
1470              
1471             B
1472              
1473             A message is comprised of two tokens:
1474              
1475             =over
1476              
1477             =item The Message Identifier
1478              
1479             The message id should contain no whitespace characters, consist only of
1480             upper- and/or lowercase letters, digits, the underscore, and be valid
1481             as a Perl subroutine name. The id should I be unique; at the
1482             very least, it B be unique to the package in which it is defined.
1483              
1484             As well as naming a message, the message id is also used to determine the
1485             message type and severity. Try to organise your message catalogues using
1486             descriptive and consistent naming and type conventions.
1487              
1488             (Read the section about L to see how typing works.)
1489              
1490             =item The Message Template
1491              
1492             The template is the text part of the message. It could be a simple string,
1493             or it could be a C format complete with one or more parameter
1494             placeholders. A message may accept arguments, in which case C will
1495             merge the argument values with the template to produce the final output.
1496              
1497             =back
1498              
1499             Messages are defined in groups of one or more key-value pairs, and the
1500             C method is quite flexible about how they are presented for
1501             processing.
1502              
1503             =over
1504              
1505             =item As a flat list of key-value pairs.
1506              
1507             use message
1508             INF_GREETING => "I am completely operational, " .
1509             "and all my circuits are functioning perfectly.",
1510             RSP_DO_WHAT => "What would you have me do?\n",
1511             NTC_FAULT => "I've just picked up a fault in the %s unit.",
1512             CRT_NO_CAN_DO => "I'm sorry, %s. I'm afraid I can't do that";
1513              
1514             =item As an anonymous hash, or hash reference.
1515              
1516             use message {
1517             INF_GREETING => "I am completely operational, " .
1518             "and all my circuits are functioning perfectly.",
1519             RSP_DO_WHAT => "What would you have me do?\n",
1520             NTC_FAULT => "I've just picked up a fault in the %s unit.",
1521             CRT_NO_CAN_DO => "I'm sorry, %s. I'm afraid I can't do that",
1522             };
1523              
1524             =item As an anonymous array, or array reference.
1525              
1526             use message [
1527             INF_GREETING => "I am completely operational, " .
1528             "and all my circuits are functioning perfectly.",
1529             RSP_DO_WHAT => "What would you have me do?\n",
1530             NTC_FAULT => "I've just picked up a fault in the %s unit.",
1531             CRT_NO_CAN_DO => "I'm sorry, %s. I'm afraid I can't do that",
1532             ];
1533              
1534             =item As a string (perhaps using a HEREDOC).
1535              
1536             use message << 'EOF';
1537             INF_GREETING I am completely operational,
1538             ... and all my circuits are functioning perfectly.
1539             RSP_DO_WHAT What would you have me do?\n
1540             NTC_FAULT I've just picked up a fault in the %s unit.
1541             CRT_NO_CAN_DO I'm sorry, %s. I'm afraid I can't do that
1542             EOF
1543              
1544             When defining messages in this way, longer templates may be broken-up (as
1545             shown on the third line of the example above) by placing one or more dots
1546             (C<.>) where a message id would normally appear. This forces the text
1547             fragment on the right to be appended to the template above, separated
1548             by a single space. Similarly, the addition symbol (C<+>) may be used
1549             in place of dot(s) if a newline is desired as the separator. This is
1550             particularly helpful when using PerlTidy and shorter line lengths.
1551              
1552             =back
1553              
1554             Multiple sets of export options and message groups may be added to the
1555             same import method's argument list:
1556              
1557             use message ':MESSAGES, :MISC' => (
1558             INF_GREETING => "I am completely operational, " .
1559             "and all my circuits are functioning perfectly.",
1560             RSP_DO_WHAT => "What would you have me do?\n",
1561             ), ':MESSAGES, :NOTICES' => (
1562             NTC_FAULT => "I've just picked up a fault in the %s unit.",
1563             ), ':MESSAGES, :ERRORS' => (
1564             CRT_NO_CAN_DO => "I'm sorry, %s. I'm afraid I can't do that",
1565             );
1566              
1567             When a message group has been processed any export related options that
1568             are currently in force will be reset; no further messages will be marked
1569             as exportable until a new set of export options and messages is added to
1570             the same directive.
1571              
1572             Pay attention when defining messages as simple lists of key-value pairs, as
1573             any new export option(s) will punctuate a list of messages up to that point
1574             and they will be processed as a complete group.
1575              
1576             The message parser will also substitute the following escape sequences
1577             with the correct character shown in parentheses:
1578              
1579             =over
1580              
1581             =item * C<\n> (newline)
1582              
1583             =item * C<\r> (linefeed)
1584              
1585             =item * C<\t> (tab)
1586              
1587             =item * C<\a> (bell)
1588              
1589             =item * C<\s> (space)
1590              
1591             =back
1592              
1593             =head3 id
1594              
1595             MESSAGE_ID->id;
1596              
1597             Gets the message's identifier.
1598              
1599             =head3 level
1600              
1601             MESSAGE_ID->level( $severity_int );
1602             MESSAGE_ID->level( $long_or_short_type_str );
1603             $severity_int = MESSAGE_ID->level;
1604              
1605             Sets or gets a message's severity level.
1606              
1607             The severity level is always returned as an integer value, while it may be
1608             set using an integer value or a type code (long or short) with the desired
1609             value.
1610              
1611             =over
1612              
1613             =item B
1614              
1615             # Give my notice a higher severity, equivalent to a warning.
1616              
1617             NTC_FAULT->level(4);
1618             NTC_FAULT->level('W');
1619             NTC_FAULT->level('WARNING');
1620              
1621             =back
1622              
1623             (See L for more informtion about typing.)
1624              
1625             =head3 output
1626            
1627             $formatted_message_str = MESSAGE_ID->output;
1628              
1629             Returns the formatted text produced last time a particular message was
1630             used, or it returnd C if the message hasn't yet been issued. The
1631             message's C value would also include the values of any parameters
1632             passed to the message.
1633              
1634             =over
1635              
1636             =item B
1637              
1638             # Package in which messages are defined.
1639             #
1640             package My::App::MsgRepo;
1641             use Message::String EXPORT_OK => {
1642             NTC_FAULT => 'I've just picked up a fault in the %s unit.',
1643             };
1644              
1645             1;
1646              
1647             # Package in which messages are required.
1648             #
1649             use My::App::MsgRepo qw/NTC_FAULT/;
1650             use Test::More;
1651              
1652             NTC_FAULT('AE-35'); # The message is issued...
1653              
1654             # Some time later...
1655             diag NTC_FAULT->output; # What was the last reported fault again?
1656              
1657             # Output:
1658             # I've just picked up a fault in the AE-35 unit.
1659              
1660             =back
1661              
1662             =head3 readmode
1663              
1664             MESSAGE_ID->readmode( $mode_str );
1665             MESSAGE_ID->readmode( $mode_int );
1666             $mode_int = MESSAGE_ID->readmode;
1667              
1668             Uses L> to set the terminal driver mode when getting the
1669             response from C. The terminal driver mode is restored to its C
1670             state after the input is complete.
1671              
1672             Ostensibly, this method is intended for use with Type R (Response) messages,
1673             specifically to switch off TTY echoing for password entry. You should,
1674             however, never need to use explicitly if the text I<"password"> is contained
1675             within the message's template, as its use is implied.
1676              
1677             =over
1678              
1679             =item B
1680              
1681             RSP_MESSAGE->readmode('noecho');
1682              
1683             =back
1684              
1685             =head3 response
1686              
1687             $response_str = MESSAGE_ID->response;
1688              
1689             Returns the input given in response to the message last time it was used, or
1690             it returns C if the message hasn't yet been isssued.
1691              
1692             The C accessor is only useful with Type R (Response) messages.
1693              
1694             =over
1695              
1696             =item B
1697              
1698             # Package in which messages are defined.
1699             #
1700             package My::App::MsgRepo;
1701             use Message::String EXPORT_OK => {
1702             INF_GREETING => 'Welcome to the machine.',
1703             RSP_USERNAME => 'Username: ',
1704             RSP_PASSWORD => 'Password: ',
1705             };
1706              
1707             # Since RSP_PASSWORD is a response and contains the word "password",
1708             # the response is not echoed to the TTY.
1709             #
1710             # RSP_PASSWORD->readmode('noecho') is implied.
1711              
1712             1;
1713              
1714             # Package in which messages are required.
1715             #
1716             use My::App::MsgRepo qw/INF_GREETING RSP_USERNAME RSP_PASSWORD/;
1717             use DBI;
1718              
1719             INF_GREETING; # Pleasantries
1720             RSP_USERNAME; # Prompt for and fetch username
1721             RSP_PASSWORD; # Prompt for and fetch password
1722              
1723             $dbh = DBI->connect( 'dbi:mysql:test;host=127.0.0.1',
1724             RSP_USERNAME->response, RSP_PASSWORD->response )
1725             or die $DBI::errstr;
1726              
1727             =back
1728              
1729             =head3 severity
1730              
1731             MESSAGE_ID->severity( $severity_int );
1732             MESSAGE_ID->severity( $long_or_short_type_str );
1733             $severity_int = MESSAGE_ID->severity;
1734              
1735             (An alias for the C method.)
1736              
1737             =head3 template
1738              
1739             MESSAGE_ID->template( $format_or_text_str );
1740             $format_or_text_str = MESSAGE_ID->template;
1741              
1742             Sets or gets the message template. The template may be a plain string of
1743             text, or it may be a C format containing parameter placeholders.
1744              
1745             =over
1746              
1747             =item B
1748              
1749             # Redefine our message templates.
1750              
1751             INF_GREETING->template('Ich bin völlig funktionsfähig, und alle meine '
1752             . 'Schaltungen sind perfekt funktioniert.');
1753             CRT_NO_CAN_DO->template('Tut mir leid, %s. Ich fürchte, ich kann das '
1754             . 'nicht tun.');
1755            
1756             # Some time later...
1757            
1758             INF_GREETING;
1759             CRT_NO_CAN_DO('Dave');
1760              
1761             =back
1762              
1763             =head3 to_string
1764              
1765             $output_or_template_str = MESSAGE_ID->to_string;
1766              
1767             Gets the string value of the message. If the message has been issued then
1768             you get the message output, complete with any message parameter values. If
1769             the message has not yet been issued then the message template is returned.
1770              
1771             Message objects overload the stringification operator ("") and it is this
1772             method that will be called whenever the string value of a message is
1773             required.
1774              
1775             =over
1776              
1777             =item B
1778              
1779             print INF_GREETING->to_string . "\n";
1780            
1781             # Or, embrace your inner lazy:
1782              
1783             print INF_GREETING . "\n";
1784              
1785             =back
1786              
1787             =head3 type
1788              
1789             MESSAGE_ID->type( $long_or_short_type_str );
1790             $short_type_str = MESSAGE_ID->type;
1791              
1792             Gets or sets a message's type characteristics, which includes its severity
1793             level.
1794              
1795             =over
1796              
1797             =item B
1798              
1799             # Check my message's type
1800              
1801             $code = NTC_FAULT->type; # Returns "N"
1802              
1803             # Have my notice behave more like a warning.
1804              
1805             NTC_FAULT->type('W');
1806             NTC_FAULT->type('WARNING');
1807              
1808             =back
1809              
1810             =head3 verbosity
1811              
1812             MESSAGE_ID->type( $severity_int );
1813             MESSAGE_ID->type( $long_or_short_type_str );
1814             $severity_int = MESSAGE_ID->verbosity;
1815              
1816             Gets or sets the level above which messages will B be issued. Messages
1817             above this level may still be generated and their values are still usable,
1818             but they are silenced.
1819              
1820             I
1821             (Error) message.>
1822              
1823             =over
1824              
1825             =item B
1826              
1827             # Only issue Alert, Critical, Error and Warning messages.
1828              
1829             message->verbosity('WARNING'); # Or ...
1830             message->verbosity('W'); # Or ...
1831             message->verbosity(4);
1832              
1833             =back
1834              
1835             =head3 overloaded ""
1836              
1837             $output_or_template_str = MESSAGE_ID;
1838              
1839             Message objects overload Perl's I operator, calling the
1840             C method.
1841              
1842             =head1 MESSAGE TYPES
1843              
1844             Messages come in nine great flavours, each identified by a single-letter
1845             type code. A message's type represents the severity of the condition that
1846             would cause the message to be issued:
1847              
1848             =head3 Type Codes
1849              
1850             Type Alt Level / Type
1851             Code Type Priority Description
1852             ---- ---- -------- ---------------------
1853             A ALT 1 Alert
1854             C CRT 2 Critical
1855             E ERR 3 Error
1856             W WRN 4 Warning
1857             N NTC 5 Notice
1858             I INF 6 Info
1859             D DEB 7 Debug (or diagnostic)
1860             R RSP 1 Response
1861             M MSG 6 General message
1862              
1863             =head2 How messages are assigned a type
1864              
1865             When a message is defined an attempt is made to discern its type by examining
1866             it for a series of clues in the message's identifier:
1867              
1868             =over
1869              
1870             =item B: check for a suffix matching C
1871              
1872             The I suffix spoils the fun by removing absolutely all of
1873             the guesswork from the process of assigning type characteristics. It is
1874             kind of ugly but removes absolutely all ambiguity. It is somewhat special
1875             in that it does not form part of the message's identifier, which is great
1876             if you have to temporarily re-type a message but don't want to hunt down
1877             and change every occurrence of its use.
1878              
1879             This suffix is a great substitute for limited imaginative faculties when
1880             naming messages.
1881              
1882             =item B: check for a suffix matching C
1883              
1884             This step, like the following three steps, uses information embedded within
1885             the identifier to determine the type of the message. Since message ids are
1886             meant to be mnemonic, at least some attempt should be made by message
1887             authors to convey purpose and meaning in their choice of id.
1888              
1889             =item B: check for a prefix matching C
1890              
1891             =item B: check for a suffix matching C)$/>,
1892             where the alternation set is comprised of long type codes (see
1893             L).
1894              
1895             =item B: check for a prefix matching C)/>,
1896             where the alternation set is comprised of long type codes (see
1897             L).
1898              
1899             =item B: as a last resort the message is characterised as Type-M
1900             (General Message).
1901              
1902             =back
1903              
1904             =head3 Long Type Codes
1905              
1906             In addition to single-letter type codes, some longer aliases may under some
1907             circumstances be used in their stead. This can and does make some statements
1908             a little less cryptic.
1909              
1910             We can use one of this package's protected methods (C<_types_by_alias>) to
1911             not only list the type code aliases but also reveal type code equivalence:
1912              
1913             use Test::More;
1914             use Data::Dumper::Concise;
1915             use Message::String;
1916            
1917             diag Dumper( { message->_types_by_alias } );
1918            
1919             # {
1920             # ALERT => "A",
1921             # ALR => "A",
1922             # ALT => "A",
1923             # CRIT => "C",
1924             # CRITICAL => "C",
1925             # CRT => "C",
1926             # DEB => "D",
1927             # DEBUG => "D",
1928             # DGN => "D",
1929             # DIAGNOSTIC => "D",
1930             # ERR => "E",
1931             # ERROR => "E",
1932             # FATAL => "C",
1933             # FTL => "C",
1934             # INF => "I",
1935             # INFO => "I",
1936             # INP => "R",
1937             # INPUT => "R",
1938             # MESSAGE => "M",
1939             # MISC => "M",
1940             # MSC => "M",
1941             # MSG => "M",
1942             # NOT => "N",
1943             # NOTICE => "N",
1944             # NTC => "N",
1945             # OTH => "M",
1946             # OTHER => "M",
1947             # OTR => "M",
1948             # PRM => "R",
1949             # PROMPT => "R",
1950             # RES => "R",
1951             # RESPONSE => "R",
1952             # RSP => "R",
1953             # WARN => "W",
1954             # WARNING => "W",
1955             # WNG => "W",
1956             # WRN => "W"
1957             # }
1958              
1959             =head2 Changing a message's type
1960              
1961             Under exceptional conditions it may be necessary to alter a message's type,
1962             and this may be achieved in one of three ways:
1963              
1964             =over
1965              
1966             =item 1. I by choosing a more suitable identifier.
1967              
1968             This is the cleanest way to make such a permanent change, and has only one
1969             disadvantage: you must hunt down code that uses the old identifier and change
1970             it. Fortunately, C is our friend and constants are easy to track down.
1971              
1972             =item 2. I by using a type-override suffix.
1973              
1974             # Change NTC_FAULT from being a notice to a response, so that it
1975             # blocks for input. We may still use the "NTC_FAULT" identifier.
1976              
1977             use message << 'EOF';
1978             NTC_FAULT:R I've just picked up a fault in the %s unit.
1979             EOF
1980              
1981             Find the original definition and append the type-override suffix, which
1982             must match regular expression C, obviously being careful
1983             to choose the correct type code. This has a cosmetic advantage in that the
1984             suffix will be effective but not be part of the the id. The disadvantage is
1985             that this can render any forgotten changes invisible, so don't forget to
1986             change it back when you're done.
1987              
1988             =item 3. I at runtime, using the message's C mutator:
1989              
1990             # I'm debugging an application and want to temporarily change
1991             # a message named APP234I to be a response so that, when it displays,
1992             # it blocks waiting for input -
1993            
1994             APP234I->type('R'); # Or, ...
1995             APP234I->type('RSP'); # Possibly much clearer, or ...
1996             APP234I->type('RESPONSE'); # Clearer still
1997            
1998             =back
1999              
2000             =head1 WHISTLES, BELLS & OTHER DOODADS
2001              
2002             =head2 Customising message output
2003              
2004             Examples shown below operate on a pragma level, which affects all messages.
2005              
2006             Any particular message may override any of these settings simply by replacing
2007             C with C>.
2008              
2009             =head3 Embedding timestamps
2010              
2011             # Get or set the default timestamp format
2012             $strftime_format_strn = message->_default_timestamp_format;
2013             message->_default_timestamp_format($strftime_format_str);
2014            
2015             # Don't embed time data in messages of specified type
2016             message->_type_timestamp($type_str, '');
2017              
2018             # Embed time data in messages of specified type, using default format
2019             message->_type_timestamp($type_str, 1);
2020            
2021             # Embed time data in messages of specified type, using specified format
2022             message->_type_timestamp($type_str, $strftime_format_str);
2023              
2024             # Don't Embed time data in ANY message types.
2025             message->_type_timestamp('');
2026              
2027             # Embed time data in ALL message types, using default format
2028             message->_type_timestamp(1);
2029            
2030             =head3 Embedding type information
2031              
2032             # Embed no additional type info in messages of a type
2033             message->_type_tlc($type_str, '');
2034              
2035             # Embed additional type info in messages of a type (3-letters max)
2036             message->_type_tlc($type_str, $three_letter_code_str);
2037              
2038             # Example
2039             message->_type_tlc('I', 'INF');
2040            
2041             =head3 Embedding the message id
2042              
2043             # Embed or don't embed message ids in a type of message
2044             message->_type_id($type_str, $bool);
2045            
2046             # Embed or don't embed message ids in all types of message
2047             message->_type_id($bool);
2048              
2049             =head1 REPOSITORY
2050              
2051             =over 2
2052              
2053             =item * L
2054              
2055             =item * L
2056              
2057             =back
2058              
2059             =head1 BUGS
2060              
2061             Please report any bugs or feature requests to C, or through
2062             the web interface at L. I will be notified, and then you'll
2063             automatically be notified of progress on your bug as I make changes.
2064              
2065             =head1 SUPPORT
2066              
2067             You can find documentation for this module with the perldoc command.
2068              
2069             perldoc Message::String
2070              
2071              
2072             You can also look for information at:
2073              
2074             =over 4
2075              
2076             =item * RT: CPAN's request tracker (report bugs here)
2077              
2078             L
2079              
2080             =item * AnnoCPAN: Annotated CPAN documentation
2081              
2082             L
2083              
2084             =item * CPAN Ratings
2085              
2086             L
2087              
2088             =item * Search CPAN
2089              
2090             L
2091              
2092             =back
2093              
2094             =head1 ACKNOWLEDGEMENTS
2095              
2096             Standing as we all do from time to time on the shoulders of giants:
2097              
2098             =over
2099              
2100             =item Dave RolskyI<, et al.>
2101              
2102             For L
2103              
2104             =item Eric Brine
2105              
2106             For L.
2107              
2108             =item Graham BarrI<, et al.>
2109              
2110             For L and L
2111              
2112             =item Jens ReshackI<, et al.>
2113              
2114             For L.
2115              
2116             =item Jonathon Stowe & Kenneth Albanowski
2117              
2118             For L.
2119              
2120             =item Ray Finch
2121              
2122             For L
2123              
2124             =item Robert SedlacekI<, et al.>
2125              
2126             For L
2127              
2128             =back
2129              
2130             =head1 AUTHOR
2131              
2132             Iain Campbell
2133              
2134             =head1 COPYRIGHT AND LICENSE
2135              
2136             This software is copyright (c) 2015 by Iain Campbell.
2137              
2138             This is free software; you can redistribute it and/or modify it under
2139             the same terms as the Perl 5 programming language system itself.
2140              
2141             =cut