File Coverage

blib/lib/App/Base/Script/Common.pm
Criterion Covered Total %
statement 118 118 100.0
branch 12 12 100.0
condition 6 9 66.6
subroutine 26 26 100.0
pod 10 10 100.0
total 172 175 98.2


line stmt bran cond sub pod time code
1             use strict;
2 12     12   133602 use warnings;
  12         32  
  12         393  
3 12     12   69 use 5.010;
  12         22  
  12         316  
4 12     12   217 use Moose::Role;
  12         42  
5 12     12   495  
  12         485552  
  12         87  
6             our $VERSION = '0.08'; ## VERSION
7              
8             =head1 NAME
9              
10             App::Base::Script::Common - Behaviors common to App::Base::Script and App::Base::Daemon
11              
12             =head1 DESCRIPTION
13              
14             App::Base::Script::Common provides infrastructure that is common to the
15             App::Base::Script and App::Base::Daemon classes, including options parsing.
16              
17             =cut
18              
19             use App::Base::Script::Option;
20 12     12   71845  
  12         48  
  12         77  
21             use Cwd qw( abs_path );
22 12     12   8867 use Getopt::Long;
  12         28  
  12         781  
23 12     12   9781 use IO::Handle;
  12         122733  
  12         55  
24 12     12   3286 use List::Util qw( max );
  12         17225  
  12         495  
25 12     12   77 use Path::Tiny;
  12         30  
  12         980  
26 12     12   946 use POSIX qw( strftime );
  12         12212  
  12         530  
27 12     12   1401 use Text::Reform qw( form break_wrap );
  12         16663  
  12         105  
28 12     12   22695  
  12         80247  
  12         79  
29             use MooseX::Types::Moose qw( Str Bool );
30 12     12   2023  
  12         30  
  12         141  
31             has 'return_value' => (
32             is => 'rw',
33             default => 0
34             );
35              
36             =head1 REQUIRED SUBCLASS METHODS
37              
38             =head2 documentation
39              
40             Returns a scalar (string) containing the documentation portion
41             of the script's usage statement.
42              
43             =cut
44              
45             requires 'documentation'; # Seriously, it does.
46              
47             # For our own subclasses like App::Base::Script and App::Base::Daemon
48              
49             =head2 __run
50              
51             For INTERNAL USE ONLY: Used by subclasses such as App::Base::Script and
52             App::Base::Daemon to redefine dispatch rules to their own required
53             subclass methods such as script_run() and daemon_run().
54              
55             =cut
56              
57             requires '__run';
58              
59             =head2 error
60              
61             All App::Base::Script::Common-implementing classes must have an
62             error() method that handles exceptional cases which also
63             require a shutdown of the running script/daemon/whatever.
64              
65             =cut
66              
67             requires 'error';
68              
69             =head1 OPTIONAL SUBCLASS METHODS
70              
71             =head2 options
72              
73             Concrete subclasses can specify their own options list by defining a method
74             called options() which returns an arrayref of hashes with the parameters
75             required to create L<App::Base::Script::Option> objects. Alternatively, your
76             script/daemon can simply get by with the standard --help option provided by its
77             role.
78              
79             =cut
80              
81             my $self = shift;
82             return [];
83 24     24 1 67 }
84 24         90  
85             =head1 ATTRIBUTES
86              
87             =head2 _option_values
88              
89             The (parsed) option values, including defaults values if none were
90             specified, for all of the options declared by $self. This accessor
91             should not be called directly; use getOption() instead.
92              
93             =cut
94              
95             has '_option_values' => (
96             is => 'rw',
97             isa => 'HashRef',
98             );
99              
100             =head2 orig_args
101              
102             An arrayref of arguments as they existed prior to option parsing.
103              
104             =cut
105              
106             has 'orig_args' => (
107             is => 'rw',
108             isa => 'ArrayRef[Str]',
109             );
110              
111             =head2 parsed_args
112              
113             An arrayref of the arguments which remained after option parsing.
114              
115             =cut
116              
117             has 'parsed_args' => (
118             is => 'rw',
119             isa => 'ArrayRef[Str]',
120             );
121              
122             =head2 script_name
123              
124             The name of the running script, computed from $0.
125              
126             =cut
127              
128             has 'script_name' => (
129             is => 'ro',
130             default => sub { path($0)->basename; },
131             );
132              
133             =head1 METHODS
134              
135             =head2 BUILDARGS
136              
137             Combines the results of base_options() and options() and then parses the
138             command-line arguments of the script. Exits with a readable error message
139             if the script was invoked in a nonsensical or invalid manner.
140              
141             =cut
142              
143             my $class = shift;
144             my $arg_ref = shift;
145              
146 43     43 1 9071887 ## no critic (RequireLocalizedPunctuationVars)
147 43         158 $ENV{APP_BASE_SCRIPT_EXE} = abs_path($0);
148             $arg_ref->{orig_args} = [@ARGV];
149              
150 43         2088 my $results = $class->_parse_arguments(\@ARGV);
151 43         353 if ($results->{parse_result}) {
152             $arg_ref->{_option_values} = $results->{option_values};
153 43         316 $arg_ref->{parsed_args} = $results->{parsed_args};
154 43 100       150  
155 41         109 # This exits.
156 41         105 $class->usage(0) if ($results->{option_values}->{'help'});
157             } else {
158             # This exits.
159 41 100       149 $class->usage(1);
160             }
161              
162 2         9 return $arg_ref;
163             }
164              
165 38         780 =head2 all_options
166              
167             Returns the composition of options() and base_options() as list of L<App::Base::Script::Option> objects.
168              
169             =cut
170              
171             my $self = shift;
172             state $cache;
173             my $class = ref($self) || $self;
174             $cache->{$class} //=
175 61     61 1 132 [map { App::Base::Script::Option->new($_) } @{$self->options}, @{$self->base_options}];
176 61         106 return $cache->{$class};
177 61   66     444 }
178              
179 61   100     297 =head2 base_options
  130         3874  
  24         162  
  24         169  
