File Coverage

blib/lib/Text/Colorizer.pm
Criterion Covered Total %
statement 64 136 47.0
branch 8 38 21.0
condition 12 30 40.0
subroutine 13 25 52.0
pod 10 10 100.0
total 107 239 44.7


line stmt bran cond sub pod time code
1              
2             package Text::Colorizer ;
3              
4 2     2   78469 use strict;
  2         4  
  2         113  
5 2     2   14 use warnings ;
  2         4  
  2         57  
6 2     2   11 use Carp ;
  2         11  
  2         198  
7              
8             BEGIN
9             {
10              
11 2         21 use Sub::Exporter -setup =>
12             {
13             exports => [ qw() ],
14             groups =>
15             {
16             all => [ qw() ],
17             }
18 2     2   2041 };
  2         28277  
19            
20 2     2   808 use vars qw ($VERSION);
  2         5  
  2         91  
21 2     2   38 $VERSION = '0.02';
22             }
23              
24             #-------------------------------------------------------------------------------
25              
26 2     2   1850 use English qw( -no_match_vars ) ;
  2         9051  
  2         12  
27              
28 2     2   2587 use Readonly ;
  2         6375  
  2         141  
29             Readonly my $EMPTY_STRING => q{} ;
30              
31 2     2   15 use Carp qw(carp croak confess) ;
  2         4  
  2         111  
32 2     2   8096 use Term::ANSIColor qw(colored) ;
  2         20303  
  2         10266  
33              
34             #-------------------------------------------------------------------------------
35              
36             =head1 NAME
37              
38             Text::Colorizer - Create colored text from text and color descrition. An ANSI to HTML tranformation is provided
39              
40             =head1 SYNOPSIS
41              
42             my $c= Text::Colorizer->new
43             (
44             NAME => '' ,
45             INTERACTION =>
46             {
47             INFO => sub {print @_},
48             WARN => \&Carp::carp,
49             DIE => \&Carp::confess,
50             }
51            
52             FORMAT => 'HTML' | 'ANSI' |'ASCII',
53              
54             DEFAULT_COLOR => 'bright_white on_black',
55             COLOR_NAMES =>
56             {
57             HTML =>
58             {
59             white => "color:#888;",
60             black => "color:#000;",
61             ...
62             }
63             ANSI => ...
64             ASCII => ...
65             }
66             ) ;
67              
68             # or
69            
70             my $c= Text::Colorizer->new() ;
71            
72             my $colored_text = $c->color
73             (
74             'red on_black' => 'string',
75             $color => [... many strings..],
76             'user_defined_color_name' => 'string'
77             ) ;
78              
79             =head1 DESCRIPTION
80              
81             This module defined methods to produce colored html from ANSI color description. The generated code use I
 tags.  
82             The generated HTML can be embeded in your pod documentation.
83              
84             =head1 DOCUMENTATION
85              
86             Valid colors:
87            
88             black red green yellow blue magenta cyan white
89            
90             bright_black bright_red bright_green bright_yellow
91             bright_blue bright_magenta bright_cyan bright_white
92              
93             on_black on_red on_green on yellow
94             on_blue on_magenta on_cyan on_white
95            
96             on_bright_black on_bright_red on_bright_green on_bright_yellow
97             on_bright_blue on_bright_magenta on_bright_cyan on_bright_white
98              
99             =begin html
100              
101            
 
