File Coverage

blib/lib/Perl/Version.pm
Criterion Covered Total %
statement 182 182 100.0
branch 59 60 98.3
condition 25 29 86.2
subroutine 25 25 100.0
pod 10 10 100.0
total 301 306 98.3


line stmt bran cond sub pod time code
1             package Perl::Version;
2              
3 5     5   59181 use warnings;
  5         10  
  5         181  
4 5     5   29 use strict;
  5         9  
  5         249  
5 5     5   25 use Carp;
  5         15  
  5         501  
6 5     5   30 use Scalar::Util qw( blessed );
  5         9  
  5         968  
7              
8             our $VERSION = '1.013_02';
9              
10             use overload (
11 5         68 '""' => \&stringify,
12             '<=>' => \&vcmp,
13             'cmp' => \&vcmp,
14 5     5   17185 );
  5         8315  
15              
16 5         964 use constant REGEX => qr/ ( (?i: Revision: \s+ ) | v | )
17             ( \d+ (?: [.] \d+)* )
18 5     5   1011 ( (?: _ \d+ )? ) /x;
  5         14  
19              
20 5     5   30 use constant MATCH => qr/ ^ ( \s* ) @{[ REGEX ]} ( \s* ) $ /x;
  5         9  
  5         15  
  5         1126  
21              
22             my %NORMAL_FORMAT = (
23             prefix => 'v',
24             printf => ['%d'],
25             extend => '.%d',
26             alpha => '_%02d',
27             suffix => '',
28             fields => 3,
29             );
30              
31             my %NUMERIC_FORMAT = (
32             prefix => '',
33             printf => [ '%d', '.%03d' ],
34             extend => '%03d',
35             alpha => '_%02d',
36             suffix => '',
37             fields => 2,
38             );
39              
40             my %COMPONENT_NAME;
41              
42             BEGIN {
43 5     5   29 %COMPONENT_NAME = (
44             revision => 0,
45             version => 1,
46             subversion => 2
47             );
48              
49             # Make accessors
50 5         27 my @fields = ( keys %COMPONENT_NAME, qw( alpha ) );
51              
52 5     5   35 no strict 'refs';
  5         10  
  5         1363  
53              
54 5         52 for my $field ( @fields ) {
55             *$field = sub {
56 108     108   118206 my $self = shift;
57 108         317 return $self->component( $field, @_ );
58 20         145 };
59              
60 20         39 my $inc_func = "inc_$field";
61             *$inc_func = sub {
62 13     13   31 my $self = shift;
63 13         54 return $self->increment( $field );
64 20         15868 };
65             }
66             }
67              
68             sub new {
69 310     310 1 120459 my $class = shift;
70 310   66     2980 my $self
71             = bless {}, ref $class
72             || $class
73             || croak "new must be called as a class or object method";
74              
75 309         1269 $self->{version} = [0];
76              
77 309 100       1737 $self->_parse( @_ ) if @_;
78              
79 308         1394 return $self;
80             }
81              
82             sub _resolve_component_name {
83 319     319   962 my $self = shift;
84 319         427 my $name = shift;
85              
86 319 100       1573 if ( $name =~ /^-?\d+$/ ) {
87             # Allow negative subscripts
88 278 100       797 $name += $self->components if $name < 0;
89 278         601 return $name;
90             }
91              
92 41 100       576 croak "Unknown component name: $name"
93             unless exists $COMPONENT_NAME{ lc( $name ) };
94              
95 39         1789 return $COMPONENT_NAME{ lc( $name ) };
96             }
97              
98             sub _guess_num_format {
99 836     836   1066 my $self = shift;
100 836         1338 my $num = shift;
101              
102 836 100       2994 if ( $num =~ m{ ^ 0 \d }x ) {
103 122         481 return '%0' . length( $num ) . 'd';
104             }
105              
106 714         2106 return '%d';
107             }
108              
109             sub _parse {
110 307     307   411 my $self = shift;
111              
112             # Check for vstring before anything else happens
113 307 100 66     1978 if ( $] >= 5.008_001 && Scalar::Util::isvstring $_[0] ) {
114 3         29 $self->{format} = {%NORMAL_FORMAT};
115 3         14 my @parts = map { ord } split //, shift;
  9         19  
116 3         9 $self->{version} = \@parts;
117 3         7 return;
118             }
119              
120 304         561 my $version = join( ' ', map { "$_" } @_ );
  304         1165  
