File Coverage

blib/lib/Module/Spec/V1.pm
Criterion Covered Total %
statement 35 62 56.4
branch 19 42 45.2
condition 2 14 14.2
subroutine 7 13 53.8
pod 3 5 60.0
total 66 136 48.5


line stmt bran cond sub pod time code
1              
2             package Module::Spec::V1;
3             $Module::Spec::V1::VERSION = '0.8.0';
4             # ABSTRACT: Load modules based on V1 specifications
5 3     3   115098 use 5.012;
  3         21  
6              
7             # use warnings;
8              
9             our @EXPORT_OK = qw(need_module try_module);
10              
11             BEGIN {
12 3     3   699 require Module::Spec::V0;
13 3         8 *_generate_code = \&Module::Spec::V0::_generate_code;
14 3         13 *_opts = \&Module::Spec::V0::_opts;
15 3         10 *_need_module = \&Module::Spec::V0::_need_module;
16 3         4 *_require_module = \&Module::Spec::V0::_require_module;
17 3         7 *_try_module = \&Module::Spec::V0::_try_module;
18 3         2867 *croak = \&Module::Spec::V0::croak;
19             }
20              
21             state $MODULE_RE = qr/ [^\W\d]\w*+ (?: :: \w++ )*+ /x;
22             state $VERSION_RE = qr/ v?+ (?>\d+) (?: [\._] \d+ )*+ /x;
23              
24             sub parse_module_spec {
25 0     0 0 0 my $spec = pop;
26 0 0       0 if ( my ( $m, @v ) = _parse_module_spec($spec) ) {
27 0         0 my %info = ( module => $m );
28 0 0       0 $info{version} = $v[0] if @v;
29 0         0 return \%info;
30             }
31 0         0 return;
32             }
33              
34             sub _parse_module_spec {
35 63 100   63   638 if ( $_[0] =~ m/\A $MODULE_RE \z/x ) {
    100          
    50          
36 15         64 return $_[0];
37             }
38             elsif ( ref $_[0] eq 'ARRAY' ) {
39              
40             croak(qq{Should contain one or two entries})
41 25 50 33     33 unless @{ $_[0] } && @{ $_[0] } <= 2;
  25         66  
  25         64  
42 25         44 my $m = $_[0][0];
43 25 50       219 $m =~ m/\A $MODULE_RE \z/x
44             or croak(qq{Can't parse $m});
45 25 50       35 return ($m) if @{ $_[0] } == 1;
  25         49  
46 25         40 my $v = $_[0][1];
47 25         43 return ( $m, _parse_version_spec($v) );
48             }
49             elsif ( ref $_[0] eq 'HASH' ) {
50              
51 23 50       34 croak(qq{Should contain a single pair}) unless keys %{ $_[0] } == 1;
  23         63  
52 23         31 my ( $m, $v ) = %{ $_[0] };
  23         53  
53 23 50       256 $m =~ m/\A $MODULE_RE \z/x
54             or croak(qq{Can't parse $m});
55 23         64 return ( $m, _parse_version_spec($v) );
56             }
57 0         0 return;
58             }
59              
60 48 100   48   199 sub _parse_v_spec { $_[0] eq '0' ? () : ( $_[0] ) }
61              
62             sub _parse_version_spec { # Extra sanity check
63 48 50 33 48   337 unless ( defined $_[0] && $_[0] =~ m/\A $VERSION_RE \z/x ) {
64 0         0 croak(qq{Invalid version $_[0]});
65             }
66 48         151 goto &_parse_v_spec;
67             }
68              
69             # Precomputed for most common case
70             state $_OPTS = _opts();
71              
72             # need_module($spec)
73             # need_module($spec, \%opts)
74             sub need_module {
75 28 100   28 1 14343 my $opts = @_ > 1 ? _opts(pop) : $_OPTS;
76              
77 28 50       66 my ( $m, @v ) = _parse_module_spec( $_[-1] )
78             or croak(qq{Can't parse $_[-1]});
79 28         84 return _need_module( $opts, $m, @v );
80             }
81              
82             # generate_code($spec, \%opts);
83             sub generate_code {
84 0 0   0 0 0 my $opts = @_ > 1 ? pop : {};
85              
86 0 0       0 my ( $m, @v ) = _parse_module_spec( $_[-1] )
87             or croak(qq(Can't parse $_[-1]}));
88 0         0 return _generate_code( $opts, $m, @v );
89             }
90              
91             # try_module($spec)
92             # try_module($spec, \%opts)
93             sub try_module {
94 35 100   35 1 13940 my $opts = @_ > 1 ? _opts(pop) : $_OPTS;
95              
96 35 50       67 my ( $m, @v ) = _parse_module_spec( $_[-1] )
97             or croak(qq{Can't parse $_[-1]});
98 35         80 return _try_module( $opts, $m, @v );
99             }
100              
101             sub need_modules {
102 0 0   0 1   my $op = $_[0] =~ /\A-/ ? shift : '-all';
103 0           state $SUB_FOR = {
104             '-all' => \&_need_all_modules,
105             '-any' => \&_need_any_modules,
106             '-oneof' => \&_need_first_module,
107             };
108 0 0         croak(qq{Unknown operator "$op"}) unless my $sub = $SUB_FOR->{$op};
109 0 0 0       if ( @_ == 1 && ref $_[0] eq 'HASH' ) {
110 0           @_ = map { [ $_ => $_[0]{$_} ] } keys %{ $_[0] };
  0            
  0            
111             }
112 0           goto &$sub;
113             }
114              
115             sub _need_all_modules {
116 0     0     map { scalar need_module($_) } @_;
  0            
117             }
118              
119             sub _need_any_modules {
120 0     0     my ( @m, $m );
121 0   0       ( $m = try_module($_) ) && push @m, $m for @_;
122 0           return @m;
123             }
124              
125             sub _need_first_module {
126 0     0     my $m;
127 0   0       ( $m = try_module($_) ) && return ($m) for @_;
128 0           return;
129             }
130              
131             1;
132              
133             #pod =encoding utf8
134             #pod
135             #pod =head1 SYNOPSIS
136             #pod
137             #pod use Module::Spec::V1 ();
138             #pod Module::Spec::V1::need_module('Mango');
139             #pod Module::Spec::V1::need_module( [ 'Mango' => '2.3' ] );
140             #pod Module::Spec::V1::need_module( { 'Mango' => '2.3' } );
141             #pod
142             #pod =head1 DESCRIPTION
143             #pod
144             #pod B
145             #pod
146             #pod =head2 MODULE SPECS
147             #pod
148             #pod As string
149             #pod
150             #pod M any version
151             #pod
152             #pod As a hash ref
153             #pod
154             #pod { M => V } minimum match, ≥ V
155             #pod { M => '0' } accepts any version
156             #pod
157             #pod As an array ref
158             #pod
159             #pod [ M ]
160             #pod [ M => V ] minimum match, ≥ V
161             #pod [ M => '0' ] same as [ M ], accepts any version
162             #pod
163             #pod =head1 FUNCTIONS
164             #pod
165             #pod L implements the following functions.
166             #pod
167             #pod =head2 need_module
168             #pod
169             #pod $module = need_module('SomeModule');
170             #pod $module = need_module( { 'SomeModule' => '2.3' } );
171             #pod $module = need_module( [ 'SomeModule' => '2.3' ] );
172             #pod
173             #pod $module = need_module($spec);
174             #pod $module = need_module( $spec, \%opts );
175             #pod
176             #pod Loads a module and checks for a version requirement (if any).
177             #pod Returns the name of the loaded module.
178             #pod
179             #pod On list context, returns the name of the loaded module
180             #pod and its version (as reported by C<< $m->VERSION >>).
181             #pod
182             #pod ( $m, $v ) = need_module($spec);
183             #pod ( $m, $v ) = need_module( $spec, \%opts );
184             #pod
185             #pod These options are currently available:
186             #pod
187             #pod =over 4
188             #pod
189             #pod =item require
190             #pod
191             #pod require => 1 # default
192             #pod require => 0
193             #pod require => sub { my ($m, @v) = @_; ... }
194             #pod
195             #pod Controls whether the specified module should be Cd or not.
196             #pod It can be given as a non-subroutine value, which gets
197             #pod interpreted as a boolean: true means that the module
198             #pod should be loaded with C and false means
199             #pod that no attempt should be made to load it.
200             #pod This option can also be specified as a subroutine which gets
201             #pod passed the module name and version requirement (if any)
202             #pod and which should return true if the module should be loaded
203             #pod with C or false otherwise.
204             #pod
205             #pod =back
206             #pod
207             #pod =head2 need_modules
208             #pod
209             #pod @modules = need_modules(@spec);
210             #pod @modules = need_modules(-all => @spec);
211             #pod @modules = need_modules(-any => @spec);
212             #pod @modules = need_modules(-oneof => @spec);
213             #pod
214             #pod @modules = need_modules(\%spec);
215             #pod @modules = need_modules(-all => \%spec);
216             #pod @modules = need_modules(-any => \%spec);
217             #pod @modules = need_modules(-oneof => \%spec);
218             #pod
219             #pod Loads some modules according to a specified rule.
220             #pod
221             #pod The current supported rules are C<-all>, C<-any> and C<-oneof>.
222             #pod If none of these are given as the first argument,
223             #pod C<-all> is assumed.
224             #pod
225             #pod The specified modules are given as module specs,
226             #pod either as a list or as a single hashref.
227             #pod If given as a list, the corresponding order will be respected.
228             #pod If given as a hashref, a random order is to be expected.
229             #pod
230             #pod The behavior of the rules are as follows:
231             #pod
232             #pod =over 4
233             #pod
234             #pod =item -all
235             #pod
236             #pod All specified modules are loaded by C.
237             #pod If successful, returns the names of the loaded modules.
238             #pod
239             #pod =item -any
240             #pod
241             #pod All specified modules are loaded by C.
242             #pod Returns the names of the modules successfully loaded.
243             #pod
244             #pod =item -oneof
245             #pod
246             #pod Specified modules are loaded by C
247             #pod until a successful load.
248             #pod Returns (in list context) the name of the loaded module.
249             #pod
250             #pod =back
251             #pod
252             #pod =head2 try_module
253             #pod
254             #pod $module = try_module('SomeModule');
255             #pod $module = try_module( { 'SomeModule' => '2.3' } );
256             #pod $module = try_module( [ 'SomeModule' => '2.3' ] );
257             #pod
258             #pod $module = try_module($spec);
259             #pod $module = try_module( $spec, \%opts );
260             #pod
261             #pod Tries to load a module (if available) and checks for a version
262             #pod requirement (if any). Returns the name of the loaded module
263             #pod if it can be loaded successfully and satisfies any specified version
264             #pod requirement.
265             #pod
266             #pod On list context, returns the name of the loaded module
267             #pod and its version (as reported by C<< $m->VERSION >>).
268             #pod
269             #pod ( $m, $v ) = try_module($spec);
270             #pod ( $m, $v ) = try_module($spec, \%opts);
271             #pod
272             #pod These options are currently available:
273             #pod
274             #pod =over 4
275             #pod
276             #pod =item require
277             #pod
278             #pod require => 1 # default
279             #pod require => 0
280             #pod require => sub { my ($m, @v) = @_; ... }
281             #pod
282             #pod Controls whether the specified module should be Cd or not.
283             #pod It can be given as a non-subroutine value, which gets
284             #pod interpreted as a boolean: true means that the module
285             #pod should be loaded with C and false means
286             #pod that no attempt should be made to load it.
287             #pod This option can also be specified as a subroutine which gets
288             #pod passed the module name and version requirement (if any)
289             #pod and which should return true if the module should be loaded
290             #pod with C or false otherwise.
291             #pod
292             #pod =back
293             #pod
294             #pod =head1 CAVEATS
295             #pod
296             #pod =over 4
297             #pod
298             #pod =item *
299             #pod
300             #pod Single quotes (C<'>) are not accepted as package separators.
301             #pod
302             #pod =item *
303             #pod
304             #pod Exceptions are not thrown from the perspective of the caller.
305             #pod
306             #pod =back
307             #pod
308             #pod =head1 SEE ALSO
309             #pod
310             #pod L
311             #pod
312             #pod =cut
313              
314             __END__