File Coverage

blib/lib/Module/Spec/V1.pm
Criterion Covered Total %
statement 35 65 53.8
branch 20 44 45.4
condition 2 14 14.2
subroutine 7 14 50.0
pod 4 6 66.6
total 68 143 47.5


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