180 61         296  
181             The options provided for every classes which implements App::Base::Script::Common.
182             See BUILT-IN OPTIONS
183              
184             =cut
185              
186             return [{
187             name => 'help',
188             documentation => 'Show this help information',
189             },
190             ];
191             }
192 24     24 1 569  
193             =head2 switch_name_width
194              
195             Computes the maximum width of any of the switch (option) names.
196              
197             =cut
198              
199             my $self = shift;
200             return max(map { length($_->display_name) } @{$self->all_options});
201             }
202              
203             =head2 switches
204              
205 9     9 1 26 Generates the switch table output of the usage statement.
206 9         17  
  29         75  
  9         21  
207             =cut
208              
209             my $self = shift;
210              
211             my $col_width = $ENV{COLUMNS} || 76;
212              
213             my $max_option_length = $self->switch_name_width;
214             my $sw = '[' x ($max_option_length + 2);
215             my $doc = '[' x ($col_width - $max_option_length - 1);
216 9     9 1 476  
217             my @lines = map { form {break => break_wrap}, "$sw $doc", '--' . $_->display_name, $_->show_documentation; }
218 9   50     44 (sort { $a->name cmp $b->name } (@{$self->all_options}));
219              
220 9         28 return join('', @lines);
221 9         25 }
222 9         25  
223             =head2 cli_template
224 29         13142  
225 9         14 The template usage form that should be shown to the user in the usage
  40         999  
  9         23  
