File Coverage

lib/Command/View/DocMethods.pm
Criterion Covered Total %
statement 132 308 42.8
branch 25 130 19.2
condition 15 78 19.2
subroutine 22 39 56.4
pod 0 18 0.0
total 194 573 33.8


line stmt bran cond sub pod time code
1             package Command::V2; # additional methods to produce documentation, TODO: turn into a real view
2 9     9   189 use strict;
  9         13  
  9         235  
3 9     9   32 use warnings;
  9         12  
  9         219  
4              
5 9     9   2584 use Term::ANSIColor qw();
  9         21649  
  9         182  
6 9     9   3993 use Pod::Simple::Text;
  9         221670  
  9         95  
7             require Text::Wrap;
8              
9             # This is changed with "local" where used in some places
10             $Text::Wrap::columns = 100;
11              
12             # Required for color output
13             eval {
14             binmode STDOUT, ":utf8";
15             binmode STDERR, ":utf8";
16             };
17              
18             sub help_brief {
19 2     2 0 4 my $self = shift;
20 2 50       5 if (my $doc = $self->__meta__->doc) {
21 2         8 return $doc;
22             }
23             else {
24 0         0 my @parents = $self->__meta__->ancestry_class_metas;
25 0         0 for my $parent (@parents) {
26 0 0       0 if (my $doc = $parent->doc) {
27 0         0 return $doc;
28             }
29             }
30 0         0 return "no description!!!: define 'doc' in the class definition for "
31             . $self->class;
32             }
33             }
34              
35             sub help_synopsis {
36 1     1 0 1 my $self = shift;
37 1         3 return '';
38             }
39              
40             sub help_detail {
41 1     1 0 19 my $self = shift;
42 1   33     7 return "!!! define help_detail() in module " . ref($self) || $self . "!";
43             }
44              
45             sub sub_command_category {
46 2     2 0 12 return;
47             }
48              
49             sub sub_command_sort_position {
50             # override to do something besides alpha sorting by name
51 2     2 0 10 return '9999999999 ' . $_[0]->command_name_brief;
52             }
53              
54             # LEGACY: poorly named
55             sub help_usage_command_pod {
56 0     0 0 0 return shift->doc_manual(@_);
57             }
58              
59             # LEGACY: poorly named
60             sub help_usage_complete_text {
61 2     2 0 24 shift->doc_help(@_)
62             }
63              
64             sub doc_help {
65 1     1 0 2 my $self = shift;
66              
67 1         7 my $command_name = $self->command_name;
68 1         1 my $text;
69              
70 1         3 my $extra_help = '';
71 1         5 my @extra_help = $self->_additional_help_sections;
72 1         3 while (@extra_help) {
73 0   0     0 my $title = shift @extra_help || '';
74 0   0     0 my $content = shift @extra_help || '';
75 0         0 $extra_help .= sprintf(
76             "%s\n\n%s\n",
77             Term::ANSIColor::colored($title, 'underline'),
78             _pod2txt($content)
79             ),
80             }
81              
82             # standard: update this to do the old --help format
83 1         7 my $synopsis = $self->help_synopsis;
84 1         5 my $required_inputs = $self->help_options(is_optional => 0, is_input => 1);
85 1         3 my $required_outputs = $self->help_options(is_optional => 0, is_output => 1);
86 1         4 my $required_params = $self->help_options(is_optional => 0, is_param => 1);
87 1         4 my $optional_inputs = $self->help_options(is_optional => 1, is_input => 1);
88 1         3 my $optional_outputs = $self->help_options(is_optional => 1, is_output => 1);
89 1         4 my $optional_params = $self->help_options(is_optional => 1, is_param => 1);
90 1         2 my @parts;
91            
92 1         4 push @parts, Term::ANSIColor::colored('USAGE', 'underline');
93 1   50     23 push @parts,
94             Text::Wrap::wrap(
95             ' ',
96             ' ',
97             Term::ANSIColor::colored($self->command_name, 'bold'),
98             $self->_shell_args_usage_string || '',
99             );
100              
101 1 50       141 push @parts,
102             ( $synopsis
103             ? sprintf("%s\n%s\n", Term::ANSIColor::colored("SYNOPSIS", 'underline'), $synopsis)
104             : ''
105             );
106 1 50       5 push @parts,
107             ( $required_inputs
108             ? sprintf("%s\n%s\n", Term::ANSIColor::colored("REQUIRED INPUTS", 'underline'), $required_inputs)
109             : ''
110             );
111 1 50       6 push @parts,
112             ( $required_params
113             ? sprintf("%s\n%s\n", Term::ANSIColor::colored("REQUIRED PARAMS", 'underline'), $required_params)
114             : ''
115             );
116 1 50       23 push @parts,
117             ( $optional_inputs
118             ? sprintf("%s\n%s\n", Term::ANSIColor::colored("OPTIONAL INPUTS", 'underline'), $optional_inputs)
119             : ''
120             );
121 1 50       3 push @parts,
122             ( $optional_params
123             ? sprintf("%s\n%s\n", Term::ANSIColor::colored("OPTIONAL PARAMS", 'underline'), $optional_params)
124             : ''
125             );
126 1 50       3 push @parts,
127             ( $required_outputs
128             ? sprintf("%s\n%s\n", Term::ANSIColor::colored("REQUIRED OUTPUTS", 'underline'), $required_outputs)
129             : ''
130             );
131 1 50       3 push @parts,
132             ( $optional_outputs
133             ? sprintf("%s\n%s\n", Term::ANSIColor::colored("OPTIONAL OUTPUTS", 'underline'), $optional_outputs)
134             : ''
135             );
136 1   50     3 push @parts,
137             sprintf(
138             "%s\n%s\n",
139             Term::ANSIColor::colored("DESCRIPTION", 'underline'),
140             _pod2txt($self->help_detail || '')
141             );
142 1 50       41 push @parts,
143             ( $extra_help ? $extra_help : '' );
144              
145 1         4 $text = sprintf(
146             "\n%s\n%s\n\n%s%s%s%s%s%s%s%s%s\n",
147             @parts
148             );
149              
150 1         5 return $text;
151             }
152              
153             sub parent_command_class {
154 0     0 0 0 my $class = shift;
155 0 0       0 $class = ref($class) if ref($class);
156 0         0 my @components = split("::", $class);
157 0 0       0 return if @components == 1;
158 0         0 my $parent = join("::", @components[0..$#components-1]);
159 0 0       0 return $parent if $parent->can("command_name");
160 0         0 return;
161             }
162              
163             sub doc_sections {
164 0     0 0 0 my $self = shift;
165 0         0 my @sections;
166              
167 0         0 my $command_name = $self->command_name;
168              
169 9     9   6048 my $version = do { no strict; ${ $self->class . '::VERSION' } };
  9         14  
  9         6115  
  0         0  
  0         0  
  0         0  
170 0         0 my $help_brief = $self->help_brief;
171 0         0 my $datetime = $self->__context__->now;
172 0         0 my ($date,$time) = split(' ',$datetime);
173              
174 0 0       0 push(@sections, UR::Doc::Section->create(
175             title => "NAME",
176             content => "$command_name" . ($help_brief ? " - $help_brief" : ""),
177             format => "pod",
178             ));
179              
180 0 0       0 push(@sections, UR::Doc::Section->create(
181             title => "VERSION",
182             content => "This document " # separated to trick the version updater
183             . "describes $command_name "
184             . ($version ? "version $version " : "")
185             . "($date at $time)",
186             format => "pod",
187             ));
188              
189 0         0 my $synopsis = $self->command_name . ' ' . $self->_shell_args_usage_string . "\n\n" . $self->help_synopsis;
190 0 0       0 if ($synopsis) {
191 0         0 push(@sections, UR::Doc::Section->create(
192             title => "SYNOPSIS",
193             content => $synopsis,
194             format => 'pod'
195             ));
196             }
197              
198 0         0 my $required_args = $self->help_options(is_optional => 0, format => "pod");
199 0 0       0 if ($required_args) {
200 0         0 push(@sections, UR::Doc::Section->create(
201             title => "REQUIRED ARGUMENTS",
202             content => "=over\n\n$required_args\n\n=back\n\n",
203             format => 'pod'
204             ));
205             }
206              
207 0         0 my $optional_args = $self->help_options(is_optional => 1, format => "pod");
208 0 0       0 if ($optional_args) {
209 0         0 push(@sections, UR::Doc::Section->create(
210             title => "OPTIONAL ARGUMENTS",
211             content => "=over\n\n$optional_args\n\n=back\n\n",
212             format => 'pod'
213             ));
214             }
215              
216 0   0     0 my $manual = $self->_doc_manual_body || $self->help_detail;
217 0         0 push(@sections, UR::Doc::Section->create(
218             title => "DESCRIPTION",
219             content => $manual,
220             format => 'pod',
221             ));
222              
223 0         0 my @extra_help = $self->_additional_help_sections;
224 0         0 while (@extra_help) {
225 0   0     0 my $title = shift @extra_help || '';
226 0   0     0 my $content = shift @extra_help || '';
227 0         0 push (@sections, UR::Doc::Section->create(
228             title => $title,
229             content => $content,
230             format => 'pod'
231             ));
232             }
233              
234 0 0       0 if ($self->can("doc_sub_commands")) {
235 0         0 my $sub_commands = $self->doc_sub_commands(brief => 1);
236 0 0       0 if ($sub_commands) {
237 0         0 push(@sections, UR::Doc::Section->create(
238             title => "SUB-COMMANDS",
239             content => $sub_commands,
240             format => "pod",
241             ));
242             }
243             }
244              
245 0         0 my @footer_section_methods = (
246             'LICENSE' => '_doc_license',
247             'AUTHORS' => '_doc_authors',
248             'CREDITS' => '_doc_credits',
249             'BUGS' => '_doc_bugs',
250             'SEE ALSO' => '_doc_see_also'
251             );
252            
253 0         0 while (@footer_section_methods) {
254 0         0 my $header = shift @footer_section_methods;
255 0         0 my $method = shift @footer_section_methods;
256 0         0 my @txt = $self->$method;
257 0 0 0     0 next if (@txt == 0 or (@txt == 1 and not $txt[0]));
      0        
258 0         0 my $content;
259 0 0       0 if (@txt == 1) {
260 0         0 $content = $txt[0];
261             } else {
262 0         0 $content = join("\n", @txt);
263             }
264              
265 0         0 push(@sections, UR::Doc::Section->create(
266             title => $header,
267             content => $content,
268             format => "pod",
269             ));
270             }
271              
272 0         0 return @sections;
273             }
274              
275             sub doc_sub_commands {
276 0     0 0 0 my $self = shift;
277 0         0 return;
278             }
279              
280             sub doc_manual {
281 0     0 0 0 my $self = shift;
282 0         0 my $pod = $self->_doc_name_version;
283              
284 0         0 my $synopsis = $self->command_name . ' ' . $self->_shell_args_usage_string . "\n\n" . $self->help_synopsis;
285 0         0 my $required_args = $self->help_options(is_optional => 0, format => "pod");
286 0         0 my $optional_args = $self->help_options(is_optional => 1, format => "pod");
287 0 0       0 $pod .=
    0          
    0          
288             (
289             $synopsis
290             ? "=head1 SYNOPSIS\n\n" . $synopsis . "\n\n"
291             : ''
292             )
293             . (
294             $required_args
295             ? "=head1 REQUIRED ARGUMENTS\n\n=over\n\n" . $required_args . "\n\n=back\n\n"
296             : ''
297             )
298             . (
299             $optional_args
300             ? "=head1 OPTIONAL ARGUMENTS\n\n=over\n\n" . $optional_args . "\n\n=back\n\n"
301             : ''
302             );
303              
304 0         0 my $manual = $self->_doc_manual_body;
305 0         0 my $help = $self->help_detail;
306 0 0 0     0 if ($manual or $help) {
307 0         0 $pod .= "=head1 DESCRIPTION:\n\n";
308              
309 0   0     0 my $txt = $manual || $help;
310 0 0       0 if ($txt =~ /^\=/) {
311             # pure POD
312 0         0 $pod .= $manual;
313             }
314             else {
315 0         0 $txt =~ s/\n/\n\n/g;
316 0         0 $pod .= $txt;
317             #$pod .= join('', map { " $_\n" } split ("\n",$txt)) . "\n";
318             }
319             }
320              
321 0         0 $pod .= $self->_doc_footer();
322 0         0 $pod .= "\n\n=cut\n\n";
323 0         0 return "\n$pod";
324             }
325              
326              
327             sub _doc_name_version {
328 0     0   0 my $self = shift;
329              
330 0         0 my $command_name = $self->command_name;
331 0         0 my $pod;
332              
333             # standard: update this to do the old --help format
334 0         0 my $synopsis = $self->command_name . ' ' . $self->_shell_args_usage_string . "\n\n" . $self->help_synopsis;
335 0         0 my $help_brief = $self->help_brief;
336 9     9   47 my $version = do { no strict; ${ $self->class . '::VERSION' } };
  9         14  
  9         9304  
  0         0  
  0         0  
  0         0  
337 0         0 my $datetime = $self->__context__->now;
338 0         0 my ($date,$time) = split(' ',$datetime);
339              
340 0 0       0 $pod =
341             "\n=pod"
342             . "\n\n=head1 NAME"
343             . "\n\n"
344             . $self->command_name
345             . ($help_brief ? " - " . $self->help_brief : '')
346             . "\n\n";
347              
348 0         0 $pod .=
349             "\n\n=head1 VERSION"
350             . "\n\n"
351             . "This document " # separated to trick the version updater
352             . "describes " . $self->command_name;
353              
354 0 0       0 if ($version) {
355 0         0 $pod .= " version " . $version . " ($date at $time).\n\n";
356             }
357             else {
358 0         0 $pod .= " ($date at $time)\n\n";
359             }
360              
361 0         0 return $pod;
362             }
363              
364             sub _doc_manual_body {
365 0     0   0 return '';
366             }
367              
368             sub help_header {
369 0     0 0 0 my $class = shift;
370 0         0 return sprintf("%s - %-80s\n",
371             $class->command_name
372             ,$class->help_brief
373             )
374             }
375              
376             sub help_options {
377 6     6 0 5 my $self = shift;
378 6         11 my %params = @_;
379              
380 6         7 my $format = delete $params{format};
381 6         24 my @property_meta = $self->_shell_args_property_meta(%params);
382              
383 6         7 my @data;
384 6         6 my $max_name_length = 0;
385 6         8 for my $property_meta (@property_meta) {
386 1         11 my $param_name = $self->_shell_arg_name_from_property_meta($property_meta);
387 1 50       3 if ($property_meta->{shell_args_position}) {
388 0         0 $param_name = uc($param_name);
389             }
390              
391             #$param_name = "--$param_name";
392 1         4 my $doc = $property_meta->doc;
393 1         3 my $valid_values = $property_meta->valid_values;
394 1         3 my $example_values = $property_meta->example_values;
395 1 50       3 unless ($doc) {
396             # Maybe a parent class has documentation for this property
397 0         0 eval {
398 0         0 foreach my $ancestor_class_meta ( $property_meta->class_meta->ancestry_class_metas ) {
399 0         0 my $ancestor_property_meta = $ancestor_class_meta->property_meta_for_name($property_meta->property_name);
400 0 0 0     0 if ($ancestor_property_meta and $doc = $ancestor_property_meta->doc) {
401 0         0 last;
402             }
403             }
404             };
405             }
406              
407 1 50       3 if (!$doc) {
408 0 0       0 if (!$valid_values) {
409 0         0 $doc = "(undocumented)";
410             }
411             else {
412 0         0 $doc = '';
413             }
414             }
415 1 50       4 if ($valid_values) {
416 0         0 $doc .= "\nvalid values:\n";
417 0         0 for my $v (@$valid_values) {
418 0         0 $doc .= " " . $v . "\n";
419 0 0       0 $max_name_length = length($v)+2 if $max_name_length < length($v)+2;
420             }
421 0         0 chomp $doc;
422             }
423 1 50 33     10 if ($example_values && @$example_values) {
424 1   50     6 $doc .= "\nexample" . (@$example_values > 1 and 's') . ":\n";
425             $doc .= join(', ',
426 1 50       2 map { ref($_) ? Data::Dumper->new([$_])->Terse(1)->Dump() : $_ } @$example_values
  1         4  
427             );
428 1         3 chomp($doc);
429             }
430 1 50       3 $max_name_length = length($param_name) if $max_name_length < length($param_name);
431              
432 1   50     4 my $param_type = $property_meta->data_type || '';
433 1 50 33     8 if (defined($param_type) and $param_type !~ m/::/) {
434 1         3 $param_type = ucfirst(lc($param_type));
435             }
436              
437 1         1 my $default_value;
438 1 50 33     5 if (defined($default_value = $property_meta->default_value)
439             || defined(my $calculated_default = $property_meta->calculated_default)
440             ) {
441 0 0       0 unless (defined $default_value) {
442 0         0 $default_value = $calculated_default->()
443             }
444              
445 0 0 0     0 if ($param_type eq 'Boolean') {
    0          
446 0 0       0 $default_value = $default_value ? "'true'" : "'false' (--no$param_name)";
447             } elsif ($property_meta->is_many && ref($default_value) eq 'ARRAY') {
448 0 0       0 if (@$default_value) {
449 0         0 $default_value = "('" . join("','",@$default_value) . "')";
450             } else {
451 0         0 $default_value = "()";
452             }
453             } else {
454 0         0 $default_value = "'$default_value'";
455             }
456 0         0 $default_value = "\nDefault value $default_value if not specified";
457             }
458              
459 1         4 push @data, [$param_name, $param_type, $doc, $default_value];
460 1 50       3 if ($param_type eq 'Boolean') {
461 0         0 push @data, ['no'.$param_name, $param_type, "Make $param_name 'false'" ];
462             }
463             }
464 6         6 my $text = '';
465 6         6 for my $row (@data) {
466 1 50 33     7 if (defined($format) and $format eq 'pod') {
    50 33        
467 0 0       0 $text .= "\n=item " . $row->[0] . ($row->[1]? ' I<' . $row->[1] . '>' : '') . "\n\n" . $row->[2] . "\n". ($row->[3]? $row->[3] . "\n" : '');
    0          
468             }
469             elsif (defined($format) and $format eq 'html') {
470 0 0       0 $text .= "\n\t
" . $row->[0] . ($row->[1]? ' ' . $row->[1] . '' : '') . "
" . $row->[2] . ($row->[3]? "
" . $row->[3] : '') . "
\n";
    0          
471             }
472             else {
473 1   50     6 $text .= sprintf(
474             " %s\n%s\n",
475             Term::ANSIColor::colored($row->[0], 'bold'), # . " " . $row->[1],
476             Text::Wrap::wrap(
477             " ", # 1st line indent,
478             " ", # all other lines indent,
479             $row->[2],
480             $row->[3] || '',
481             ),
482             );
483             }
484             }
485              
486 6         312 return $text;
487             }
488              
489              
490             sub _doc_footer {
491 0     0   0 my $self = shift;
492 0         0 my $pod = '';
493              
494 0         0 my @method_header_map = (
495             'LICENSE' => '_doc_license',
496             'AUTHORS' => '_doc_authors',
497             'CREDITS' => '_doc_credits',
498             'BUGS' => '_doc_bugs',
499             'SEE ALSO' => '_doc_see_also'
500             );
501            
502 0         0 while (@method_header_map) {
503 0         0 my $header = shift @method_header_map;
504 0         0 my $method = shift @method_header_map;
505 0         0 my @txt = $self->$method;
506 0 0 0     0 next if (@txt == 0 or (@txt == 1 and not $txt[0]));
      0        
507 0 0       0 if (@txt == 1) {
508 0         0 my @lines = split("\n",$txt[0]);
509 0         0 $pod .= "=head1 $header\n\n"
510             . join(" \n", @lines)
511             . "\n\n";
512             }
513             else {
514 0         0 $pod .= "=head1 $header\n\n"
515             . join("\n ",@txt);
516 0         0 $pod .= "\n\n";
517             }
518             }
519            
520 0         0 return $pod;
521             }
522              
523             sub _doc_license {
524 0     0   0 return '';
525             }
526              
527             sub _doc_authors {
528 0     0   0 return ();
529             }
530              
531             sub _doc_credits {
532 0     0   0 return '';
533             }
534              
535             sub _doc_bugs {
536 0     0   0 return '';
537             }
538              
539             sub _doc_see_also {
540 0     0   0 return ();
541             }
542              
543              
544             sub _shell_args_usage_string {
545 1     1   26 my $self = shift;
546              
547 1         1 return eval {
548 1 50       9 if ( $self->isa('Command::Tree') ) {
    50          
    0          
549 0         0 return '...';
550             }
551             elsif ($self->can("_execute_body") eq __PACKAGE__->can("_execute_body")) {
552 1         20 return '(no execute!)';
553             }
554             elsif ($self->__meta__->is_abstract) {
555 0         0 return '(no sub commands!)';
556             }
557             else {
558             return join(
559             " ",
560             map {
561 0         0 $self->_shell_arg_usage_string_from_property_meta($_)
  0         0  
562             } $self->_shell_args_property_meta()
563              
564             );
565             }
566             };
567             }
568              
569             sub _shell_args_usage_string_abbreviated {
570 0     0   0 my $self = shift;
571 0         0 my $detailed = $self->_shell_args_usage_string;
572 0 0       0 if (length($detailed) <= 20) {
573 0         0 return $detailed;
574             }
575             else {
576 0         0 return substr($detailed,0,17) . '...';
577             }
578             }
579              
580             sub sub_command_mapping {
581 5     5 0 8 my ($self, $class) = @_;
582 5 50       22 return if !$class;
583 9     9   43 no strict 'refs';
  9         16  
  9         5407  
584 0         0 my $mapping = ${ $class . '::SUB_COMMAND_MAPPING'};
  0         0  
585 0 0       0 if (ref($mapping) eq 'HASH') {
586 0         0 return $mapping;
587             } else {
588 0         0 return;
589             }
590             };
591              
592             sub command_name {
593 5     5 0 7 my $self = shift;
594 5   66     24 my $class = ref($self) || $self;
595 5         9 my $prepend = '';
596              
597              
598             # There can be a hash in the command entry point class that maps
599             # root level tools to classes so they can be in a different location
600             # ...this bit of code considers that misdirection:
601 5         8 my $entry_point_class = $Command::entry_point_class;
602 5         20 my $mapping = $self->sub_command_mapping($entry_point_class);
603 5         15 for my $k (%$mapping) {
604 0         0 my $v = $mapping->{$k};
605 0 0 0     0 if ($v && $v eq $class) {
606 0         0 my @words = grep { $_ ne 'Command' } split(/::/,$class);
  0         0  
607 0         0 return join(' ', $self->_command_name_for_class_word($words[0]), $k);
608             }
609             }
610              
611              
612 5 50 33     26 if (defined($entry_point_class) and $class =~ /^($entry_point_class)(::.+|)$/) {
613 0         0 $prepend = $Command::entry_point_bin;
614 0         0 $class = $2;
615 0 0       0 if ($class =~ s/^:://) {
616 0         0 $prepend .= ' ';
617             }
618             }
619 5         17 my @words = grep { $_ ne 'Command' } split(/::/,$class);
  11         23  
620 5         8 my $n = join(' ', map { $self->_command_name_for_class_word($_) } @words);
  11         27  
621 5         19 return $prepend . $n;
622             }
623              
624             sub command_name_brief {
625 9     9 0 10 my $self = shift;
626 9   33     75 my $class = ref($self) || $self;
627 9         23 my @words = grep { $_ ne 'Command' } split(/::/,$class);
  33         42  
628 9         16 my $n = join(' ', map { $self->_command_name_for_class_word($_) } $words[-1]);
  9         33  
629 9         39 return $n;
630             }
631              
632             sub color_command_name {
633 0     0 0 0 my $text = shift;
634            
635 0         0 my $colored_text = [];
636              
637 0         0 my @COLOR_TEMPLATES = ('red', 'bold red', 'magenta', 'bold magenta');
638 0         0 my @parts = split(/\s+/, $text);
639 0         0 for(my $i = 0 ; $i < @parts ; $i++ ){
640 0 0       0 push @$colored_text, ($i < @COLOR_TEMPLATES) ? Term::ANSIColor::colored($parts[$i], $COLOR_TEMPLATES[$i]) : $parts[$i];
641             }
642            
643 0         0 return join(' ', @$colored_text);
644             }
645              
646             sub _base_command_class_and_extension {
647 0     0   0 my $self = shift;
648 0   0     0 my $class = ref($self) || $self;
649 0         0 return ($class =~ /^(.*)::([^\:]+)$/);
650             }
651              
652             sub _command_name_for_class_word {
653 45     45   55 my $self = shift;
654 45         43 my $s = shift;
655 45         67 $s =~ s/_/-/g;
656 45         228 $s =~ s/^([A-Z])/\L$1/; # ignore first capital because that is assumed
657 45         91 $s =~ s/([A-Z])/-$1/g; # all other capitals prepend a dash
658 45         92 $s =~ s/([a-zA-Z])([0-9])/$1$2/g; # treat number as begining word
659 45         56 $s = lc($s);
660 45         93 return $s;
661             }
662              
663             sub _pod2txt {
664 1     1   1 my $txt = shift;
665 1         1 my $output = '';
666 1         11 my $parser = Pod::Simple::Text->new;
667 1         113 $parser->no_errata_section(1);
668 1         10 $parser->output_string($output);
669 1         976 $parser->parse_string_document("=pod\n\n$txt");
670 1         1003 return $output;
671             }
672              
673             sub _additional_help_sections {
674 1     1   2 return;
675             }
676              
677             1;