File Coverage

blib/lib/MooseX/Getopt/Basic.pm
Criterion Covered Total %
statement 132 133 99.2
branch 46 54 85.1
condition 10 14 71.4
subroutine 26 28 92.8
pod 2 3 66.6
total 216 232 93.1


line stmt bran cond sub pod time code
1             package MooseX::Getopt::Basic;
2             # ABSTRACT: MooseX::Getopt::Basic - role to implement the Getopt::Long functionality
3              
4             our $VERSION = '0.78';
5              
6 27     27   329594 use Moose::Role;
  27         375432  
  27         289  
7              
8 27     27   194654 use MooseX::Getopt::OptionTypeMap;
  27         111  
  27         1368  
9 27     27   17214 use MooseX::Getopt::Meta::Attribute;
  27         384  
  27         1607  
10 27     27   23267 use MooseX::Getopt::Meta::Attribute::NoGetopt;
  27         322  
  27         1983  
11 27     27   16671 use MooseX::Getopt::ProcessedArgv;
  27         323  
  27         1465  
12 27     27   243 use Try::Tiny;
  27         53  
  27         2174  
13 27     27   184 use Carp ();
  27         53  
  27         744  
14              
15 27     27   968 use Getopt::Long 2.37 ();
  27         12870  
  27         1393  
16 27     27   169 use namespace::autoclean;
  27         53  
  27         219  