121              
122 304 100       2481 croak "Illegal version string: $version"
123             unless $version =~ MATCH;
124              
125 303         829 my $format = { fields => 1 };
126 303         1592 my ( $pad, $pfx, $ver, $alp, $sfx ) = ( $1, $2, $3, $4, $5 );
127              
128             # Decode version into format
129 303         923 $format->{prefix} = $pad . $pfx;
130 303         1588 $format->{suffix} = $sfx;
131              
132 303         1074 my @parts = split( /[.]/, $ver );
133 303         1118 my @ver = ( shift( @parts ) + 0 );
134              
135 303         814 my @fmt = ( $self->_guess_num_format( $ver[0] ) );
136              
137 303 100 100     2098 if ( @parts == 1 && length( $parts[0] ) >= 3 ) {
138              
139 48         74 my $threes = pop @parts;
140 48         344 my @cluster = ( $threes =~ /(\d{1,3})/g );
141              
142             # warn "# $threes <", join( '>, <', @cluster ), ">\n";
143 48         92 push @fmt, map { $self->_guess_num_format( $_ ) } @cluster;
  75         214  
144 48         88 $fmt[1] = '.' . $fmt[1];
145 48         83 $format->{extend} = '%03d';
146              
147 48         75 push @parts, map { 0 + $_ } @cluster;
  75         206  
148             }
149             else {
150              
151             # Parts with leading zeros
152 255         407 my @lz = grep { m{ ^ 0 \d }x } @parts;
  416         1069  
153              
154             # Work out how many different lengths we have
155 255         468 my %le = map { length( $_ ) => 1 } @parts;
  416         1271  
156              
157 255 100 100     1086 if ( @lz && keys %le == 1 ) {
158 18         50 push @fmt,
159             ( '.' . $self->_guess_num_format( shift @lz ) ) x @parts;
160             }
161             else {
162 237         450 push @fmt, map { '.' . $self->_guess_num_format( $_ ) } @parts;
  383         896  
163             }
164              
165 255 100       1839 $format->{extend} = ( @parts ? '' : '.' ) . $fmt[-1];
166             }
167              
168 303         604 $format->{printf} = \@fmt;
169              
170 303 100       708 if ( length( $alp ) ) {
171 57 50       260 die "Badly formatted alpha got through"
172             unless $alp =~ m{ _ (\d+) }x;
173              
174 57         109 my $alpha = $1;
175              
176 57         189 $self->{alpha} = $alpha + 0;
177 57         131 $format->{alpha} = '_' . $self->_guess_num_format( $alpha );
178             }
179             else {
180 246         578 $format->{alpha} = $NORMAL_FORMAT{alpha};
181             }
182              
183 303         561 $self->{format} = $format;
184              
185 303         433 push @ver, map { $_ + 0 } @parts;
  491         1180  
