File Coverage

blib/lib/Version/Next.pm
Criterion Covered Total %
statement 56 61 91.8
branch 26 28 92.8
condition 5 6 83.3
subroutine 7 8 87.5
pod 1 1 100.0
total 95 104 91.3


line stmt bran cond sub pod time code
1 1     1   408 use strict;
  1         1  
  1         21  
2 1     1   3 use warnings;
  1         0  
  1         37  
3              
4             package Version::Next;
5             # ABSTRACT: increment module version numbers simply and correctly
6              
7             our $VERSION = '1.000';
8              
9             # Dependencies
10 1     1   302 use version 0.81 ();
  1         1252  
  1         22  
11 1     1   4 use Carp ();
  1         1  
  1         21  
12              
13             # Exporting
14 1     1   456 use Sub::Exporter 0 ( -setup => { exports => ['next_version'] } );
  1         6828  
  1         8  
15              
16             # lax versions are too lax
17             sub _cleanup {
18 78     78   59 my $version = shift;
19              
20             # treat 'undef' as 0
21 78 100       117 if ( $version eq 'undef' ) {
22 1         2 return "0";
23             }
24              
25             # fix leading dots
26 77 100       114 if ( $version =~ /^\./ ) {
27 3         7 my $num_dots =()= $version =~ /(\.)/g;
28 3 100       21 return $num_dots > 1 ? "v0$version" : "0$version";
29             }
30              
31             # fix trailing dots
32 74 100       91 if ( $version =~ /\.$/ ) {
33             # is_lax already prevents dotted-decimal with trailing dot
34 1         3 return "${version}0";
35             }
36              
37             # otherwise, it should be fine
38 73         79 return $version;
39             }
40              
41             sub next_version {
42 87     87 1 24099 my $version = shift;
43 87 50       157 return "0" unless defined $version;
44              
45 87 100       145 Carp::croak("Doesn't look like a version number: '$version'")
46             unless version::is_lax($version);
47              
48 78         885 $version = _cleanup($version);
49              
50 78         58 my $new_ver;
51 78         174 my $num_dots =()= $version =~ /(\.)/g;
52 78         105 my $has_v = $version =~ /^v/;
53 78         82 my $is_alpha = $version =~ /\A[^_]+_\d+\z/;
54              
55 78 100 100     213 if ( $has_v || $num_dots > 1 ) { # vstring
56 35 100       77 $version =~ s{^v}{} if $has_v;
57 35         63 my @parts = split /\./, $version;
58 35 50       44 if ($is_alpha) { # vstring with alpha
59 0         0 Carp::croak( _vstring_alpha_unsupported_msg($version) );
60             }
61 35         27 my @new_ver;
62 35         49 while (@parts) {
63 47         42 my $p = pop @parts;
64 47 100 66     114 if ( $p < 999 || !@parts ) {
65 35         40 unshift @new_ver, $p + 1;
66 35         36 last;
67             }
68             else {
69 12         21 unshift @new_ver, 0;
70             }
71             }
72 35 100       36 $new_ver = $has_v ? 'v' : '';
73 35         47 $new_ver .= join( ".", map { 0+ $_ } @parts, @new_ver );
  92         141  
74             }
75             else { # decimal fraction
76 43         35 my $alpha_neg_offset;
77 43 100       48 if ($is_alpha) {
78 20         29 $alpha_neg_offset = index( $version, "_" ) + 1 - length($version);
79 20         61 $version =~ s{_}{};
80             }
81 43         86 my ($fraction) = $version =~ m{\.(\d+)$};
82 43 100       83 my $n = defined $fraction ? length($fraction) : 0;
83 43         346 $new_ver = sprintf( "%.${n}f", $version + ( 10**-$n ) );
84 43 100       64 if ($is_alpha) {
85 20         32 substr( $new_ver, $alpha_neg_offset, 0, "_" );
86             }
87             }
88 78         225 return $new_ver;
89              
90             }
91              
92             sub _vstring_alpha_unsupported_msg {
93 0     0     my $v = shift;
94 0           my $msg = <<"HERE";
95             Can't determine next version number for '$v'.
96              
97             Due to changes in the interpretation of dotted-decimal version numbers with
98             alpha elements in version.pm 0.9913 and later, the notion of the 'next'
99             dotted-decimal alpha is ill-defined. Version::Next no longer supports
100             dotted-decimals with alpha elements.
101              
102             Aborting
103             HERE
104 0           chomp $msg;
105 0           return $msg;
106             }
107              
108             1;
109              
110             __END__
111              
112             =pod
113              
114             =encoding UTF-8
115              
116             =head1 NAME
117              
118             Version::Next - increment module version numbers simply and correctly
119              
120             =head1 VERSION
121              
122             version 1.000
123              
124             =head1 SYNOPSIS
125              
126             use Version::Next qw/next_version/;
127              
128             my $new_version = next_version( $old_version );
129              
130             =head1 DESCRIPTION
131              
132             This module provides a simple, correct way to increment a Perl module version
133             number. It does not attempt to guess what the original version number author
134             intended, it simply increments in the smallest possible fashion. Decimals are
135             incremented like an odometer. Dotted decimals are incremented piecewise and
136             presented in a standardized way.
137              
138             If more complex version manipulation is necessary, you may wish to consider
139             L<Perl::Version>.
140              
141             =head1 USAGE
142              
143             This module uses L<Sub::Exporter> for optional exporting. Nothing is exported
144             by default.
145              
146             =head2 C<next_version>
147              
148             my $new_version = next_version( $old_version );
149              
150             Given a string, this function make the smallest logical increment and
151             returns it. The input string must be a "lax" version numbers as defined by
152             the L<version> module. The string "undef" is treated as C<0> and
153             incremented to C<1>. Leading or trailing periods have a C<0> (or C<v0>)
154             prepended or appended as appropriate. For legacy reasons, given no
155             argument or a literal C<undef> (not the string "undef"), the function
156             returns C<0>.
157              
158             Decimal versions are incremented like an odometer, preserving the original
159             number of decimal places. If an underscore is present (indicating an "alpha"
160             version), its relative position is preserved. Examples:
161              
162             0.001 -> 0.002
163             0.999 -> 1.000
164             0.1229 -> 0.1230
165             0.12_34 -> 0.12_35
166             0.12_99 -> 0.13_00
167              
168             Dotted-decimal versions have the least significant element incremented by one.
169             If the result exceeds C<999>, the element resets to C<0> and the next
170             most significant element is incremented, and so on. Any leading zero padding
171             is removed. Examples:
172              
173             v1.2.3 -> v1.2.4
174             v1.2.999 -> v1.3.0
175             v1.999.999 -> v2.0.0
176              
177             B<NOTE>: Due to changes in the interpretation of dotted-decimal version
178             numbers with alpha elements in L<version> 0.9913 and later, the notion of
179             the 'next' dotted-decimal alpha is ill-defined. Version::Next no longer
180             supports dotted-decimals with alpha elements and a fatal exception will be
181             thrown if one is provided to C<next_version>.
182              
183             =head1 SEE ALSO
184              
185             =over 4
186              
187             =item *
188              
189             L<version>
190              
191             =item *
192              
193             L<Perl::Version>
194              
195             =back
196              
197             =for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan
198              
199             =head1 SUPPORT
200              
201             =head2 Bugs / Feature Requests
202              
203             Please report any bugs or feature requests through the issue tracker
204             at L<https://github.com/dagolden/Version-Next/issues>.
205             You will be notified automatically of any progress on your issue.
206              
207             =head2 Source Code
208              
209             This is open source software. The code repository is available for
210             public review and contribution under the terms of the license.
211              
212             L<https://github.com/dagolden/Version-Next>
213              
214             git clone https://github.com/dagolden/Version-Next.git
215              
216             =head1 AUTHOR
217              
218             David Golden <dagolden@cpan.org>
219              
220             =head1 CONTRIBUTOR
221              
222             =for stopwords Grzegorz Rożniecki
223              
224             Grzegorz Rożniecki <xaerxess@gmail.com>
225              
226             =head1 COPYRIGHT AND LICENSE
227              
228             This software is Copyright (c) 2016 by David Golden.
229              
230             This is free software, licensed under:
231              
232             The Apache License, Version 2.0, January 2004
233              
234             =cut