File Coverage

blib/lib/App/Framework/Feature/Pod.pm
Criterion Covered Total %
statement 153 158 96.8
branch 57 66 86.3
condition 6 9 66.6
subroutine 14 14 100.0
pod 9 9 100.0
total 239 256 93.3


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   9705 use strict ;
  26         71  
  26         5781  
20 26     26   181 use Carp ;
  26         66  
  26         4716  
21              
22             our $VERSION = "1.002" ;
23              
24              
25             #============================================================================================
26             # USES
27             #============================================================================================
28 26     26   174 use Pod::Usage ;
  26         73  
  26         6892  
29              
30 26     26   185 use App::Framework::Feature ;
  26         61  
  26         764  
31 26     26   162 use App::Framework::Base ;
  26         69  
  26         194269  
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 1152 my ($obj, %args) = @_ ;
108              
109 26   33     857 my $class = ref($obj) || $obj ;
110              
111             # Create object
112 26         1384 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         332 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 191 my $class = shift ;
149 26         298 my (%args) = @_ ;
150              
151             # Add extra fields
152 26         1266 $class->add_fields(\%FIELDS, \%args) ;
153              
154             # init class
155 26         599 $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 83 my $this = shift ;
186              
187 36         467 $this->_dbg_prt(["application_entry()\n"]) ;
188              
189             ## Handle special options
190 36         1119 my $app = $this->app ;
191 36         154 my %opts = $app->options() ;
192 36         255 $this->_dbg_prt(["pod options=",\%opts]) ;
193 36 100 66     421 if ($opts{'man'} || $opts{'help'})
194             {
195 1         7 $this->_dbg_prt(["pod man page=$opts{'man'} \n"]) ;
196 1 50       5 my $type = $opts{'man'} ? 'man' : 'help' ;
197 1         12 $app->usage($type) ;
198 1         20 $app->exit(0) ;
199             }
200 35 100       189 if ($opts{'man-dev'})
201             {
202 1         14 $app->usage('man-dev') ;
203 1         17 $app->exit(0) ;
204             }
205 34 50       320 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 16 my $this = shift ;
227 8         19 my ($developer) = @_ ;
228              
229 8         43 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         196 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 24 my $this = shift ;
261 8         16 my ($developer) = @_ ;
262              
263 8         325 my $name = $this->app->name() ;
264 8         217 my $summary = $this->app->summary() ;
265 8         81 my $synopsis = $this->get_synopsis() ;
266 8         225 my $version = $this->app->version() ;
267              
268 8         60 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         205 my $names_aref = $this->app->feature('Options')->option_names() ;
284 8         44 foreach my $option_name (@$names_aref)
285             {
286 90         2286 my $option_entry_href = $this->app->feature('Options')->option_entry($option_name) ;
287 90         154 my $default = "" ;
288 90 100       220 if ($option_entry_href->{'default'})
289             {
290 4         15 $default = "[Default: $option_entry_href->{'default'}]" ;
291             }
292              
293 90         129 my $multi = "" ;
294 90 100       195 if ($option_entry_href->{dest_type})
295             {
296 4         9 $multi = "(option may be specified multiple times)" ;
297             }
298            
299 90 100       128 if ($developer)
300             {
301 15         126 $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 75 100       447 $pod .= sprintf " -%-20s $option_entry_href->{summary}\t$default\t$multi\n", $option_entry_href->{'pod_spec'}
307             unless $option_entry_href->{'developer'} ;
308             }
309             }
310            
311 8 50       33 unless (@$names_aref)
312             {
313 0         0 $pod .= " NONE\n" ;
314             }
315              
316 8         82 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 19 my $this = shift ;
332 8         17 my ($developer) = @_ ;
333              
334 8         18 my $pod ="\n${POD_HEAD}1 OPTIONS\n\n" ;
335              
336 8 100       48 if ($developer)
337             {
338 1         4 $pod .= "Get options from application object as:\n my \%opts = \$app->options();\n\n" ;
339             }
340              
341 8         27 $pod .= "${POD_OVER} 8\n\n" ;
342              
343              
344             # Cycle through
345 8         202 my $names_aref = $this->app->feature('Options')->option_names() ;
346 8         35 foreach my $option_name (@$names_aref)
347             {
348 90         2530 my $option_entry_href = $this->app->feature('Options')->option_entry($option_name) ;
349 90         403 $this->_dbg_prt(["entry for $option_name=",$option_entry_href]) ;
350 90         195 my $default = "" ;
351 90 100       206 if ($option_entry_href->{'default'})
352             {
353 4         12 $default = "[Default: $option_entry_href->{'default'}]" ;
354             }
355              
356 90         113 my $show = 1 ;
357 90 100 100     277 $show = 0 if ($option_entry_href->{'developer'} && !$developer) ;
358 90 100       193 if ($show)
359             {
360 69 100       121 if ($developer)
361             {
362 15         67 $pod .= "=item -$option_entry_href->{spec} $default # Access as \$opts{$option_entry_href->{field}} \n" ;
363             }
364             else
365             {
366 54         126 $pod .= "=item B<-$option_entry_href->{pod_spec}> $default\n" ;
367             }
368 69         138 $pod .= "\n$option_entry_href->{description}\n" ;
369            
370 69 100       209 if ($option_entry_href->{dest_type})
371             {
372 4         8 $pod .= "This option may be specified multiple times.\n" ;
373            
374 4 100       27 if ($developer)
375             {
376 2         4 my $dtype = "" ;
377 2 100       13 if ($option_entry_href->{dest_type} eq '@')
    50          
378             {
379 1         2 $dtype = 'ARRAY' ;
380             }
381             elsif ($option_entry_href->{dest_type} eq '%')
382             {
383 1         3 $dtype = 'HASH' ;
384             }
385 2         8 $pod .= "(The option values will be available internally via the $dtype ref \$opts{$option_entry_href->{field}})\n" ;
386             }
387             }
388 69         127 $pod .= "\n" ;
389             }
390             }
391              
392 8 50       32 unless (@$names_aref)
393             {
394 0         0 $pod .= " NONE\n" ;
395             }
396              
397 8         17 $pod .= "\n=back\n\n" ;
398              
399 8         98 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 17 my $this = shift ;
415 8         16 my ($developer) = @_ ;
416              
417 8         27 my $pod ="\n${POD_HEAD}1 ARGS\n\n" ;
418              
419 8 100       49 if ($developer)
420             {
421 1         3 $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         32 $pod .= "${POD_OVER} 8\n\n" ;
425              
426             # Cycle through
427 8         200 my $names_aref = $this->app->feature('Args')->arg_names() ;
428 8         42 foreach my $arg_name (@$names_aref)
429             {
430 31         721 my $arg_entry_href = $this->app->feature('Args')->arg_entry($arg_name) ;
431              
432 31         50 my $default = "" ;
433 31 100       77 if ($arg_entry_href->{'default'})
434             {
435 29         57 $default = "[Default: $arg_entry_href->{'default'}]" ;
436             }
437              
438 31         32 my $show = 1 ;
439 31 50       71 if ($show)
440             {
441 31 100       53 if ($developer)
442             {
443 1         7 $pod .= "=item * $arg_entry_href->{spec} $default # Access as \$args{$arg_entry_href->{name}} \n" ;
444             }
445             else
446             {
447 30         69 $pod .= "=item B<* $arg_entry_href->{pod_spec}> $default\n" ;
448             }
449 31         42 $pod .= "\n$arg_entry_href->{description}\n" ;
450            
451 31 100       63 if ($arg_entry_href->{dest_type})
452             {
453 5         15 $pod .= "This arg may be specified multiple times.\n" ;
454             }
455 31         51 $pod .= "\n" ;
456             }
457             }
458              
459 8 100       29 unless (@$names_aref)
460             {
461 1         3 $pod .= " NONE\n" ;
462             }
463              
464 8         24 $pod .= "\n=back\n\n" ;
465              
466 8         53 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 20 my $this = shift ;
482 8         15 my ($developer) = @_ ;
483              
484 8         228 my $description = $this->app->description() ;
485              
486 8         34 my $pod =<<"POD_DESC" ;
487              
488             ${POD_HEAD}1 DESCRIPTION
489              
490             $description
491            
492             POD_DESC
493            
494 8         75 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 15 my $this = shift ;
510              
511 8         237 my $synopsis = $this->app->synopsis() ;
512 8 100       38 if (!$synopsis)
513             {
514 5         160 my %opts = $this->app->options() ;
515            
516             # start with basics
517 5         164 my $app = $this->app->name() ;
518 5         19 $synopsis = "$app [options] " ;
519            
520             ## Get args
521 5         135 my $names_aref = $this->app->feature('Args')->arg_names() ;
522 5         26 foreach my $arg_name (@$names_aref)
523             {
524 10         238 my $arg_entry_href = $this->app->feature('Args')->arg_entry($arg_name) ;
525              
526 10         23 my $type = "" ;
527 10 100       35 if ($arg_entry_href->{'type'} eq 'f')
528             {
529 8         23 $type = "file" ;
530             }
531 10 100       26 if ($arg_entry_href->{'type'} eq 'd')
532             {
533 2         2 $type = "directory" ;
534             }
535              
536 10 50       29 if ($type)
537             {
538 10         18 my $direction = "input " ;
539 10 100       50 if ($arg_entry_href->{'direction'} eq 'o')
540             {
541 3         4 $direction = "output " ;
542             }
543 10         23 $type = " ($direction $type)" ;
544             }
545              
546 10         19 my $suffix = "" ;
547 10 100       60 if ($arg_entry_href->{'dest_type'})
548             {
549 2         9 $suffix = "(s)" ;
550             }
551            
552 10 50       37 if ($arg_entry_href->{'optional'})
553             {
554 0         0 $synopsis .= 'I<[' ;
555             }
556             else
557             {
558 10         13 $synopsis .= 'B<' ;
559             }
560            
561 10         25 $synopsis .= "{$arg_name$type$suffix}" ;
562 10 50       56 $synopsis .= ']' if $arg_entry_href->{'optional'} ;
563 10         26 $synopsis .= '> ' ;
564             }
565            
566             # set our best guess
567 5         146 $this->app->synopsis($synopsis) ;
568             }
569              
570 8         26 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__