File Coverage

blib/lib/Module/Extract/VERSION.pm
Criterion Covered Total %
statement 52 54 96.3
branch 14 20 70.0
condition 7 9 77.7
subroutine 9 9 100.0
pod 1 1 100.0
total 83 93 89.2


line stmt bran cond sub pod time code
1 3     3   711028 use v5.10;
  3         9  
2 3     3   1205 use utf8;
  3         702  
  3         20  
3              
4             package Module::Extract::VERSION;
5 3     3   153 use strict;
  3         3  
  3         41  
6              
7 3     3   9 use warnings;
  3         3  
  3         93  
8 3     3   10 no warnings;
  3         3  
  3         114  
9              
10 3     3   10 use Carp qw(carp);
  3         4  
  3         1389  
11              
12             our $VERSION = '1.121';
13              
14             =encoding utf8
15              
16             =head1 NAME
17              
18             Module::Extract::VERSION - Extract a module version safely
19              
20             =head1 SYNOPSIS
21              
22             use Module::Extract::VERSION;
23              
24             my $version # just the version
25             = Module::Extract::VERSION->parse_version_safely( $file );
26              
27             my @version_info # extra info
28             = Module::Extract::VERSION->parse_version_safely( $file );
29              
30             =head1 DESCRIPTION
31              
32             This module lets you pull out of module source code the version number
33             for the module. It assumes that there is only one C<$VERSION>
34             in the file and the entire C<$VERSION> statement is on the same line.
35              
36             =cut
37              
38             =head2 Class methods
39              
40             =over 4
41              
42             =item $class->parse_version_safely( FILE );
43              
44             Given a module file, return the module version. This works just like
45             C in PAUSE. It looks for the single line that has the
46             C<$VERSION> statement, extracts it, evals it in a Safe compartment,
47             and returns the result.
48              
49             In scalar context, it returns just the version as a string. In list
50             context, it returns the list of:
51              
52             sigil
53             fully-qualified variable name
54             version value
55             file name
56             line number of $VERSION
57              
58             =cut
59              
60             my( $version_pattern, $package_block_pattern);
61              
62             BEGIN {
63 3     3   17 $version_pattern = qr/
64             (?
65             [\$*]
66             )
67             (?
68             (?
69             [\w\:\']*
70             )
71             \b
72             VERSION
73             )
74             \b
75             .*?
76             \=
77             (?
78             .*
79             )
80             /x;
81              
82 3         1531 $package_block_pattern = qr/
83             \b package \s+
84             (? \w[\w\:\']* ) \s+
85             (? \S+ ) \s* [;{]
86             /x;
87             }
88              
89             sub parse_version_safely { # stolen from PAUSE's mldistwatch, but refactored
90 11     11 1 10006 my( $class, $file ) = @_;
91              
92 11         38 local $/ = "\n";
93 11         12 local $_; # don't mess with the $_ in the map calling this
94              
95 11         12 my $fh;
96 11 50       445 unless( open $fh, "<", $file ) {
97 0         0 carp( "Could not open file [$file]: $!\n" );
98 0         0 return;
99             }
100              
101 11         21 my $in_pod = 0;
102 11         17 my( $sigil, $var, $version, $line_number, $rhs );
103 11         322 LINE: while( <$fh> ) {
104 108         109 $line_number++;
105 108         101 chomp;
106 108 50       164 $in_pod = /^=(?!cut)/ ? 1 : /^=cut/ ? 0 : $in_pod;
    50          
107 108 100 66     270 next if $in_pod || /^\s*#/;
108              
109             # package NAMESPACE VERSION <-- we handle that
110             # package NAMESPACE VERSION BLOCK
111 93 100 100     454 next unless( m/$version_pattern/ || m/$package_block_pattern/ );
112              
113 11         112 ( $sigil, $var, $rhs ) = @+{ qw(sigil var rhs) };
114              
115 11 100       32 if ($sigil) {
116 7         40 $version = $class->_eval_version( $_, @+{ qw(sigil var rhs) } );
117             }
118             else {
119 4         20 $version = $class->_eval_version( $_, '$', 'VERSION', qq('$rhs') );
120             }
121              
122 11         795 last LINE;
123             }
124 11 50 66     46 $line_number = undef if eof($fh) && ! defined( $version );
125 11         172 close $fh;
126              
127             return wantarray ?
128 11 50       177 ( $sigil, $var, $version, $file, $line_number )
129             :
130             $version;
131             }
132              
133             sub _eval_version {
134 11     11   41 my( $class, $line, $sigil, $var, $rhs ) = @_;
135              
136 11         548 require Safe;
137 11         8134 local $^W = 0;
138              
139 11         42 my $s = Safe->new;
140              
141 11 100       7673 if( $rhs =~ m/\h* qv \( \h* (['"]) ([0-9.]+) \1 \h* \)/x ) {
142 3         10 $rhs = qq('$2');
143             }
144 11 50       26 if( defined $Devel::Cover::VERSION ) {
145 11         28 $s->share_from('main', ['&Devel::Cover::use_file']);
146             }
147              
148 11         391 my $command = '$VERSION = ' . $rhs;
149 11         27 $s->reval($command);
150 11         5191 my $version = $s->reval('$VERSION');
151              
152 11         4082 return $version;
153             }
154              
155             =back
156              
157             =head1 SOURCE AVAILABILITY
158              
159             This code is in Github:
160              
161             https://github.com/briandfoy/module-extract-version.git
162              
163             =head1 AUTHOR
164              
165             brian d foy, C<< >>
166              
167             I stole the some of this code from C in the PAUSE
168             code by Andreas König, but I've moved most of it around.
169              
170             Andrey Starodubtsev added code to handle the v5.12 and v5.14
171             C syntax.
172              
173             =head1 COPYRIGHT AND LICENSE
174              
175             Copyright © 2008-2026, brian d foy C<< >>. All rights reserved.
176              
177             You may redistribute this under the Artistic License 2.0.
178              
179             =cut
180              
181             1;