186              
187 303         568 $self->{version} = \@ver;
188              
189 303         1733 return;
190             }
191              
192             sub _format {
193 264     264   376 my $self = shift;
194 264         311 my $format = shift;
195              
196 264         1896 my @result = ();
197              
198 264         294 my @parts = @{ $self->{version} };
  264         997  
199 264         369 my @fmt = @{ $format->{printf} };
  264         1037  
200              
201 264         1643 push @parts, 0 while @parts < $format->{fields};
202              
203             # Adjust the format to be the same length as the number of fields
204 264         701 pop @fmt while @fmt > @parts;
205 264         1021 push @fmt, $format->{extend} while @parts > @fmt;
206              
207 264         8551 my $version
208             = ( $format->{prefix} )
209             . sprintf( join( '', @fmt ), @parts )
210             . ( $format->{suffix} );
211              
212 264 100       837 $version .= sprintf( $format->{alpha}, $self->{alpha} )
213             if defined $self->{alpha};
214              
215 264         648 push @result, $version;
216              
217 264         2297 return join( ' ', @result );
218             }
219              
220             sub stringify {
221 137     137 1 201778 my $self = shift;
222 137   100     1294 return $self->_format( $self->{format} || \%NORMAL_FORMAT );
223             }
224              
225             sub normal {
226 63     63 1 22749 return shift->_format( \%NORMAL_FORMAT );
227             }
228              
229             sub numify {
230 64     64 1 19696 return shift->_format( \%NUMERIC_FORMAT );
231             }
232              
233             sub is_alpha {
234 1     1 1 3320 my $self = shift;
235 1         31 return exists $self->{alpha};
236             }
237              
238             sub vcmp {
239 423     423 1 6050 my ( $self, $other, $rev ) = @_;
240              
241             # Promote to object
242 423 100       981 $other = __PACKAGE__->new( $other ) unless ref $other;
243              
244 423 100 100     3698 croak "Can't compare with $other"
245             unless blessed $other && $other->isa( __PACKAGE__ );
246              
247 421 100       1031 return $other->vcmp( $self, 0 ) if $rev;
248              
249 418         416 my @this = @{ $self->{version} };
  418         1164  
250 418         616 my @that = @{ $other->{version} };
  418         1036  
251              
252 418         1437 push @this, 0 while @this < @that;
253 418         1177 push @that, 0 while @that < @this;
254              
255 418         1026 while ( @this ) {
256 1159 100       10223 if ( my $cmp = ( shift( @this ) <=> shift( @that ) ) ) {
257 55         340 return $cmp;
258             }
259             }
260              
261 363   100     5959 return ( $self->{alpha} || 0 ) <=> ( $other->{alpha} || 0 );
      100        
262             }
263              
264             sub components {
265 392     392 1 3407 my $self = shift;
266              
267 392 100       949 if ( @_ ) {
268 9         34 my $fields = shift;
269              
270 9 100       29 if ( ref $fields eq 'ARRAY' ) {
271 3         14 $self->{version} = [@$fields];
272             }
273             else {
274 6 100       259 croak "Can't set the number of components to 0"
275             unless $fields;
276              
277             # Adjust the number of fields
278 5         7 pop @{ $self->{version} }, while @{ $self->{version} } > $fields;
  6         23  
  1         3  
279 9         41 push @{ $self->{version} }, 0,
  4         8  
280 5         6 while @{ $self->{version} } < $fields;
281             }
282             }
283             else {
284 383         417 return @{ $self->{version} };
  383         1123  
285             }
286             }
287              
288             sub component {
289 387     387 1 26629 my $self = shift;
290 387         550 my $field = shift;
291              
292 387 100       3035 defined $field or croak "You must specify a component number";
293              
294 386 100       839 if ( lc( $field ) eq 'alpha' ) {
295 95 100       181 if ( @_ ) {
296 34         63 my $alpha = shift;
297 34 100       96 if ( $alpha ) {
298 5         28 $self->{alpha} = $alpha;
299             }
300             else {
301 29         824 delete $self->{alpha};
302             }
303             }
304             else {
305 61   100     551 return $self->{alpha} || 0;
306             }
307             }
308             else {
309 291         3228 $field = $self->_resolve_component_name( $field );
310 290         2181 my $fields = $self->components;
311              
312 290 100       509 if ( @_ ) {
313 61 100       154 if ( $field >= $fields ) {
314              
315             # Extend array if necessary
316 2         8 $self->components( $field + 1 );
317             }
318              
319 61         189 $self->{version}->[$field] = shift;
320             }
321             else {
322 229 100 66     1241 return unless $field >= 0 && $field < $fields;
323 171         710 return $self->{version}->[$field];
324             }
325             }
326             }
327              
328             sub increment {
329 32     32 1 1625 my $self = shift;
330 32         61 my $field = shift;
331 32         116 my $fields = $self->components;
332              
333 32 100       122 if ( lc( $field ) eq 'alpha' ) {
334 4         13 $self->alpha( $self->alpha + 1 );
335             }
336             else {
337 28         114 $field = $self->_resolve_component_name( $field );
338              
339 27 100 66     4252 croak "Component $field is out of range 0..", $fields - 1
340             if $field < 0 || $field >= $fields;
341              
342             # Increment the field
343 26         261 $self->component( $field, $self->component( $field ) + 1 );
344              
345             # Zero out any following fields
346 26         77 while ( ++$field < $fields ) {
347 28         51 $self->component( $field, 0 );
348             }
349 26         65 $self->alpha( 0 );
350             }
351             }
352              
353             sub set {
354 2     2 1 4 my $self = shift;
355 2         4 my $other = shift;
356              
357 2 100       13 $other = __PACKAGE__->new( $other ) unless ref $other;
358              
359 2         10 my @comp = $other->components;
360              
361 2         8 $self->components( \@comp );
362 2         9 $self->alpha( $other->alpha );
363             }
364              
365             1;
366             __END__