File Coverage

blib/lib/Message/String.pm
Criterion Covered Total %
statement 466 467 99.7
branch 201 222 90.5
condition 62 82 75.6
subroutine 90 90 100.0
pod 9 12 75.0
total 828 873 94.8


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