226             statement when --help or an invalid invocation is provided.
227 9         5949  
228             Defaults to "(program name) [options]", which is pretty standard Unix.
229              
230             =cut
231              
232             return "$0 [options] "; # Override this if your script has a more complex command-line
233             # invocation template such as "$0[options] company_id [list1 [, list2 [, ...]]] "
234             }
235              
236             =head2 usage
237              
238             Outputs a statement explaining the usage of the script, then exits.
239              
240 6     6 1 59 =cut
241              
242             my $self = shift;
243              
244             my $col_width = $ENV{COLUMNS} || 76;
245              
246             my $format = '[' x $col_width;
247              
248             my $message = join('', "\n", form({break => break_wrap}, $format, ["Usage: " . $self->cli_template, split(/[\r\n]/, $self->documentation)]));
249              
250             $message .= "\nOptions:\n\n";
251 6     6 1 1267  
252             $message .= $self->switches . "\n\n";
253 6   50     36  
254             print STDERR $message;
255 6         21  
256             exit(1);
257 6         24  
258             }
259 6         4256  
260             =head2 getOption
261 6         21  
262             Returns the value of a specified option. For example, getOption('help') returns
263 6         57 1 or 0 depending on whether the --help option was specified. For option types
264             which are non-boolean (see App::Base::Script::Option) the return value is the actual
265 6         28 string/integer/float provided on the common line - or undef if none was provided.
266              
267             =cut
268              
269             my $self = shift;
270             my $option = shift;
271              
272             if (exists($self->_option_values->{$option})) {
273             return $self->_option_values->{$option};
274             } else {
275             die "Unknown option $option";
276             }
277              
278             }
279 83     83 1 923  
280 83         338 =head2 run
281              
282 83 100       3456 Runs the script, returning the return value of __run
283 82         2283  
284             =cut
285 1         13  
286             my $self = shift;
287              
288             # This is implemented by subclasses of App::Base::Script::Common
289             $self->__run;
290             return $self->return_value;
291             }
292              
293             =head2 _parse_arguments
294              
295             Parses the arguments in @ARGV, returning a hashref containing:
296              
297 31     31 1 5834 =over 4
298              
299             =item -
300 31         254  
301 17         2366 The parsed arguments (that is, those that should remain in @ARGV)
302              
303             =item -
304              
305             The option values, as a hashref, including default values
306              
307             =item -
308              
309             Whether the parsing encountered any errors
310              
311             =back
312              
313             =cut
314              
315             my $self = shift;
316             my $args = shift;
317              
318             local @ARGV = (@$args);
319              
320             # Build the hash of options to pass to Getopt::Long
321             my $options = $self->all_options;
322             my %options_hash = ();
323             my %getopt_args = ();
324              
325             foreach my $option (@$options) {
326             my $id = $option->name;
327 43     43   158 my $type = $option->option_type;
328 43         91 if ($type eq 'string') {
329             $id .= '=s';
330 43         185 } elsif ($type eq 'integer') {
331             $id .= '=i';
332             } elsif ($type eq 'float') {
333 43         263 $id .= '=f';
334 43         146 }
335 43         135  
336             my $scalar = $option->default;
337 43         157 $getopt_args{$option->name} = \$scalar;
338 233         6218 $options_hash{$id} = \$scalar;
339 233         6415 }
340 233 100       728  
    100          
    100          
341 33         88 my $result = GetOptions(%options_hash);
342             my %option_values = map { $_ => ${$getopt_args{$_}} } (keys %getopt_args);
343 4         8 return {
344             parse_result => $result,
345 4         8 option_values => \%option_values,
346             parsed_args => \@ARGV
347             };
348 233         6155  
349 233         5769 }
350 233         637  
351             =head2 __error
352              
353 43         450 Dispatches its arguments to the subclass-provided error() method (see REQUIRED
354 43         21538 SUBCLASS METHODS), then exits.
  233         346  
  233         532  
355              
356 43         523 =cut
357              
358             my $self = shift;
359             warn(join " ", @_);
360             exit(-1);
361             }
362              
363             no Moose::Role;
364             1;
365              
366              
367             =head1 USAGE
368              
369             Invocation of a App::Base::Script::Common-based program is accomplished as follows:
370              
371 6     6   466 =over 4
372 6         119  
373 6         427 =item -
374              
375             Define a class that derives (via 'use Moose' and 'with') from App::Base::Script::Common
376 12     12   73975  
  12         31  
  12         135  
377             =item -
378              
379             Instantiate an object of that class via new( )
380              
381             =item -
382              
383             Run the program by calling run( ). The return value of run( ) is the exit
384             status of the script, and should typically be passed back to the calling
385             program via exit()
386              
387             =back
388              
389             =head2 The new() method
390              
391             A Moose-style constructor for the App::Base::Script::Common-derived class.
392             Every such class has one important attribute: options -- an array ref of hashes
393             describing options to be added to the command-line processing for the script.
394             See L<App::Base::Script::Option> for more information.
395              
396             =head2 Options handling
397              
398             One of the most useful parts of App::Base::Script::Common is the simplified access to
399             options processing. The getOption() method allows your script to determine the
400             value of a given option, determined as follows:
401              
402             =over 4
403              
404             =item 1
405              
406             If given as a command line option (registered via options hashref)
407              
408             =item 2
409              
410             The default value specified in the App::Base::Script::Option object that
411             was passed to the options() attribute at construction time.
412              
413             =back
414              
415             For example, if your script registers an option 'foo' by saying
416              
417             my $object = MyScript->new(
418             options => [
419             App::Base::Script::Option->new(
420             name => "foo",
421             documentation => "The foo option",
422             option_type => "integer",
423             default => 7,
424             ),
425             ]
426             );
427              
428             Then in script_run() you can say
429              
430             my $foo = $self->getOption("foo")
431              
432             And C<$foo> will be resolved as follows:
433              
434             =over 4
435              
436             =item 1
437              
438             A --foo value specified as a command-line switch
439              
440             =item 2
441              
442             The default value specified at registration time ("bar")
443              
444             =back
445              
446             =head1 BUILT-IN OPTIONS
447              
448             =head2 --help
449              
450             Print a usage statement
451              
452             =cut