File Coverage

blib/lib/Message/String.pm
Criterion Covered Total %
statement 469 471 99.5
branch 201 226 88.9
condition 62 82 75.6
subroutine 90 90 100.0
pod 9 12 75.0
total 831 881 94.3


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