File Coverage

blib/lib/Module/Spec/V2.pm
Criterion Covered Total %
statement 20 64 31.2
branch 10 44 22.7
condition 0 14 0.0
subroutine 6 13 46.1
pod 3 5 60.0
total 39 140 27.8


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