File Coverage

blib/lib/App/Base/Script/Common.pm
Criterion Covered Total %
statement 121 121 100.0
branch 12 12 100.0
condition 6 9 66.6
subroutine 27 27 100.0
pod 10 10 100.0
total 176 179 98.3


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