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 13 13 100.0
pod n/a
total 164 181 90.6


line stmt bran cond sub pod time code
1             package MooX::Options;
2              
3 30     30   2680827 use strictures 2;
  30         35041  
  30         1813  
4              
5             our $VERSION = "4.101";
6              
7 30     30   4951 use Carp ('croak');
  30         84  
  30         1981  
8 24     24   163 use Module::Runtime qw(use_module);
  24         61  
  24         355  
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 69     69   42608 my ( undef, @import ) = @_;
15 69         735 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 69         475 my $target = caller;
33 69         231 for my $needed_methods (qw/with around has/) {
34 200 100       1284 next if $target->can($needed_methods);
35 5         547 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 66         278 my $with = $target->can('with');
41 66         366 my $around = $target->can('around');
42 66         459 my $has = $target->can('has');
43              
44 66         149 my @target_isa;
45 24     24   4577 { no strict 'refs'; @target_isa = @{"${target}::ISA"} };
  24         70  
  24         13549  
  66         117  
  66         291  
  66         344  
46              
47 66 100       219 if (@target_isa) { #only in the main class, not a role
48              
49             ## no critic (ProhibitStringyEval, ErrorHandling::RequireCheckingReturnValueOfEval, ValuesAndExpressions::ProhibitImplicitNewlines)
50 61         274 eval "#line ${\(__LINE__+1 . ' ' . __FILE__)}\n" . '{
  61         3049  
51             package ' . $target . ';
52             use MRO::Compat ();
53 24     24   11413  
  24     1   35025  
  24         1980  
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 61 100       458 croak($@) if $@;
68              
69             $around->(
70             _options_config => sub {
71 248     249   7134 my ( $orig, $self ) = ( shift, shift );
72 248         812 return $self->$orig(@_), %$options_config;
73             }
74 60         691 );
75              
76             ## use critic
77             }
78             else {
79 6 50       142 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 63         63096 my $options_data = {};
87 63 50       320 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 139 100   141   1018 return if $target->can('new_with_options');
102 64         292 $with->('MooX::Options::Role');
103 64 50       54355 if ( $options_config->{with_config_from_file} ) {
104 0         0 $with->('MooX::ConfigFromFile::Role');
105             }
106 64 50       249 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 265         8096 my ( $orig, $self ) = ( shift, shift );
117 265         1426 return ( $self->$orig(@_), %$options_data );
118             }
119 64         448 );
120 63         379 };
121              
122             my @banish_keywords
123 63         328 = qw/h help man usage option new_with_options parse_options options_usage _options_data _options_config/;
124 63 50       249 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 87     89   16105 my ( $name, %attributes ) = @_;
130 87         273 for my $ban (@banish_keywords) {
131 834 100       4146 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 78         373 my %_moo_attrs = _filter_attributes(%attributes);
137 78 100       637 $has->( $name => %_moo_attrs ) if %_moo_attrs;
138              
139             ## no critic (RegularExpressions::RequireExtendedFormatting)
140 78         48670 $name =~ s/^\+//; # one enhances an attribute being an option
141 78         436 $options_data->{$name}
142             = { _validate_and_filter_options(%attributes) };
143              
144 76         345 $apply_modifiers->();
145 76         494 return;
146 63         361 };
147              
148 63 100       249 if ( my $info = $Role::Tiny::INFO{$target} ) {
149 5         26 $info->{not_methods}{$option} = $option;
150             }
151              
152 24     24   192 { no strict 'refs'; *{"${target}::option"} = $option; }
  24         83  
  24         10947  
  63         128  
  63         126  
  63         371  
153              
154 63         213 $apply_modifiers->();
155              
156 63         60041 return;
157             }
158              
159             my %filter_key = map { $_ => 1 } ( @OPTIONS_ATTRIBUTES, 'negativable' );
160              
161             sub _filter_attributes {
162 78     80   285 my %attributes = @_;
163 103         547 return map { ( $_ => $attributes{$_} ) }
164 78         256 grep { !exists $filter_key{$_} } keys %attributes;
  177         603  
165             }
166              
167             sub _validate_and_filter_options {
168 78     80   306 my (%options) = @_;
169 78 100       357 $options{doc} = $options{documentation} if !defined $options{doc};
170 78 100       273 $options{order} = 0 if !defined $options{order};
171              
172 78 100 100     514 if ( $options{json}
      100        
173             || ( defined $options{format} && $options{format} eq 'json' ) )
174             {
175 2         5 delete $options{repeatable};
176 2         6 delete $options{autosplit};
177 2         4 delete $options{autorange};
178 2         6 delete $options{negativable};
179 2         5 delete $options{negatable};
180 2         5 $options{json} = 1;
181 2         12 $options{format} = 's';
182             }
183              
184 78 100 100     298 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 4         25 eval { use_module("Data::Record"); use_module("Regexp::Common"); }
  4         199  
188 4 50       11 and $options{autosplit} = ',';
189             }
190              
191             exists $options{negativable}
192 78 100       507 and $options{negatable} = delete $options{negativable};
193              
194 226         716 my %cmdline_options = map { ( $_ => $options{$_} ) }
195 78         227 grep { exists $options{$_} } @OPTIONS_ATTRIBUTES, 'required';
  1092         2660  
196              
197             $cmdline_options{repeatable} = 1
198 78 100 66     511 if $cmdline_options{autosplit} or $cmdline_options{autorange};
199             $cmdline_options{format} .= "@"
200             if $cmdline_options{repeatable}
201             && defined $cmdline_options{format}
202 78 100 100     418 && 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 78 100 100     870 and defined $cmdline_options{format};
209              
210 76         511 return %cmdline_options;
211             }
212              
213             1;
214              
215             __END__