File Coverage

blib/lib/MooX/Options.pm
Criterion Covered Total %
statement 85 103 82.5
branch 32 44 72.7
condition 12 21 57.1
subroutine 12 12 100.0
pod n/a
total 141 180 78.3


line stmt bran cond sub pod time code
1             package MooX::Options;
2              
3 30     30   1599236 use strictures 2;
  30         26876  
  30         1392  
4              
5             our $VERSION = "4.102";
6              
7 30     30   3806 use Carp ('croak');
  30         60  
  30         1412  
8 24     24   125 use Module::Runtime qw(use_module);
  24         46  
  24         252  
9              
10             my @OPTIONS_ATTRIBUTES
11             = qw/format short repeatable negatable autosplit autorange doc long_doc order json hidden spacer_before spacer_after/;
12              
13             sub import {
14 68     68   23495 my ( undef, @import ) = @_;
15 68         506 my $options_config = {
16             protect_argv => 1,
17             flavour => [],
18             skip_options => [],
19             prefer_commandline => 0,
20             with_config_from_file => 0,
21             with_locale_textdomain_oo => 0,
22             usage_string => undef,
23              
24             #long description (manual)
25             description => undef,
26             authors => [],
27             synopsis => undef,
28             spacer => " ",
29             @import
30             };
31              
32 68         283 my $target = caller;
33 68         168 for my $needed_methods (qw/with around has/) {
34 197 100       922 next if $target->can($needed_methods);
35 5         373 croak( "Can't find the method <$needed_methods> in <$target>!\n"
36             . "Ensure to load a Role::Tiny compatible module like Moo or Moose before using MooX::Options."
37             );
38             }
39              
40 65         195 my $with = $target->can('with');
41 65         243 my $around = $target->can('around');
42 65         289 my $has = $target->can('has');
43              
44 65         101 my @target_isa;
45 24     24   3588 { no strict 'refs'; @target_isa = @{"${target}::ISA"} };
  24         50  
  24         10121  
  65         84  
  65         242  
  65         269  
46              
47 65 100       161 if (@target_isa) { #only in the main class, not a role
48              
49             ## no critic (ProhibitStringyEval, ErrorHandling::RequireCheckingReturnValueOfEval, ValuesAndExpressions::ProhibitImplicitNewlines)
50 60         213 eval "#line ${\(__LINE__+1 . ' ' . __FILE__)}\n" . '{
  60         2258  
51             package ' . $target . ';
52             use MRO::Compat ();
53 24     24   6130  
  24         27552  
  24         1593  
54             sub _options_data {
55             my ( $class, @meta ) = @_;
56             return $class->maybe::next::method(@meta);
57             }
58              
59             sub _options_config {
60             my ( $class, @params ) = @_;
61             return $class->maybe::next::method(@params);
62             }
63              
64             1;
65             }';
66              
67 60 100       309 croak($@) if $@;
68              
69             $around->(
70             _options_config => sub {
71 105     107   1999 my ( $orig, $self ) = ( shift, shift );
72 105         252 return $self->$orig(@_), %$options_config;
73             }
74 59         443 );
75              
76             ## use critic
77             }
78             else {
79 5 50       18 if ( $options_config->{with_config_from_file} ) {
80 0         0 croak(
81             "Please, don't use the option into a role."
82             );
83             }
84             }
85              
86 62         45512 my $options_data = {};
87 62 50       216 if ( $options_config->{with_config_from_file} ) {
88             $options_data->{config_prefix} = {
89 0         0 format => 's',
90             doc => 'config prefix',
91             order => 0,
92             };
93             $options_data->{config_files} = {
94 0         0 format => 's@',
95             doc => 'config files',
96             order => 0,
97             };
98             }
99              
100             my $apply_modifiers = sub {
101 107 100   109   571 return if $target->can('new_with_options');
102 63         194 $with->('MooX::Options::Role');
103 63 50       37786 if ( $options_config->{with_config_from_file} ) {
104 0         0 $with->('MooX::ConfigFromFile::Role');
105             }
106 63 50       162 if ( $options_config->{with_locale_textdomain_oo} ) {
107 0         0 $with->('MooX::Locale::TextDomain::OO');
108 0         0 use_module("MooX::Options::Descriptive::Usage");
109 0 0       0 MooX::Options::Descriptive::Usage->can("localizer")
110             or MooX::Options::Descriptive::Usage->can("with")
111             ->("MooX::Locale::TextDomain::OO");
112             }
113              
114             $around->(
115             _options_data => sub {
116 121         2265 my ( $orig, $self ) = ( shift, shift );
117 121         550 return ( $self->$orig(@_), %$options_data );
118             }
119 63         311 );
120 62         236 };
121              
122             my @banish_keywords
123 62         210 = qw/h help man usage option new_with_options parse_options options_usage _options_data _options_config/;
124 62 50       146 if ( $options_config->{with_config_from_file} ) {
125 0         0 push @banish_keywords, qw/config_files config_prefix config_dirs/;
126             }
127              
128             my $option = sub {
129 56     58   12399 my ( $name, %attributes ) = @_;
130 56         128 for my $ban (@banish_keywords) {
131 524 100       1703 croak(
132             "You cannot use an option with the name '$ban', it is implied by MooX::Options"
133             ) if $name eq $ban;
134             }
135              
136 47         159 my %_moo_attrs = _filter_attributes(%attributes);
137 47 100       246 $has->( $name => %_moo_attrs ) if %_moo_attrs;
138              
139             ## no critic (RegularExpressions::RequireExtendedFormatting)
140 47         20333 $name =~ s/^\+//; # one enhances an attribute being an option
141 47         145 $options_data->{$name}
142             = { _validate_and_filter_options(%attributes) };
143              
144 45         146 $apply_modifiers->();
145 45         275 return;
146 62         220 };
147              
148 62 100       161 if ( my $info = $Role::Tiny::INFO{$target} ) {
149 5         19 $info->{not_methods}{$option} = $option;
150             }
151              
152 24     24   148 { no strict 'refs'; *{"${target}::option"} = $option; }
  24         56  
  24         8530  
  62         86  
  62         79  
  62         247  