17              
18             has ARGV => (is => 'rw', isa => 'ArrayRef', traits => ['NoGetopt']);
19             has extra_argv => (is => 'rw', isa => 'ArrayRef', traits => ['NoGetopt']);
20              
21             sub process_argv {
22 112     112 1 13944 my ($class, @params) = @_;
23              
24 112 100       560 my $constructor_params = ( @params == 1 ? $params[0] : {@params} );
25              
26 112         233 my $config_from_file;
27 112 100       670 if($class->meta->does_role('MooseX::ConfigFromFile')) {
28 13         6040 local @ARGV = @ARGV;
29              
30             # just get the configfile arg now out of @ARGV; the rest of the args
31             # will be fetched later
32 13         27 my $configfile;
33 13         113 my $opt_parser = Getopt::Long::Parser->new( config => [ qw( no_auto_help pass_through no_auto_version ) ] );
34 13         4018 $opt_parser->getoptions( "configfile=s" => \$configfile );
35              
36 13         8903 my $cfmeta = $class->meta->find_attribute_by_name('configfile');
37 13         936 my $init_arg = $cfmeta->init_arg;
38              
39             # was it passed to the constructor?
40 13 100       51 if (!defined $configfile)
41             {
42 7 50       30 $configfile = $constructor_params->{$init_arg} if defined $init_arg;
43             }
44              
45 13 100       44 if(!defined $configfile) {
46             # this is a classic legacy usecase documented in
47             # MooseX::ConfigFromFile that we should continue to support
48 7     7   53 $configfile = try { $class->configfile };
  7         461  
49              
50 7 50 33     234 $configfile = $cfmeta->default
51             if not defined $configfile and $cfmeta->has_default;
52              
53             # note that this will die horribly if the default sub depends on
54             # other attributes
55 7 100       122 $configfile = $configfile->($class) if ref $configfile eq 'CODE';
56 7 100       134 if (defined $configfile) {
57             $config_from_file = try {
58 4     4   223 $class->get_config_from_file($configfile);
59             }
60             catch {
61 0 0   0   0 die $_ unless /Specified configfile '\Q$configfile\E' does not exist/;
62 4         26 };
63             }
64              
65 7 100 66     12249 $constructor_params->{$init_arg} = $configfile
66             if defined $configfile and defined $init_arg;
67             }
68             else {
69 6         30 $config_from_file = $class->get_config_from_file($configfile);
70             }
71             }
72              
73 112 50       49394 Carp::croak("Single parameters to new_with_options() must be a HASH ref")
74             unless ref($constructor_params) eq 'HASH';
75              
76 112 100       590 my %processed = $class->_parse_argv(
77             options => [
78             $class->_attrs_to_options( $config_from_file )
79             ],
80             params => $config_from_file ? { %$config_from_file, %$constructor_params } : $constructor_params,
81             );
82              
83 102 100       816 my $params = $config_from_file ? { %$config_from_file, %{$processed{params}} } : $processed{params};
  9         38  
84              
85             # did the user request usage information?
86 102 100 100     1198 if ( $processed{usage} and $params->{help_flag} ) {
87 10         3738 $class->print_usage_text($processed{usage});
88 9         1982 exit 0;
89             }
90              
91             return MooseX::Getopt::ProcessedArgv->new(
92             argv_copy => $processed{argv_copy},
93             extra_argv => $processed{argv},
94             usage => $processed{usage},
95 92         112636 constructor_params => $constructor_params, # explicit params to ->new
96             cli_params => $params, # params from CLI
97             );
98             }
99              
100             sub new_with_options {
101 111     111 1 664668 my ($class, @params) = @_;
102              
103 111         500 my $pa = $class->process_argv(@params);
104              
105             # $pa->constructor_params contains everything passed to new_with_options,
106             # so it may contain the "argv" key, which may not exist on the class
107 91         198 my %constructor_params = %{ $pa->constructor_params };
  91         4078  
108 91 50       527 delete $constructor_params{argv} if (not $class->meta->find_attribute_by_name('argv'));
109              
110             $class->new(
111             ARGV => $pa->argv_copy,
112             extra_argv => $pa->extra_argv,
113             ( $pa->usage ? ( usage => $pa->usage ) : () ),
114             %constructor_params, # explicit params to ->new
115 91 100       11101 %{ $pa->cli_params }, # params from CLI
  91         3316  
116             );
117             }
118              
119 18     18   68 sub _getopt_spec { shift->_traditional_spec(@_); }
120              
121             sub _parse_argv {
122 112     112   561 my ( $class, %params ) = @_;
123              
124 112 100       212 local @ARGV = @{ $params{params}{argv} || \@ARGV };
  112         978  
125              
126 112         635 my ( $opt_spec, $name_to_init_arg ) = $class->_getopt_spec(%params);
127              
128             # Get a clean copy of the original @ARGV
129 112         386 my $argv_copy = [ @ARGV ];
130              
131 112         203 my @warnings;
132             my ( $parsed_options, $usage ) = try {
133 112     112   6290 local $SIG{__WARN__} = sub { push @warnings, @_ };
  8         9692  
134              
135 112         630 return $class->_getopt_get_options(\%params, $opt_spec);
136             }
137             catch {
138 10     10   10988 $class->_getopt_spec_exception(\@warnings, $_);
139 112         1142 };
140              
141 102 50       160648 $class->_getopt_spec_warnings(@warnings) if @warnings;
142              
143             # Get a copy of the Getopt::Long-mangled @ARGV
144 102         349 my $argv_mangled = [ @ARGV ];
145              
146             my %constructor_args = (
147             map {
148 102         692 $name_to_init_arg->{$_} => $parsed_options->{$_}
  121         509  
149             } keys %$parsed_options,
150             );
151              
152             return (
153 102 100       1268 params => \%constructor_args,
154             argv_copy => $argv_copy,
155             argv => $argv_mangled,
156             ( defined($usage) ? ( usage => $usage ) : () ),
157             );
158             }
159              
160             sub _getopt_get_options {
161 18     18   49 my ($class, $params, $opt_spec) = @_;
162 18         27 my %options;
163 18         95 Getopt::Long::GetOptions(\%options, @$opt_spec);
164 18         15773 return ( \%options, undef );
165             }
166              
167       0     sub _getopt_spec_warnings { }
168              
169             sub _getopt_spec_exception {
170 10     10   36 my ($self, $warnings, $exception) = @_;
171 10         326 die @$warnings, $exception;
172             }
173              
174             # maintained for backwards compatibility only
175             sub _getopt_full_usage
176             {
177 9     9   163 my ($self, $usage) = @_;
178 9         28 print $usage->text;
179             }
180             #(this is already documented in MooseX::Getopt. But FIXME later, via RT#82195)
181             #pod =for Pod::Coverage
182             #pod print_usage_text
183             #pod =cut
184 9     9 0 268 sub print_usage_text { shift->_getopt_full_usage(@_) }
185              
186             sub _usage_format {
187 94     94   523 return "usage: %c %o";
188             }
189              
190             sub _traditional_spec {
191 18     18   55 my ( $class, %params ) = @_;
192              
193 18         35 my ( @options, %name_to_init_arg );
194              
195 18         27 foreach my $opt ( @{ $params{options} } ) {
  18         67  
196 109         251 push @options, $opt->{opt_string};
197              
198 109         173 my $identifier = $opt->{name};
199 109         196 $identifier =~ s/\W/_/g; # Getopt::Long does this to all option names
200              
201 109         280 $name_to_init_arg{$identifier} = $opt->{init_arg};
202             }
203              
204 18         94 return ( \@options, \%name_to_init_arg );
205             }
206              
207             sub _compute_getopt_attrs {
208 112     112   238 my $class = shift;
209 1099         18132 sort { $a->insertion_order <=> $b->insertion_order }
210             grep {
211 635 100       83983 $_->does("MooseX::Getopt::Meta::Attribute::Trait")
212             or
213             $_->init_arg !~ /^_/
214             } grep {
215 636         24716 defined $_->init_arg
216             } grep {
217 112         501 !$_->does('MooseX::Getopt::Meta::Attribute::Trait::NoGetopt')
  959         205353  
218             } $class->meta->get_all_attributes
219             }
220              
221             sub _get_cmd_flags_for_attr {
222 581     581   1199 my ( $class, $attr ) = @_;
223              
224 581         1738 my $flag = $attr->init_arg;
225              
226 581         840 my @aliases;
227              
228 581 100       1875 if ($attr->does('MooseX::Getopt::Meta::Attribute::Trait')) {
229 321 100       100541 $flag = $attr->cmd_flag if $attr->has_cmd_flag;
230 321 100       13726 @aliases = @{ $attr->cmd_aliases } if $attr->has_cmd_aliases;
  213         7426  
231             }
232              
233 581         23819 return ( $flag, @aliases );
234             }
235              
236             sub _attrs_to_options {
237 112     112   280 my $class = shift;
238 112   100     607 my $config_from_file = shift || {};
239              
240 112         225 my @options;
241              
242 112         453 foreach my $attr ($class->_compute_getopt_attrs) {
243 581         4400 my ( $flag, @aliases ) = $class->_get_cmd_flags_for_attr($attr);
244              
245 581         1649 my $opt_string = join(q{|}, $flag, @aliases);
246              
247 581 100       28322 if ($attr->name eq 'configfile') {
    100          
248 13         22 $opt_string .= '=s';
249             }
250             elsif ($attr->has_type_constraint) {
251 473         19897 my $type = $attr->type_constraint;
252 473 50       6173 if (MooseX::Getopt::OptionTypeMap->has_option_type($type)) {
253 473         5466 $opt_string .= MooseX::Getopt::OptionTypeMap->get_option_type($type)
254             }
255             }
256              
257             my $push = {
258             name => $flag,
259             init_arg => $attr->init_arg,
260             opt_string => $opt_string,
261 581   66     25728 required => $attr->is_required && !$attr->has_default && !$attr->has_builder && !exists $config_from_file->{$attr->name},
262             # NOTE:
263             # this "feature" was breaking because
264             # Getopt::Long::Descriptive would return
265             # the default value as if it was a command
266             # line flag, which would then override the
267             # one passed into a constructor.
268             # See 100_gld_default_bug.t for an example
269             # - SL
270             #( ( $attr->has_default && ( $attr->is_default_a_coderef xor $attr->is_lazy ) ) ? ( default => $attr->default({}) ) : () ),
271             };
272 581 100       30673 if ($attr->has_documentation) {
273 104         4120 my $doc = $attr->documentation;
274 104         860 $doc =~ s/[\r\n]+/ /g;
275 104         292 $push->{doc} = $doc;
276             }
277 581         3616 push @options, $push;
278             }
279              
280 112         1068 return @options;
281             }
282              
283             1;
284              
285             __END__
286              
287             =pod
288              
289             =encoding UTF-8
290              
291             =head1 NAME
292              
293             MooseX::Getopt::Basic - MooseX::Getopt::Basic - role to implement the Getopt::Long functionality
294              
295             =head1 VERSION
296              
297             version 0.78
298              
299             =head1 SYNOPSIS
300              
301             ## In your class
302             package My::App;
303             use Moose;
304              
305             with 'MooseX::Getopt::Basic';
306              
307             has 'out' => (is => 'rw', isa => 'Str', required => 1);
308             has 'in' => (is => 'rw', isa => 'Str', required => 1);
309              
310             # ... rest of the class here
311              
312             ## in your script
313             #!/usr/bin/perl
314              
315             use My::App;
316              
317             my $app = My::App->new_with_options();
318             # ... rest of the script here
319              
320             ## on the command line
321             % perl my_app_script.pl --in file.input --out file.dump
322              
323             =head1 DESCRIPTION
324              
325             This is like L<MooseX::Getopt> and can be used instead except that it
326             doesn't make use of L<Getopt::Long::Descriptive> (or "GLD" for short).
327              
328             =head1 METHODS
329              
330             =head2 new_with_options
331              
332             See L<MooseX::Getopt/new_with_options>.
333              
334             =head2 process_argv
335              
336             See L<MooseX::Getopt/process_argv>.
337              
338             =for Pod::Coverage print_usage_text
339              
340             =head1 SUPPORT
341              
342             Bugs may be submitted through L<the RT bug tracker|https://rt.cpan.org/Public/Dist/Display.html?Name=MooseX-Getopt>
343             (or L<bug-MooseX-Getopt@rt.cpan.org|mailto:bug-MooseX-Getopt@rt.cpan.org>).
344              
345             There is also a mailing list available for users of this distribution, at
346             L<http://lists.perl.org/list/moose.html>.
347              
348             There is also an irc channel available for users of this distribution, at
349             L<C<#moose> on C<irc.perl.org>|irc://irc.perl.org/#moose>.
350              
351             =head1 AUTHOR
352              
353             Stevan Little <stevan@iinteractive.com>
354              
355             =head1 COPYRIGHT AND LICENSE
356              
357             This software is copyright (c) 2007 by Infinity Interactive, Inc.
358              
359             This is free software; you can redistribute it and/or modify it under
360             the same terms as the Perl 5 programming language system itself.
361              
362             =cut