File Coverage

blib/lib/App/Framework/Feature/Pod.pm
Criterion Covered Total %
statement 153 158 96.8
branch 57 66 86.3
condition 5 9 55.5
subroutine 14 14 100.0
pod 9 9 100.0
total 238 256 92.9


line stmt bran cond sub pod time code
1             package App::Framework::Feature::Pod ;
2              
3             =head1 NAME
4              
5             App::Framework::Feature::Pod - Application documentation
6              
7             =head1 SYNOPSIS
8              
9             # Data feature is loaded by default as if the script contained:
10             use App::Framework '+Pod' ;
11              
12              
13             =head1 DESCRIPTION
14              
15             Used by the application framework to create pod-based man pages and help.
16              
17             =cut
18              
19 26     26   4694 use strict ;
  26         40  
  26         714  
20 26     26   91 use Carp ;
  26         38  
  26         2352  
21              
22             our $VERSION = "1.002" ;
23              
24              
25             #============================================================================================
26             # USES
27             #============================================================================================
28 26     26   123 use Pod::Usage ;
  26         31  
  26         3249  
29              
30 26     26   130 use App::Framework::Feature ;
  26         33  
  26         598  
31 26     26   95 use App::Framework::Base ;
  26         38  
  26         36772  
32              
33             #============================================================================================
34             # OBJECT HIERARCHY
35             #============================================================================================
36             our @ISA = qw(App::Framework::Feature) ;
37              
38             #============================================================================================
39             # GLOBALS
40             #============================================================================================
41              
42             my $POD_HEAD = "=head" ;
43             my $POD_OVER = "=over" ;
44              
45              
46             my %FIELDS = (
47             ) ;
48              
49              
50             =head2 ADDITIONAL COMMAND LINE OPTIONS
51              
52             This feature adds the following additional command line options to any application:
53              
54             =over 4
55              
56             =item B<-help> - show help
57              
58             Displays brief help message then exits
59              
60             =item B<-man> - show full man pages
61              
62             Displays the application's full man pages then exits
63              
64             =item B<-man-dev> - show full developer man pages
65              
66             Displays the application's full developer man pages then exits. Developer man pages contain extra
67             information and is intended for the application developer (rather than the end user).
68              
69             =item B<-pod> - show man pages as pod [I]
70              
71             Outputs the full pod text.
72              
73             =back
74              
75             =cut
76              
77              
78             my @OPTIONS = (
79             ['h|"help"', 'Print help', 'Show brief help message then exit'],
80             ['man', 'Full documentation', 'Show full man page then exit' ],
81             ['man-dev', 'Full developer\'s documentation', 'Show full man page for the application developer then exit' ],
82             ['dev:pod', 'Output full pod', 'Show full man page as pod then exit' ],
83             ) ;
84              
85              
86             #============================================================================================
87              
88             =head2 CONSTRUCTOR
89              
90             =over 4
91              
92             =cut
93              
94             #============================================================================================
95              
96              
97             =item B< new([%args]) >
98              
99             Create a new Pod.
100              
101             The %args are specified as they would be in the B method (see L).
102              
103             =cut
104              
105             sub new
106             {
107 26     26 1 286 my ($obj, %args) = @_ ;
108              
109 26   33     559 my $class = ref($obj) || $obj ;
110              
111             # Create object
112 26         435 my $this = $class->SUPER::new(%args,
113             'feature_options' => \@OPTIONS,
114             'registered' => [qw/application_entry/],
115             ) ;
116              
117             #$this->debug(2);
118              
119            
120 26         90 return($this) ;
121             }
122              
123              
124              
125             #============================================================================================
126              
127             =back
128              
129             =head2 CLASS METHODS
130              
131             =over 4
132              
133             =cut
134              
135             #============================================================================================
136              
137              
138             #-----------------------------------------------------------------------------
139              
140             =item B< init_class([%args]) >
141              
142             Initialises the Pod object class variables.
143              
144             =cut
145              
146             sub init_class
147             {
148 26     26 1 85 my $class = shift ;
149 26         109 my (%args) = @_ ;
150              
151             # Add extra fields
152 26         301 $class->add_fields(\%FIELDS, \%args) ;
153              
154             # init class
155 26         235 $class->SUPER::init_class(%args) ;
156              
157             }
158              
159             #============================================================================================
160              
161             =back
162              
163             =head2 OBJECT METHODS
164              
165             =over 4
166              
167             =cut
168              
169             #============================================================================================
170              
171             #----------------------------------------------------------------------------
172              
173             =item B
174              
175             Called by the application framework at the start of the application.
176            
177             This method checks for the user specifying any of the options described above (see L) and handles
178             them if so.
179              
180             =cut
181              
182              
183             sub application_entry
184             {
185 36     36 1 61 my $this = shift ;
186              
187 36         270 $this->_dbg_prt(["application_entry()\n"]) ;
188              
189             ## Handle special options
190 36         860 my $app = $this->app ;
191 36         123 my %opts = $app->options() ;
192 36         202 $this->_dbg_prt(["pod options=",\%opts]) ;
193 36 100 33     221 if ($opts{'man'} || $opts{'help'})
194             {
195 1         5 $this->_dbg_prt(["pod man page=$opts{'man'} \n"]) ;
196 1 50       3 my $type = $opts{'man'} ? 'man' : 'help' ;
197 1         27 $app->usage($type) ;
198 1         15 $app->exit(0) ;
199             }
200 35 100       160 if ($opts{'man-dev'})
201             {
202 1         6 $app->usage('man-dev') ;
203 1         11 $app->exit(0) ;
204             }
205 34 50       207 if ($opts{'pod'})
206             {
207 0         0 print $this->pod() ;
208 0         0 $app->exit(0) ;
209             }
210            
211             }
212              
213              
214             #----------------------------------------------------------------------------
215              
216             =item B
217              
218             Return full pod of application
219              
220             If the optional $developer flag is set, returns application developer biased information
221              
222             =cut
223              
224             sub pod
225             {
226 8     8 1 11 my $this = shift ;
227 8         10 my ($developer) = @_ ;
228              
229 8         26 my $pod =
230             $this->pod_head($developer) .
231             $this->pod_args($developer) .
232             $this->pod_options($developer) .
233             $this->pod_description($developer) .
234             "\n=cut\n" ;
235 8         79 return $pod ;
236             }
237            
238             #----------------------------------------------------------------------------
239              
240             =item B< Pod([%args]) >
241              
242             Alias to L
243              
244             =cut
245              
246             *Pod = \&pod ;
247              
248             #----------------------------------------------------------------------------
249              
250             =item B
251              
252             Return pod heading of application
253              
254             If the optional $developer flag is set, returns application developer biased information
255              
256             =cut
257              
258             sub pod_head
259             {
260 8     8 1 11 my $this = shift ;
261 8         10 my ($developer) = @_ ;
262              
263 8         232 my $name = $this->app->name() ;
264 8         158 my $summary = $this->app->summary() ;
265 8         28 my $synopsis = $this->get_synopsis() ;
266 8         147 my $version = $this->app->version() ;
267              
268 8         34 my $pod =<<"POD_HEAD" ;
269              
270             ${POD_HEAD}1 NAME
271              
272             $name (v$version) - $summary
273              
274             ${POD_HEAD}1 SYNOPSIS
275              
276             $synopsis
277              
278             Options:
279              
280             POD_HEAD
281              
282             # Cycle through
283 8         155 my $names_aref = $this->app->feature('Options')->option_names() ;
284 8         24 foreach my $option_name (@$names_aref)
285             {
286 90         1547 my $option_entry_href = $this->app->feature('Options')->option_entry($option_name) ;
287 90         82 my $default = "" ;
288 90 100       128 if ($option_entry_href->{'default'})
289             {
290 4         12 $default = "[Default: $option_entry_href->{'default'}]" ;
291             }
292              
293 90         69 my $multi = "" ;
294 90 100       115 if ($option_entry_href->{dest_type})
295             {
296 4         5 $multi = "(option may be specified multiple times)" ;
297             }
298            
299 90 100       94 if ($developer)
300             {
301 15         45 $pod .= sprintf " -%-20s $option_entry_href->{summary}\t$default\n", $option_entry_href->{'spec'} ;
302             }
303             else
304             {
305             # show option if it's not a devevloper option
306             $pod .= sprintf " -%-20s $option_entry_href->{summary}\t$default\t$multi\n", $option_entry_href->{'pod_spec'}
307 75 100       280 unless $option_entry_href->{'developer'} ;
308             }
309             }
310            
311 8 50       31 unless (@$names_aref)
312             {
313 0         0 $pod .= " NONE\n" ;
314             }
315              
316 8         53 return $pod ;
317             }
318              
319             #----------------------------------------------------------------------------
320              
321             =item B
322              
323             Return pod of options of application
324              
325             If the optional $developer flag is set, returns application developer biased information
326              
327             =cut
328              
329             sub pod_options
330             {
331 8     8 1 9 my $this = shift ;
332 8         9 my ($developer) = @_ ;
333              
334 8         23 my $pod ="\n${POD_HEAD}1 OPTIONS\n\n" ;
335              
336 8 100       18 if ($developer)
337             {
338 1         1 $pod .= "Get options from application object as:\n my \%opts = \$app->options();\n\n" ;
339             }
340              
341 8         17 $pod .= "${POD_OVER} 8\n\n" ;
342              
343              
344             # Cycle through
345 8         154 my $names_aref = $this->app->feature('Options')->option_names() ;
346 8         19 foreach my $option_name (@$names_aref)
347             {
348 90         1580 my $option_entry_href = $this->app->feature('Options')->option_entry($option_name) ;
349 90         249 $this->_dbg_prt(["entry for $option_name=",$option_entry_href]) ;
350 90         95 my $default = "" ;
351 90 100       127 if ($option_entry_href->{'default'})
352             {
353 4         9 $default = "[Default: $option_entry_href->{'default'}]" ;
354             }
355              
356 90         70 my $show = 1 ;
357 90 100 100     189 $show = 0 if ($option_entry_href->{'developer'} && !$developer) ;
358 90 100       127 if ($show)
359             {
360 69 100       77 if ($developer)
361             {
362 15         32 $pod .= "=item -$option_entry_href->{spec} $default # Access as \$opts{$option_entry_href->{field}} \n" ;
363             }
364             else
365             {
366 54         103 $pod .= "=item B<-$option_entry_href->{pod_spec}> $default\n" ;
367             }
368 69         93 $pod .= "\n$option_entry_href->{description}\n" ;
369            
370 69 100       91 if ($option_entry_href->{dest_type})
371             {
372 4         8 $pod .= "This option may be specified multiple times.\n" ;
373            
374 4 100       14 if ($developer)
375             {
376 2         0 my $dtype = "" ;
377 2 100       6 if ($option_entry_href->{dest_type} eq '@')
    50          
378             {
379 1         1 $dtype = 'ARRAY' ;
380             }
381             elsif ($option_entry_href->{dest_type} eq '%')
382             {
383 1         1 $dtype = 'HASH' ;
384             }
385 2         5 $pod .= "(The option values will be available internally via the $dtype ref \$opts{$option_entry_href->{field}})\n" ;
386             }
387             }
388 69         80 $pod .= "\n" ;
389             }
390             }
391              
392 8 50       26 unless (@$names_aref)
393             {
394 0         0 $pod .= " NONE\n" ;
395             }
396              
397 8         12 $pod .= "\n=back\n\n" ;
398              
399 8         37 return $pod ;
400             }
401              
402             #----------------------------------------------------------------------------
403              
404             =item B
405              
406             Return pod of args of application
407              
408             If the optional $developer flag is set, returns application developer biased information
409              
410             =cut
411              
412             sub pod_args
413             {
414 8     8 1 12 my $this = shift ;
415 8         10 my ($developer) = @_ ;
416              
417 8         19 my $pod ="\n${POD_HEAD}1 ARGS\n\n" ;
418              
419 8 100       21 if ($developer)
420             {
421 1         6 $pod .= "Get args from application object as:\n my \@args = \$app->args();\n# or\n my \%args = \$app->feature('Args')->arghash();\n\n" ;
422             }
423              
424 8         18 $pod .= "${POD_OVER} 8\n\n" ;
425              
426             # Cycle through
427 8         150 my $names_aref = $this->app->feature('Args')->arg_names() ;
428 8         25 foreach my $arg_name (@$names_aref)
429             {
430 31         500 my $arg_entry_href = $this->app->feature('Args')->arg_entry($arg_name) ;
431              
432 31         31 my $default = "" ;
433 31 100       54 if ($arg_entry_href->{'default'})
434             {
435 29         38 $default = "[Default: $arg_entry_href->{'default'}]" ;
436             }
437              
438 31         26 my $show = 1 ;
439 31 50       49 if ($show)
440             {
441 31 100       32 if ($developer)
442             {
443 1         4 $pod .= "=item * $arg_entry_href->{spec} $default # Access as \$args{$arg_entry_href->{name}} \n" ;
444             }
445             else
446             {
447 30         51 $pod .= "=item B<* $arg_entry_href->{pod_spec}> $default\n" ;
448             }
449 31         37 $pod .= "\n$arg_entry_href->{description}\n" ;
450            
451 31 100       44 if ($arg_entry_href->{dest_type})
452             {
453 5         12 $pod .= "This arg may be specified multiple times.\n" ;
454             }
455 31         38 $pod .= "\n" ;
456             }
457             }
458              
459 8 100       26 unless (@$names_aref)
460             {
461 1         3 $pod .= " NONE\n" ;
462             }
463              
464 8         10 $pod .= "\n=back\n\n" ;
465              
466 8         42 return $pod ;
467             }
468              
469             #----------------------------------------------------------------------------
470              
471             =item B
472              
473             Return pod of description of application
474              
475             If the optional $developer flag is set, returns application developer biased information
476              
477             =cut
478              
479             sub pod_description
480             {
481 8     8 1 12 my $this = shift ;
482 8         15 my ($developer) = @_ ;
483              
484 8         149 my $description = $this->app->description() ;
485              
486 8         24 my $pod =<<"POD_DESC" ;
487              
488             ${POD_HEAD}1 DESCRIPTION
489              
490             $description
491            
492             POD_DESC
493            
494 8         46 return $pod ;
495             }
496              
497              
498             #----------------------------------------------------------------------------
499              
500             =item B
501              
502             Check to ensure synopsis is set. If not, set based on application name and any Args
503             settings
504              
505             =cut
506              
507             sub get_synopsis
508             {
509 8     8 1 11 my $this = shift ;
510              
511 8         186 my $synopsis = $this->app->synopsis() ;
512 8 100       25 if (!$synopsis)
513             {
514 5         101 my %opts = $this->app->options() ;
515            
516             # start with basics
517 5         114 my $app = $this->app->name() ;
518 5         15 $synopsis = "$app [options] " ;
519            
520             ## Get args
521 5         88 my $names_aref = $this->app->feature('Args')->arg_names() ;
522 5         16 foreach my $arg_name (@$names_aref)
523             {
524 10         192 my $arg_entry_href = $this->app->feature('Args')->arg_entry($arg_name) ;
525              
526 10         14 my $type = "" ;
527 10 100       23 if ($arg_entry_href->{'type'} eq 'f')
528             {
529 8         12 $type = "file" ;
530             }
531 10 100       22 if ($arg_entry_href->{'type'} eq 'd')
532             {
533 2         2 $type = "directory" ;
534             }
535              
536 10 50       17 if ($type)
537             {
538 10         10 my $direction = "input " ;
539 10 100       22 if ($arg_entry_href->{'direction'} eq 'o')
540             {
541 3         2 $direction = "output " ;
542             }
543 10         18 $type = " ($direction $type)" ;
544             }
545              
546 10         13 my $suffix = "" ;
547 10 100       31 if ($arg_entry_href->{'dest_type'})
548             {
549 2         2 $suffix = "(s)" ;
550             }
551            
552 10 50       33 if ($arg_entry_href->{'optional'})
553             {
554 0         0 $synopsis .= 'I<[' ;
555             }
556             else
557             {
558 10         9 $synopsis .= 'B<' ;
559             }
560            
561 10         22 $synopsis .= "{$arg_name$type$suffix}" ;
562 10 50       17 $synopsis .= ']' if $arg_entry_href->{'optional'} ;
563 10         12 $synopsis .= '> ' ;
564             }
565            
566             # set our best guess
567 5         100 $this->app->synopsis($synopsis) ;
568             }
569              
570 8         17 return $synopsis ;
571             }
572              
573              
574              
575             # ============================================================================================
576             # PRIVATE METHODS
577             # ============================================================================================
578              
579              
580             # ============================================================================================
581             # END OF PACKAGE
582              
583             =back
584              
585             =head1 DIAGNOSTICS
586              
587             Setting the debug flag to level 1 prints out (to STDOUT) some debug messages, setting it to level 2 prints out more verbose messages.
588              
589             =head1 AUTHOR
590              
591             Steve Price C<< >>
592              
593             =head1 BUGS
594              
595             None that I know of!
596              
597             =cut
598              
599              
600             1;
601              
602             __END__