File Coverage

blib/lib/MooX/Options.pm
Criterion Covered Total %
statement 95 103 92.2
branch 36 44 81.8
condition 20 21 95.2
subroutine 12 12 100.0
pod n/a
total 163 180 90.5


line stmt bran cond sub pod time code
1             package MooX::Options;
2              
3 32     32   1863182 use strictures 2;
  32         29856  
  32         1604  
4              
5             our $VERSION = "4.103";
6              
7 31     31   4527 use Carp ('croak');
  31         72  
  31         1625  
8 25     25   137 use Module::Runtime qw(use_module);
  25         49  
  25         292  
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 70     70   30990 my ( undef, @import ) = @_;
15 70         620 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 70         314 my $target = caller;
33 70         157 for my $needed_methods (qw/with around has/) {
34 203 100       1015 next if $target->can($needed_methods);
35 5         437 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 67         224 my $with = $target->can('with');
41 67         277 my $around = $target->can('around');
42 67         316 my $has = $target->can('has');
43              
44 67         117 my @target_isa;
45 25     25   4047 { no strict 'refs'; @target_isa = @{"${target}::ISA"} };
  25         57  
  25         11438  
  67         97  
  67         229  
  67         275  
46              
47 67 100       179 if (@target_isa) { #only in the main class, not a role
48              
49             ## no critic (ProhibitStringyEval, ErrorHandling::RequireCheckingReturnValueOfEval, ValuesAndExpressions::ProhibitImplicitNewlines)
50 62         227 eval "#line ${\(__LINE__+1 . ' ' . __FILE__)}\n" . '{
  62         2540  
51             package ' . $target . ';
52             use MRO::Compat ();
53 25     25   6667  
  25         30027  
  25         1760  
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 62 100       343 croak($@) if $@;
68              
69             $around->(
70             _options_config => sub {
71 253     255   5095 my ( $orig, $self ) = ( shift, shift );
72 253         539 return $self->$orig(@_), %$options_config;
73             }
74 61         518 );
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 64         50825 my $options_data = {};
87 64 50       244 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 143 100   145   815 return if $target->can('new_with_options');
102 65         213 $with->('MooX::Options::Role');
103 65 50       41545 if ( $options_config->{with_config_from_file} ) {
104 0         0 $with->('MooX::ConfigFromFile::Role');
105             }
106 65 50       186 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 271         5647 my ( $orig, $self ) = ( shift, shift );
117 271         976 return ( $self->$orig(@_), %$options_data );
118             }
119 65         414 );
120 64         279 };
121              
122             my @banish_keywords
123 64         315 = qw/h help man usage option new_with_options parse_options options_usage _options_data _options_config/;
124 64 50       176 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 90     92   12901 my ( $name, %attributes ) = @_;
130 90         223 for my $ban (@banish_keywords) {
131 864 100       2412 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 81         240 my %_moo_attrs = _filter_attributes(%attributes);
137 81 100       406 $has->( $name => %_moo_attrs ) if %_moo_attrs;
138              
139             ## no critic (RegularExpressions::RequireExtendedFormatting)
140 81         34272 $name =~ s/^\+//; # one enhances an attribute being an option
141 81         252 $options_data->{$name}
142             = { _validate_and_filter_options(%attributes) };
143              
144 79         250 $apply_modifiers->();
145 79         385 return;
146 64         273 };
147              
148 64 100       198 if ( my $info = $Role::Tiny::INFO{$target} ) {
149 5         19 $info->{not_methods}{$option} = $option;
150             }
151              
152 25     25   165 { no strict 'refs'; *{"${target}::option"} = $option; }
  25         53  
  25         8958  
  64         107  
  64         91  
  64         300  
153              
154 64         174 $apply_modifiers->();
155              
156 64         50799 return;
157             }
158              
159             my %filter_key = map { $_ => 1 } ( @OPTIONS_ATTRIBUTES, 'negativable' );
160              
161             sub _filter_attributes {
162 81     83   197 my %attributes = @_;
163 106         322 return map { ( $_ => $attributes{$_} ) }
164 81         174 grep { !exists $filter_key{$_} } keys %attributes;
  190         414  
165             }
166              
167             sub _validate_and_filter_options {
168 81     83   212 my (%options) = @_;
169 81 100       248 $options{doc} = $options{documentation} if !defined $options{doc};
170 81 100       208 $options{order} = 0 if !defined $options{order};
171              
172 81 100 100     380 if ( $options{json}
      100        
173             || ( defined $options{format} && $options{format} eq 'json' ) )
174             {
175 3         5 delete $options{repeatable};
176 3         5 delete $options{autosplit};
177 3         4 delete $options{autorange};
178 3         5 delete $options{negativable};
179 3         4 delete $options{negatable};
180 3         3 $options{json} = 1;
181 3         5 $options{format} = 's';
182             }
183              
184 81 100 100     212 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 5         20 eval { use_module("Data::Record"); use_module("Regexp::Common"); }
  5         141  
188 5 50       7 and $options{autosplit} = ',';
189             }
190              
191             exists $options{negativable}
192 81 100       263 and $options{negatable} = delete $options{negativable};
193              
194 244         525 my %cmdline_options = map { ( $_ => $options{$_} ) }
195 81         157 grep { exists $options{$_} } @OPTIONS_ATTRIBUTES, 'required';
  1134         1734  
196              
197             $cmdline_options{repeatable} = 1
198 81 100 66     344 if $cmdline_options{autosplit} or $cmdline_options{autorange};
199             $cmdline_options{format} .= "@"
200             if $cmdline_options{repeatable}
201             && defined $cmdline_options{format}
202 81 100 100     285 && substr( $cmdline_options{format}, -1 ) ne '@';
      100        
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 81 100 100     572 and defined $cmdline_options{format};
209              
210 79         379 return %cmdline_options;
211             }
212              
213             1;
214              
215             __END__