102            
103             on_white on_black on_green on_yellow on_cyan on_red on_blue on_magenta
104             white text text text text text text text text
105             black text text text text text text text text
106             green text text text text text text text text
107             yellow text text text text text text text text
108             cyan text text text text text text text text
109             red text text text text text text text text
110             blue text text text text text text text text
111             magenta text text text text text text text text
112             bright_white text text text text text text text text
113             bright_black text text text text text text text text
114             bright_green text text text text text text text text
115             bright_yellow text text text text text text text text
116             bright_cyan text text text text text text text text
117             bright_red text text text text text text text text
118             bright_blue text text text text text text text text
119             bright_magenta text text text text text text text text
120            
121            
122             on_bright_white on_bright_black on_bright_green on_bright_yellowon_bright_cyan on_bright_red on_bright_blue on_bright_magent
123             white text text text text text text text text
124             black text text text text text text text text
125             green text text text text text text text text
126             yellow text text text text text text text text
127             cyan text text text text text text text text
128             red text text text text text text text text
129             blue text text text text text text text text
130             magenta text text text text text text text text
131             bright_white text text text text text text text text
132             bright_black text text text text text text text text
133             bright_green text text text text text text text text
134             bright_yellow text text text text text text text text
135             bright_cyan text text text text text text text text
136             bright_red text text text text text text text text
137             bright_blue text text text text text text text text
138             bright_magenta text text text text text text text text
139            
140            
141            
142              
143             =end html
144              
145             =head2 Default background color
146              
147             B
148              
149             =head1 SUBROUTINES/METHODS
150              
151             =cut
152              
153              
154             #-------------------------------------------------------------------------------
155              
156             Readonly my $NEW_ARGUMENTS => [qw(NAME INTERACTION VERBOSE JOIN JOIN_FLAT FORMAT DEFAULT_COLOR COLORS)] ;
157              
158             sub new
159             {
160              
161             =head2 new(NAMED_ARGUMENTS)
162              
163             Create a Text::Colorizer object.
164              
165             my $c= Text::Colorizer->new() ;
166              
167             I - a list of pairs - Option => Value
168              
169             =over 2
170              
171             =item * NAME - String - Name of the Data::HexDump::Range object, set to 'Anonymous' by default
172              
173             =item * INTERACTION - Hash reference - Set of subs that are used to display information to the user
174              
175             Useful if you use Data::HexDump::Range in an application without terminal.
176              
177             =item * VERBOSE - Boolean - Display information about the creation of the object. Default is I
178              
179             =item * JOIN - String - string used to join colored elements. Default is an empty string.
180              
181             =item * JOIN_FLAT - String - string used to join colored elements passed in array references. Default is an empty string.
182              
183             =item * FORMAT - String - format of the dump string generated by Data::HexDump::Range.
184              
185             Default is B which allows for colors. Other formats are 'ASCII' and 'HTML'.
186              
187             =item * DEFAULT_COLOR - the color used if no color is defined
188              
189             DEFAULT_COLOR => {ANSI => 'bright_white', HTML => 'color:#aaa; '} ;
190              
191             =item * COLOR - String 'bw' or 'cycle'.
192              
193             Ranges for which no color has been defined, in 'ANSI' or 'HTML' format mode, will be rendered in
194             black and white or with a color picked from a cyclic color list. Default is 'bw'.
195              
196             =item * COLOR_NAMES - A hash reference or a file name
197              
198             {
199             HTML =>
200             {
201             white => "color:#888;",
202             black => "color:#000;",
203             ...
204             }
205             ANSI => ...
206             ASCII => ...
207             }
208              
209             =back
210              
211             I - Text::Colorizer
212              
213             I - Dies if the color description are not valid
214              
215             =cut
216              
217 3     3 1 1739 my ($invocant, @setup_data) = @_ ;
218              
219 3   100     18 my $class = ref($invocant) || $invocant ;
220 3 100       30 confess 'Invalid constructor call!' unless defined $class ;
221              
222 2         5 my $object = {} ;
223              
224 2         10 my ($package, $file_name, $line) = caller() ;
225 2         48 bless $object, $class ;
226              
227 2         9 $object->Setup($package, $file_name, $line, @setup_data) ;
228              
229 2         7 return($object) ;
230             }
231              
232             #-------------------------------------------------------------------------------
233              
234             sub Setup
235             {
236              
237             =head2 Setup
238              
239             Helper sub called by new. This is a private sub.
240              
241             =cut
242              
243 2     2 1 6 my ($self, $package, $file_name, $line, @setup_data) = @_ ;
244              
245 2 50       9 if (@setup_data % 2)
246             {
247 0         0 croak "Invalid number of argument '$file_name, $line'!" ;
248             }
249              
250 2         3 my %valid_argument = map {$_ => 1} @{$NEW_ARGUMENTS} ;
  16         55  
  2         12  
251              
252 2   50 0   27 $self->{INTERACTION}{INFO} ||= sub {print @_} ;
  0         0  
253 2   50     15 $self->{INTERACTION}{WARN} ||= \&Carp::carp ;
254 2   50     12 $self->{INTERACTION}{DIE} ||= \&Carp::croak ;
255 2         5 $self->{NAME} = 'Anonymous';
256 2         4 $self->{FILE} = $file_name ;
257 2         4 $self->{LINE} = $line ;
258              
259 2         9 $self->CheckOptionNames(\%valid_argument, @setup_data) ;
260              
261 2         60 %{$self} =
  2         51  
262             (
263             NAME => 'Anonymous',
264             FILE => $file_name,
265             LINE => $line,
266            
267             JOIN => $EMPTY_STRING,
268             JOIN_FLAT => $EMPTY_STRING,
269            
270             FORMAT => 'ANSI',
271             DEFAULT_COLOR => {ANSI => 'bright_white', HTML => 'color:#fff; '},
272            
273             COLORS =>
274             {
275             ANSI =>
276             {
277             # you can defined aliases too
278             # alarm => 'bright_red on_bright_yellwo',
279             reset => 'reset',
280            
281             white => 'white',
282             black => 'black',
283             green => 'green',
284             yellow => 'yellow',
285             cyan => 'cyan',
286             red => 'red',
287             blue => 'blue',
288             magenta => 'magenta',
289            
290             bright_white => 'bright_white',
291             bright_black => 'bright_black',
292             bright_green => 'bright_green',
293             bright_yellow => 'bright_yellow',
294             bright_cyan => 'bright_cyan',
295             bright_red => 'bright_red',
296             bright_blue => 'bright_blue',
297             bright_magenta => 'bright_magenta',
298            
299             on_white => 'on_white',
300             on_black => 'on_black',
301             on_green => 'on_green',
302             on_yellow => 'on_yellow',
303             on_cyan => 'on_cyan',
304             on_red => 'on_red',
305             on_blue => 'on_blue',
306             on_magenta => 'on_magenta',
307            
308             on_bright_white => 'on_bright_white',
309             on_bright_black => 'on_bright_black',
310             on_bright_green => 'on_bright_green',
311             on_bright_yellow => 'on_bright_yellow',
312             on_bright_cyan => 'on_bright_cyan',
313             on_bright_red => 'on_bright_red',
314             on_bright_blue => 'on_bright_blue',
315             on_bright_magenta => 'on_bright_magenta',
316             },
317            
318             HTML =>
319             {
320             # any attribute you can put in a span
321             reset => $EMPTY_STRING,
322            
323             white => 'color:#aaa; ',
324             black => 'color:#000; ',
325             green => 'color:#0a0; ',
326             yellow => 'color:#aa0; ',
327             cyan => 'color:#0aa; ',
328             red => 'color:#a00; ',
329             blue => 'color:#00a; ',
330             magenta => 'color:#a0a; ',
331            
332             bright_white => 'color:#fff; ',
333             bright_black => 'color:#000; ',
334             bright_green => 'color:#0f0; ',
335             bright_yellow => 'color:#ff0; ',
336             bright_cyan => 'color:#0ff; ',
337             bright_red => 'color:#f00; ',
338             bright_blue => 'color:#00f; ',
339             bright_magenta => 'color:#f0f; ',
340              
341             on_white => 'background-color:#aaa; ',
342             on_black => 'background-color:#000; ',
343             on_green => 'background-color:#0a0; ',
344             on_yellow => 'background-color:#aa0; ',
345             on_cyan => 'background-color:#0aa; ',
346             on_red => 'background-color:#a00; ',
347             on_blue => 'background-color:#00a; ',
348             on_magenta => 'background-color:#a0a; ',
349            
350             on_bright_white => 'background-color:#fff; ',
351             on_bright_black => 'background-color:#000; ',
352             on_bright_green => 'background-color:#0f0; ',
353             on_bright_yellow => 'background-color:#ff0; ',
354             on_bright_cyan => 'background-color:#0ff; ',
355             on_bright_red => 'background-color:#f00; ',
356             on_bright_blue => 'background-color:#00f; ',
357             on_bright_magenta => 'background-color:#f0f; ',
358             },
359             },
360              
361             @setup_data,
362             ) ;
363              
364 2         39 my $location = "$self->{FILE}:$self->{LINE}" ;
365              
366 2   50 0   17 $self->{INTERACTION}{INFO} ||= sub {print @_} ;
  0         0  
367 2   50     12 $self->{INTERACTION}{WARN} ||= \&Carp::carp ;
368 2   50     11 $self->{INTERACTION}{DIE} ||= \&Carp::confess ;
369              
370 2 50       7 if($self->{VERBOSE})
371             {
372 0         0 $self->{INTERACTION}{INFO}('Creating ' . ref($self) . " '$self->{NAME}' at $location.\n") ;
373             }
374              
375 2 50       9 if('HASH' ne ref $self->{COLORS})
376             {
377 0 0       0 my $colors = do $self->{COLORS}
378             or $self->{INTERACTION}{DIE}("Can't load color file '$self->{COLORS}'.\n") ;
379            
380 0 0       0 'HASH' eq ref $colors
381             or $self->{INTERACTION}{DIE}("Invalid color file '$self->{COLORS}'.\n") ;
382            
383 0         0 $self->{COLORS} = $colors ;
384             }
385              
386 2         6 return ;
387             }
388              
389             #-------------------------------------------------------------------------------
390              
391             sub CheckOptionNames
392             {
393              
394             =head2 CheckOptionNames
395              
396             Verifies the named options passed to the members of this class. Calls B<{INTERACTION}{DIE}> in case
397             of error. This shall not be used directly.
398              
399             =cut
400              
401 2     2 1 4 my ($self, $valid_options, @options) = @_ ;
402              
403 2 50       7 if (@options % 2)
404             {
405 0         0 $self->{INTERACTION}{DIE}->('Invalid number of argument!') ;
406             }
407              
408 2 50       9 if('HASH' eq ref $valid_options)
    0          
409             {
410             # OK
411             }
412             elsif('ARRAY' eq ref $valid_options)
413             {
414 0         0 $valid_options = map{$_ => 1} @{$valid_options} ;
  0         0  
  0         0  
415             }
416             else
417             {
418 0         0 $self->{INTERACTION}{DIE}->("Invalid argument '$valid_options'!") ;
419             }
420              
421 2         3 my %options = @options ;
422              
423 2         8 for my $option_name (keys %options)
424             {
425 0 0       0 unless(exists $valid_options->{$option_name})
426             {
427 0         0 $self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid Option '$option_name' at '$self->{FILE}:$self->{LINE}'!") ;
428             }
429             }
430              
431 2 50 33     40 if
      33        
      33        
432             (
433             (defined $options{FILE} && ! defined $options{LINE})
434             || (!defined $options{FILE} && defined $options{LINE})
435             )
436             {
437 0         0 $self->{INTERACTION}{DIE}->("$self->{NAME}: Incomplete option FILE::LINE!") ;
438             }
439              
440 2         6 return(1) ;
441             }
442              
443             #-------------------------------------------------------------------------------
444              
445             sub get_colors
446             {
447              
448             =head2 get_colors( )
449              
450             Returns the colors defined in the object
451              
452             my $colors = $c->get_colors( ) ;
453              
454             I - None
455              
456             I - A hash reference
457              
458             I - None
459              
460             =cut
461              
462 0     0 1   my ($self) = @_ ;
463              
464 0           return $self->{COLORS} ;
465             }
466              
467             #-------------------------------------------------------------------------------
468              
469             sub set_colors
470             {
471              
472             =head2 set_colors(\%colors)
473              
474             Copies
475              
476             my %colors =
477             (
478             HTML =>
479             {
480             white => "style='color:#888;'",
481             black => "style='color:#000;'",
482             ...
483             bright_white => "style='color:#fff;'",
484             bright_black => "style='color:#000;'",
485             bright_green => "style='color:#0f0;'",
486             ...
487             }
488             ) ;
489            
490             $c->set_color(\%colors) ;
491              
492             I
493              
494             =over 2
495              
496             =item * \%colors - A hash reference
497              
498             =back
499              
500             I - Nothing
501              
502             I - dies if the color definitions are invalid
503              
504             =cut
505              
506 0     0 1   my ($self, $colors) = @_ ;
507              
508 0           $self->{COLORS} = $colors ;
509              
510 0           return ;
511             }
512              
513             #-------------------------------------------------------------------------------
514              
515             sub flatten ## no critic (Subroutines::RequireArgUnpacking)
516             {
517            
518             =head2 [P] flatten($scalar || \@array)
519              
520             Transforms array references to a flat list
521              
522             I -
523              
524             =over 2
525              
526             =item * $scalar -
527              
528             =back
529              
530             I - a lsit of scalars
531              
532             =cut
533              
534             return
535 0           map
536             {
537 0     0 1   my $description = $_ ;
538            
539 0 0         if(ref($description) eq 'ARRAY')
540             {
541 0           flatten(@{$description}) ;
  0            
542             }
543             else
544             {
545 0           $description
546             }
547             } @_ ;
548             }
549              
550             #-------------------------------------------------------------------------------
551              
552             sub color
553             {
554              
555             =head2 color($color_name, $text, $color_name, \@many_text_strings, ...) ;
556              
557             Returns colored text. according to the object setting. Default is HTML color coded.
558              
559             my $colored_text = $c->color
560             (
561             'red on_black' => 'string',
562             $color => [... many strings..]
563             'user_defined_color_name' => 'string'
564             ) ;
565            
566             I - A list of colors and text pairs
567              
568             =over 2
569              
570             =item * $color -
571              
572             =item * $text -
573              
574             =back
575              
576             I - A single string
577              
578             I - Dies if the color is invalid
579              
580             =cut
581              
582 0     0 1   my ($self, @colors_and_text) = @_ ;
583              
584 0           my ($header, $footer, $colorizer) = ($EMPTY_STRING, $EMPTY_STRING) ;
585              
586 0           my %ascii_to_html =
587             (
588             q{<} => '<',
589             q{>} => '>',
590             q{&} => '&',
591             q{'} => ''',
592             q{"} => '"',
593             ) ;
594              
595 0           for ($self->{FORMAT})
596             {
597             /ASCII/xsm and do
598 0 0         {
599             $colorizer =
600             sub
601             {
602             #~ my ($text, $color) = @_ ;
603             #~ return $text ;
604            
605 0     0     $_[0] ;
606 0           } ;
607            
608 0           last ;
609             } ;
610            
611             /ANSI/xsm and do
612 0 0         {
613 0 0 0 0     $colorizer = sub { my ($text, $color) = @_ ; (defined $color && $color ne $EMPTY_STRING) ? colored($text, $color) : $text ; } ;
  0            
  0            
614            
615 0           last ;
616             } ;
617            
618             /HTML/xsm and do
619 0 0         {
620 0           $header = qq~
\n~ ; 
621             $colorizer =
622             sub
623             {
624 0     0     my ($text, $color) = @_ ;
625            
626 0           $text =~ s/(<|>|&|\'|\")/$ascii_to_html{$1}/xsmeg ;
  0            
627            
628 0           "" . $text . '' ;
629 0           } ;
630            
631 0           $footer .= "\n\n" ;
632            
633 0           last ;
634             } ;
635            
636 0           $self->{INTERACTION}{DIE}("Error: Invalid format '$self->{FORMAT}'.\n");
637             }
638              
639 0 0         $self->{INTERACTION}{DIE}("Error: number of elements in argument list'.\n") if @colors_and_text % 2 ;
640              
641 0           my @formated ;
642              
643 0           while(@colors_and_text)
644             {
645 0           my ($color_tag, $text) = (shift @colors_and_text, shift @colors_and_text) ;
646 0           my $colors = $self->{DEFAULT_COLOR}{$self->{FORMAT}} ;
647            
648 0 0 0       if(defined $color_tag && $self->{FORMAT} ne 'ASCII')
649             {
650 0           for my $color_tag_component (split /\s+/xsm, $color_tag)
651             {
652 0           $color_tag_component =~ s/\s+//gxsm ;
653            
654 0           my $color = $self->{COLORS}{$self->{FORMAT}}{$color_tag_component} ;
655            
656 0 0         $self->{INTERACTION}{DIE}("Error: Invalid color componenent '$self->{FORMAT}::$color_tag_component'.\n") unless defined $color ;
657            
658 0           $colors .= q{ } . $color ;
659             }
660             }
661            
662 0           push @formated, join $self->{JOIN_FLAT}, map {$colorizer->($_, $colors)} flatten($text) ;
  0            
663             }
664              
665 0           return $header . join($self->{JOIN}, @formated) . $footer ;
666             }
667              
668             #-------------------------------------------------------------------------------
669              
670             sub color_all ## no critic (Subroutines::RequireArgUnpacking)
671             {
672              
673             =head2 color_all($color, $string, \@many_text_strings, ...)
674              
675             Uses a single color to colorize all the strings
676              
677             my $colored_text = $c->color_all($color, $string, \@many_text_strings, ...) ;
678              
679             I
680              
681             =over 2
682              
683             =item * $xxx -
684              
685             =back
686              
687             I - Nothing
688              
689             I
690              
691             =cut
692              
693 0     0 1   my ($self, $color) = (shift @_, shift @_) ;
694              
695 0           return $self->color(map{$color, $_} @_) ;
  0            
696             }
697              
698             #-------------------------------------------------------------------------------
699              
700             sub color_with ## no critic (Subroutines::RequireArgUnpacking)
701             {
702              
703             =head2 color_with(\%color_definitions, 'color' => 'text', $color => \@many_text_strings, ...) ;
704              
705             Colors a text, temporarely overridding the colors defined in the object.
706              
707             my %colors =
708             {
709             HTML =>
710             {
711             white => "style='color:#888;'",
712             black => "style='color:#000;'",
713             ...
714             bright_white => "style='color:#fff;'",
715             bright_black => "style='color:#000;'",
716             bright_green => "style='color:#0f0;'",
717             ...
718             }
719             },
720            
721             my $colored_text = $c->color
722             (
723             'red on_black' => 'string',
724             'blue on_yellow' => [... many strings..]
725             'user_defined_color_name' => 'string'
726             ) ;
727              
728             I
729              
730             =over 2
731              
732             =item * $ -
733              
734             =item * $color -
735              
736             =item * $xxx -
737              
738             =back
739              
740             I - Nothing
741              
742             I - Dies if any argument is invalid
743              
744             =cut
745              
746 0     0 1   my ($self, $colors) = (shift @_, shift @_) ;
747              
748 0           local $self->{COLORS} = $colors ; ## no critic (Variables::ProhibitLocalVars)
749 0           return $self->color(@_) ;
750             }
751              
752             #-------------------------------------------------------------------------------
753              
754             sub color_all_with ## no critic (Subroutines::RequireArgUnpacking)
755             {
756              
757             =head2 color_all_with($temporary_colors, $color, $text | \@many_text_string, ...) ;
758              
759             Uses a single color to colorize all the strings, using a temporary color definition
760              
761             my $temporary_colors =
762             {
763             HTML =>
764             {
765             white => "style='color:#888;'",
766             black => "style='color:#000;'",
767             ...
768             bright_white => "style='color:#fff;'",
769             bright_black => "style='color:#000;'",
770             bright_green => "style='color:#0f0;'",
771             ...
772             }
773             },
774            
775             my $colored_text = $c->color_all_with($temporary_colors, $color, 'string', [... many strings..], ...) ;
776              
777             I
778              
779             =over 2
780              
781             =item * $xxx -
782              
783             =back
784              
785             I - A colorized string
786              
787             I Dies if invalid input is received
788              
789             =cut
790              
791 0     0 1   my ($self, $colors) = (shift @_, shift @_) ;
792              
793 0           local $self->{COLORS} = $colors ; ## no critic (Variables::ProhibitLocalVars)
794 0           return $self->color_all(@_) ;
795             }
796              
797             #-------------------------------------------------------------------------------
798              
799             1 ;
800              
801             =head1 BUGS AND LIMITATIONS
802              
803             None so far.
804              
805             =head1 AUTHOR
806              
807             Nadim ibn hamouda el Khemir
808             CPAN ID: NKH
809             mailto: nadim@cpan.org
810              
811             =head1 COPYRIGHT AND LICENSE
812              
813             Copyright 2010 Nadim Khemir.
814              
815             This program is free software; you can redistribute it and/or
816             modify it under the terms of either:
817              
818             =over 4
819              
820             =item * the GNU General Public License as published by the Free
821             Software Foundation; either version 1, or (at your option) any
822             later version, or
823              
824             =item * the Artistic License version 2.0.
825              
826             =back
827              
828             =head1 SUPPORT
829              
830             You can find documentation for this module with the perldoc command.
831              
832             perldoc Text::Colorizer
833              
834             You can also look for information at:
835              
836             =over 4
837              
838             =item * AnnoCPAN: Annotated CPAN documentation
839              
840             L
841              
842             =item * RT: CPAN's request tracker
843              
844             Please report any bugs or feature requests to L .
845              
846             We will be notified, and then you'll automatically be notified of progress on
847             your bug as we make changes.
848              
849             =item * Search CPAN
850              
851             L
852              
853             =back
854              
855             =head1 SEE ALSO
856              
857             L
858              
859             =cut