File Coverage

blib/lib/Module/Spec/V2.pm
Criterion Covered Total %
statement 20 67 29.8
branch 11 46 23.9
condition 0 14 0.0
subroutine 6 14 42.8
pod 4 6 66.6
total 41 147 27.8


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