153              
154 62         148 $apply_modifiers->();
155              
156 62         43712 return;
157             }
158              
159             my %filter_key = map { $_ => 1 } ( @OPTIONS_ATTRIBUTES, 'negativable' );
160              
161             sub _filter_attributes {
162 47     49   117 my %attributes = @_;
163 66         188 return map { ( $_ => $attributes{$_} ) }
164 47         106 grep { !exists $filter_key{$_} } keys %attributes;
  103         235  
165             }
166              
167             sub _validate_and_filter_options {
168 47     49   125 my (%options) = @_;
169 47 100       156 $options{doc} = $options{documentation} if !defined $options{doc};
170 47 100       129 $options{order} = 0 if !defined $options{order};
171              
172 47 50 66     223 if ( $options{json}
      33        
173             || ( defined $options{format} && $options{format} eq 'json' ) )
174             {
175 0         0 delete $options{repeatable};
176 0         0 delete $options{autosplit};
177 0         0 delete $options{autorange};
178 0         0 delete $options{negativable};
179 0         0 delete $options{negatable};
180 0         0 $options{json} = 1;
181 0         0 $options{format} = 's';
182             }
183              
184 47 50 33     125 if ( $options{autorange} and not defined $options{autosplit} ) {
185              
186             # XXX maybe we should warn here since a previously beloved feature isn't enabled automatically
187 0         0 eval { use_module("Data::Record"); use_module("Regexp::Common"); }
  0         0  
188 0 0       0 and $options{autosplit} = ',';
189             }
190              
191             exists $options{negativable}
192 47 100       96 and $options{negatable} = delete $options{negativable};
193              
194 118         267 my %cmdline_options = map { ( $_ => $options{$_} ) }
195 47         120 grep { exists $options{$_} } @OPTIONS_ATTRIBUTES, 'required';
  658         1000  
196              
197             $cmdline_options{repeatable} = 1
198 47 100 66     209 if $cmdline_options{autosplit} or $cmdline_options{autorange};
199             $cmdline_options{format} .= "@"
200             if $cmdline_options{repeatable}
201             && defined $cmdline_options{format}
202 47 50 66     169 && substr( $cmdline_options{format}, -1 ) ne '@';
      66        
203              
204             croak(
205             "Negatable params is not usable with non boolean value, don't pass format to use it !"
206             )
207             if ( $cmdline_options{negatable} )
208 47 100 66     368 and defined $cmdline_options{format};
209              
210 45         193 return %cmdline_options;
211             }
212              
213             1;
214              
215             __END__