File Coverage

blib/lib/MooX/Options/Role.pm
Criterion Covered Total %
statement 243 259 93.8
branch 138 156 87.8
condition 33 42 78.5
subroutine 19 21 90.4
pod 6 6 100.0
total 439 484 90.5


line stmt bran cond sub pod time code
1             package MooX::Options::Role;
2              
3 23     23   145110 use strictures 2;
  23         171  
  23         1124  
4              
5             ## no critic (ProhibitExcessComplexity)
6              
7             our $VERSION = "4.103";
8              
9             =head1 NAME
10              
11             MooX::Options::Role - role that is apply to your object
12              
13             =head1 USAGE
14              
15             Don't use MooX::Options::Role directly. It is used by L to upgrade your module. But it is useless alone.
16              
17             =cut
18              
19 23     23   4963 use Carp qw/croak/;
  23         50  
  23         1143  
20 23     23   139 use Module::Runtime qw(use_module);
  23         49  
  23         136  
21 23     23   6721 use MooX::Options::Descriptive;
  23         72  
  23         269  
22 23     23   9650 use Scalar::Util qw/blessed/;
  23         51  
  23         27971  
23              
24             ### PRIVATE
25              
26             sub _option_name {
27 366     366   931 my ( $name, %data ) = @_;
28 366         900 my $cmdline_name = join( '|', grep {defined} ( $name, $data{short} ) );
  732         1656  
29             ## no critic (RegularExpressions::RequireExtendedFormatting)
30 366 100       1159 $cmdline_name =~ m/[^\w]$/
31             and croak
32             "cmdline argument '$cmdline_name' should end with a word character";
33 365 100 100     1125 $cmdline_name .= '+' if $data{repeatable} && !defined $data{format};
34 365 50       679 $cmdline_name .= '!' if $data{negativable};
35 365 100       700 $cmdline_name .= '!' if $data{negatable};
36 365 100       811 $cmdline_name .= '=' . $data{format} if defined $data{format};
37 365         1167 return $cmdline_name;
38             }
39              
40             sub _options_prepare_descriptive {
41 156     156   311 my ($options_data) = @_;
42              
43 156         352 my @options;
44             my %all_options;
45 156         0 my %has_to_split;
46              
47 156         265 my $data_record_loaded = 0;
48 156         653 for my $name (
49             sort {
50             $options_data->{$a}{order}
51             <=> $options_data->{$b}{order} # sort by order
52 406 50       921 or $a cmp $b # sort by attr name
53             } keys %$options_data
54             )
55             {
56 366         7065 my %data = %{ $options_data->{$name} };
  366         1336  
57 366         683 my $doc = $data{doc};
58 366 100       867 $doc = "no doc for $name" if !defined $doc;
59 366         553 my $option = {};
60 366 100       733 $option->{hidden} = 1 if $data{hidden};
61              
62 366 100       681 push @options, [] if $data{spacer_before};
63 366         934 push @options, [ _option_name( $name, %data ), $doc, $option ];
64 365 100       759 push @options, [] if $data{spacer_after};
65              
66 365         492 push @{ $all_options{$name} }, $name;
  365         771  
67 365 100       732 if ( $data{short} ) {
68             ## no critic (RegularExpressions::RequireExtendedFormatting)
69 63         187 my @shrt_list = split( m/\|/, $data{short} );
70 63         131 foreach my $shrt (@shrt_list) {
71             croak
72             "There is already an option '$shrt' - can't use it to shorten '$name'"
73 72 100       267 if exists $options_data->{$shrt};
74             croak
75             "There is already an abbreviation '$shrt' - can't use it to shorten '$name'"
76 71 50       118 if defined $all_options{$shrt};
77 71         82 push @{ $all_options{$shrt} }, $name;
  71         221  
78             }
79             }
80              
81 364 100       822 if ( defined $data{autosplit} ) {
82 154 100       290 if ( !$data_record_loaded ) {
83 65         229 use_module("Data::Record");
84 65         2025 use_module("Regexp::Common");
85 65         1529 Regexp::Common->import;
86 65         424286 $data_record_loaded = 1;
87             }
88             $has_to_split{$name} = Data::Record->new(
89             { split => $data{autosplit},
90             unless => $Regexp::Common::RE{quoted}
91             }
92 154         647 );
93             }
94             }
95              
96             # singleton algorithm taken from List::MoreUtils
97 154         3460 my $k;
98             my %abbrev_dd;
99             ## no critic (BuiltinFunctions::ProhibitComplexMappings)
100 154         449 foreach my $combo (
101 2289         3807 grep { 1 == $abbrev_dd{ $k = $_->[1] } }
102 3364         6346 grep { not $abbrev_dd{ $k = $_->[1] }++ }
103             map {
104 434         597 my $fa = $_;
105 434         709 map { [ $fa => substr $fa, 0, $_ ] } 1 .. length($fa)
  3364         6257  
106             } keys %all_options
107             )
108             {
109 1496         1662 my ( $name, $long_short ) = @{$combo};
  1496         2088  
110 1496 100       2540 $all_options{$name}->[0] eq $name
111             or next; # don't generate abbreviations for short
112 1382 100       2168 defined $all_options{$long_short} and next;
113 1022         1102 push @{ $all_options{$long_short} }, $name;
  1022         2085  
114             }
115              
116 154         1090 return \@options, \%has_to_split, \%all_options;
117             }
118              
119             sub _options_fix_argv {
120 154     154   326 my ( $option_data, $has_to_split, $all_options ) = @_;
121              
122 154         212 my @new_argv;
123              
124             #parse all argv
125 154         470 while ( defined( my $arg = shift @ARGV ) ) {
126 179 100       396 if ( $arg eq '--' ) {
127 1         3 push @new_argv, $arg, @ARGV;
128 1         2 last;
129             }
130 178 100       436 if ( index( $arg, '-' ) != 0 ) {
131 10         21 push @new_argv, $arg;
132 10         26 next;
133             }
134              
135 168         493 my ( $arg_name_with_dash, $arg_values ) = split( /=/x, $arg, 2 );
136 168 100 100     561 if ( index( $arg_name_with_dash, '--' ) < 0 && !defined $arg_values )
137             {
138 9 100       34 $arg_values
139             = length($arg_name_with_dash) > 2
140             ? substr( $arg_name_with_dash, 2 )
141             : undef;
142 9         27 $arg_name_with_dash = substr( $arg_name_with_dash, 0, 2 );
143             }
144 168 100       389 unshift @ARGV, $arg_values if defined $arg_values;
145              
146 168         906 my ( $dash, $negative, $arg_name_without_dash )
147             = $arg_name_with_dash =~ /^(\-+)(no\-)?(.*)$/x;
148 168         411 $arg_name_without_dash =~ s/\-/_/gx;
149              
150 168         305 my $original_long_option = $all_options->{$arg_name_without_dash};
151 168 100       328 if ( defined $original_long_option ) {
152             ## no critic (ErrorHandling::RequireCarping)
153             # uncoverable branch false
154 150 50       314 @$original_long_option == 1
155             or die
156             "Internal error, duplicate map for abbreviation detected for '$arg_name_without_dash'!";
157 150         270 $original_long_option = $original_long_option->[0];
158             }
159              
160 168         266 my $arg_name = $dash;
161              
162 168 100 66     425 if ( defined $negative && defined $original_long_option ) {
163             $arg_name .=
164             $option_data->{$original_long_option}{negatable}
165 2 50       9 ? 'no-'
166             : 'no_';
167             }
168              
169 168         253 $arg_name .= $arg_name_without_dash;
170              
171 168 100 100     683 if ( defined $original_long_option
172             && ( defined( my $arg_value = shift @ARGV ) ) )
173             {
174 144         272 my $autorange = $option_data->{$original_long_option}{autorange};
175             my $argv_processor = sub {
176              
177             #remove the quoted if exist to chain
178 185     185   535 $_[0] =~ s/^['"]|['"]$//gx;
179 185 100       349 if ($autorange) {
180             push @new_argv,
181 85         162 map { $arg_name => $_ } _expand_autorange( $_[0] );
  134         671  
182             }
183             else {
184 100         556 push @new_argv, $arg_name, $_[0];
185             }
186              
187 144         560 };
188              
189 144 100       332 if ( my $rec = $has_to_split->{$original_long_option} ) {
190 103         300 foreach my $record ( $rec->records($arg_value) ) {
191 144         18083 $argv_processor->($record);
192             }
193             }
194             else {
195 41         76 $argv_processor->($arg_value);
196             }
197             }
198             else {
199 24         92 push @new_argv, $arg_name;
200             }
201             }
202              
203 154         469 return @new_argv;
204             }
205              
206             sub _expand_autorange {
207 85     85   153 my ($arg_value) = @_;
208              
209 85         101 my @expanded_arg_value;
210 85         242 my ( $left_figure, $autorange_found, $right_figure )
211             = $arg_value =~ /^(\d*)(\.\.)(\d*)$/x;
212 85 100       166 if ($autorange_found) {
213 32 100       74 $left_figure = $right_figure unless length($left_figure);
214 32 100       64 $right_figure = $left_figure unless length($right_figure);
215 32 100 66     122 if ( length $left_figure && length $right_figure ) {
216 30         114 push @expanded_arg_value, $left_figure .. $right_figure;
217             }
218             }
219 85 100       260 return @expanded_arg_value ? @expanded_arg_value : $arg_value;
220             }
221              
222             ### PRIVATE
223              
224 23     23   194 use Moo::Role;
  23         55  
  23         189  
225             with "MooX::Locale::Passthrough";
226              
227             requires qw/_options_data _options_config/;
228              
229             =head1 METHODS
230              
231             These methods will be composed into your class
232              
233             =head2 new_with_options
234              
235             Same as new but parse ARGV with L
236              
237             Check full doc L for more details.
238              
239             =cut
240              
241             sub new_with_options {
242 129     129 1 238361 my ( $class, %params ) = @_;
243              
244             #save subcommand
245              
246 129 50       513 if ( ref( my $command_chain = $params{command_chain} ) eq 'ARRAY' ) {
247             $class->can('around')->(
248             _options_prog_name => sub {
249 0     0   0 my $prog_name = Getopt::Long::Descriptive::prog_name;
250 0         0 for my $cmd (@$command_chain) {
251 0 0 0     0 next if !blessed $cmd || !$cmd->can('command_name');
252 0 0       0 if ( defined( my $cmd_name = $cmd->command_name ) ) {
253 0         0 $prog_name .= ' ' . $cmd_name;
254             }
255             }
256              
257 0         0 return $prog_name;
258             }
259 0         0 );
260             }
261              
262 129 50       334 if ( ref( my $command_commands = $params{command_commands} ) eq 'HASH' ) {
263             $class->can('around')->(
264             _options_sub_commands => sub {
265             return [
266             ## no critic (BuiltinFunctions::RequireBlockMap)
267             map +{
268             name => $_,
269 0     0   0 command => $command_commands->{$_},
270             },
271             sort keys %$command_commands
272             ];
273             }
274 0         0 );
275             }
276              
277 129         463 my %cmdline_params = $class->parse_options(%params);
278              
279 115 100       328 if ( $cmdline_params{h} ) {
280 1         24 return $class->options_usage( $params{h}, $cmdline_params{h} );
281             }
282 114 100       258 if ( $cmdline_params{help} ) {
283 5         58 return $class->options_help( $params{help}, $cmdline_params{help} );
284             }
285 109 100       231 if ( $cmdline_params{man} ) {
286 1         12 return $class->options_man( $cmdline_params{man} );
287             }
288 108 100       237 if ( $cmdline_params{usage} ) {
289             return $class->options_short_usage( $params{usage},
290 1         6 $cmdline_params{usage} );
291             }
292              
293 107         159 my $self;
294             return $self
295 107 100       159 if eval { $self = $class->new(%cmdline_params); 1 };
  107         1652  
  103         36732  
296 4 50       4533 if ( $@ =~ /^Attribute\s\((.*?)\)\sis\srequired/x ) {
    100          
    50          
    50          
297 0         0 print STDERR "$1 is missing\n";
298             }
299             elsif ( $@ =~ /^Missing\srequired\sarguments:\s(.*)\sat\s/x ) {
300 2         10 my @missing_required = split /,\s/x, $1;
301             print STDERR
302             join( "\n",
303 2         5 ( map { $_ . " is missing" } @missing_required ), '' );
  4         100  
304             }
305             elsif ( $@ =~ /^(.*?)\srequired/x ) {
306 0         0 print STDERR "$1 is missing\n";
307             }
308             elsif ( $@ =~ /^isa\scheck.*?failed:\s/x ) {
309 2         117 print STDERR substr( $@, index( $@, ':' ) + 2 );
310             }
311             else {
312 0         0 print STDERR $@;
313             }
314 4         21 %cmdline_params = $class->parse_options( h => 1 );
315 4         16 return $class->options_usage( 1, $cmdline_params{h} );
316             }
317              
318             =head2 parse_options
319              
320             Parse your options, call L and convert the result for the "new" method.
321              
322             It is use by "new_with_options".
323              
324             =cut
325              
326             my $decode_json;
327              
328             sub parse_options {
329 156     156 1 385 my ( $class, %params ) = @_;
330              
331 156         4072 my %options_data = $class->_options_data;
332 156         5191 my %options_config = $class->_options_config;
333 156 100       2657 if ( defined $options_config{skip_options} ) {
334 153         219 delete @options_data{ @{ $options_config{skip_options} } };
  153         380  
335             }
336              
337 156         476 my ( $options, $has_to_split, $all_options )
338             = _options_prepare_descriptive( \%options_data );
339              
340 154 100       587 local @ARGV = @ARGV if $options_config{protect_argv};
341 154         429 @ARGV = _options_fix_argv( \%options_data, $has_to_split, $all_options );
342              
343 154         262 my @flavour;
344 154 100       419 if ( defined $options_config{flavour} ) {
345 151         493 push @flavour, { getopt_conf => $options_config{flavour} };
346             }
347              
348 154         483 my $prog_name = $class->_options_prog_name();
349              
350             # create usage str
351 154         687 my $usage_str = $options_config{usage_string};
352 154 100       716 $usage_str = sprintf( $class->__("USAGE: %s %s"),
353             $prog_name, " [-h] [" . $class->__("long options ...") . "]" )
354             if !defined $usage_str;
355              
356 154         1477 my ( $opt, $usage ) = describe_options(
357             ($usage_str),
358             @$options,
359             [],
360             [ 'usage', $class->__("show a short help message") ],
361             [ 'h', $class->__("show a compact help message") ],
362             [ 'help', $class->__("show a long help message") ],
363             [ 'man', $class->__("show the manual") ],
364             ,
365             @flavour
366             );
367              
368 154         192643 $usage->{prog_name} = $prog_name;
369 154         364 $usage->{target} = $class;
370              
371 154 100       433 if ( $usage->{should_die} ) {
372 10         50 return $class->options_usage( 1, $usage );
373             }
374              
375 144         338 my %cmdline_params = %params;
376 144         343 for my $name ( keys %options_data ) {
377 343         410 my %data = %{ $options_data{$name} };
  343         1084  
378 343 100 100     915 if ( !defined $cmdline_params{$name}
379             || $options_config{prefer_commandline} )
380             {
381 340         1010 my $val = $opt->$name();
382 340 100       1653 if ( defined $val ) {
383 95 100       250 if ( $data{json} ) {
384             defined $decode_json
385 5 100       13 or $decode_json = eval {
386 1         6 use_module("JSON::MaybeXS");
387 1         57 JSON::MaybeXS->can("decode_json");
388             };
389             defined $decode_json
390 5 50       14 or $decode_json = eval {
391 0         0 use_module("JSON::PP");
392 0         0 JSON::PP->can("decode_json");
393             };
394             ## no critic (ErrorHandling::RequireCarping)
395 5 50       10 $@ and die $@;
396 5 100       11 if (!eval {
397 5         45 $cmdline_params{$name} = $decode_json->($val);
398 3         15 1;
399             }
400             )
401             {
402 2         107 print STDERR $@;
403 2         11 return $class->options_usage( 1, $usage );
404             }
405             }
406             else {
407 90         239 $cmdline_params{$name} = $val;
408             }
409             }
410             }
411             }
412              
413 142 100 66     367 if ( $opt->h() || defined $params{h} ) {
414 5         30 $cmdline_params{h} = $usage;
415             }
416              
417 142 100 100     1376 if ( $opt->help() || defined $params{help} ) {
418 26         153 $cmdline_params{help} = $usage;
419             }
420              
421 142 100 100     779 if ( $opt->man() || defined $params{man} ) {
422 3         19 $cmdline_params{man} = $usage;
423             }
424              
425 142 100 66     869 if ( $opt->usage() || defined $params{usage} ) {
426 1         5 $cmdline_params{usage} = $usage;
427             }
428              
429 142         2611 return %cmdline_params;
430             }
431              
432             =head2 options_usage
433              
434             Display help message.
435              
436             Check full doc L for more details.
437              
438             =cut
439              
440             sub options_usage {
441 32     32 1 19482 my ( $class, $code, @messages ) = @_;
442 32         60 my $usage;
443 32 100 100     173 if ( @messages
444             && ref $messages[-1] eq 'MooX::Options::Descriptive::Usage' )
445             {
446 17         62 $usage = shift @messages;
447             }
448 32 100       112 $code = 0 if !defined $code;
449 32 100       127 if ( !$usage ) {
450 15         32 local @ARGV = ();
451 15         51 my %cmdline_params = $class->parse_options( help => $code );
452 15         48 $usage = $cmdline_params{help};
453             }
454 32         94 my $message = "";
455 32 100       126 $message .= join( "\n", @messages, '' ) if @messages;
456 32         186 $message .= $usage . "\n";
457 32 100       177 if ( $code > 0 ) {
458 18         261 CORE::warn $message;
459             }
460             else {
461 14         791 print $message;
462             }
463 32 100       1078 exit($code) if $code >= 0;
464 1         5 return;
465             }
466              
467             =head2 options_help
468              
469             Display long usage message
470              
471             =cut
472              
473             sub options_help {
474 10     10 1 14056 my ( $class, $code, $usage ) = @_;
475 10 100       42 $code = 0 if !defined $code;
476              
477 10 100 66     62 if ( !defined $usage || !ref $usage ) {
478 5         14 local @ARGV = ();
479 5         18 my %cmdline_params = $class->parse_options( help => $code );
480 5         14 $usage = $cmdline_params{help};
481             }
482 10         88 my $message = $usage->option_help . "\n";
483 10 50       44 if ( $code > 0 ) {
484 0         0 CORE::warn $message;
485             }
486             else {
487 10         532 print $message;
488             }
489 10 100       88 exit($code) if $code >= 0;
490 1         3 return;
491             }
492              
493             =head2 options_short_usage
494              
495             Display quick usage message, with only the list of options
496              
497             =cut
498              
499             sub options_short_usage {
500 2     2 1 1257 my ( $class, $code, $usage ) = @_;
501 2 100       8 $code = 0 if !defined $code;
502              
503 2 100 66     11 if ( !defined $usage || !ref $usage ) {
504 1         2 local @ARGV = ();
505 1         8 my %cmdline_params = $class->parse_options( help => $code );
506 1         4 $usage = $cmdline_params{help};
507             }
508 2         11 my $message = "USAGE: " . $usage->option_short_usage . "\n";
509 2 100       7 if ( $code > 0 ) {
510 1         12 CORE::warn $message;
511             }
512             else {
513 1         32 print $message;
514             }
515 2 50       79 exit($code) if $code >= 0;
516 0         0 return;
517             }
518              
519             =head2 options_man
520              
521             Display a pod like a manual
522              
523             =cut
524              
525             sub options_man {
526 3     3 1 6931 my ( $class, $usage, $output ) = @_;
527 3         11 local @ARGV = ();
528 3 100       10 if ( !$usage ) {
529 2         7 local @ARGV = ();
530 2         11 my %cmdline_params = $class->parse_options( man => 1 );
531 2         5 $usage = $cmdline_params{man};
532             }
533              
534 3         18 use_module( "Path::Class", "0.32" );
535 3         13452 my $man_file
536             = Path::Class::file( Path::Class::tempdir( CLEANUP => 1 ),
537             'help.pod' );
538 3         1378 $man_file->spew( iomode => '>:encoding(UTF-8)', $usage->option_pod );
539              
540 3         1581 use_module("Pod::Usage");
541 3         27195 Pod::Usage::pod2usage(
542             -verbose => 2,
543             -input => $man_file->stringify,
544             -exitval => 'NOEXIT',
545             -output => $output
546             );
547              
548 3         140761 exit(0);
549             }
550              
551             ### PRIVATE NEED TO BE EXPORTED
552              
553             sub _options_prog_name {
554 154     154   622 return Getopt::Long::Descriptive::prog_name;
555             }
556              
557             sub _options_sub_commands {
558 97     97   435 return;
559             }
560              
561             ### PRIVATE NEED TO BE EXPORTED
562              
563             =head1 SUPPORT
564              
565             You can find documentation for this module with the perldoc command.
566              
567             perldoc MooX::ConfigFromFile
568              
569             You can also look for information at:
570              
571             =over 4
572              
573             =item * RT: CPAN's request tracker (report bugs here)
574              
575             L
576              
577             =item * AnnoCPAN: Annotated CPAN documentation
578              
579             L
580              
581             =item * CPAN Ratings
582              
583             L
584              
585             =item * Search CPAN
586              
587             L
588              
589             =back
590              
591             =head1 AUTHOR
592              
593             celogeek
594              
595             =head1 COPYRIGHT AND LICENSE
596              
597             This software is copyright (c) 2013 by celogeek .
598              
599             This software is copyright (c) 2017 by Jens Rehsack.
600              
601             This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
602              
603             =cut
604              
605             1;