File Coverage

lib/Changes/Version.pm
Criterion Covered Total %
statement 426 935 45.5
branch 172 828 20.7
condition 140 474 29.5
subroutine 62 105 59.0
pod 38 38 100.0
total 838 2380 35.2


line stmt bran cond sub pod time code
1             ##----------------------------------------------------------------------------
2             ## Changes file management - ~/lib/Changes/Version.pm
3             ## Version v0.2.0
4             ## Copyright(c) 2022 DEGUEST Pte. Ltd.
5             ## Author: Jacques Deguest <jack@deguest.jp>
6             ## Created 2022/12/01
7             ## Modified 2023/08/20
8             ## All rights reserved
9             ##
10             ##
11             ## This program is free software; you can redistribute it and/or modify it
12             ## under the same terms as Perl itself.
13             ##----------------------------------------------------------------------------
14             package Changes::Version;
15             BEGIN
16             {
17 20     20   105953 use strict;
  20         53  
  20         706  
18 20     20   104 use warnings;
  20         44  
  20         609  
19 20     20   113 use warnings::register;
  20         175  
  20         2198  
20 20     20   664 use parent qw( Module::Generic );
  20         327  
  20         114  
21 20     20   12908275 use vars qw( $VERSION $VERSION_LAX_REGEX $DEFAULT_TYPE );
  20         41  
  20         1042  
22 20     20   131 use version ();
  20         40  
  20         393  
23 20     20   95 use Nice::Try;
  20         36  
  20         177  
24             # From version::regex
25             # Comments in the regular expression below are taken from version::regex
26 20     20   5617 our $VERSION_LAX_REGEX = qr/
27             (?<ver_str>
28             # Lax dotted-decimal version number. Distinguished by having either leading "v"
29             # or at least three non-alpha parts. Alpha part is only permitted if there are
30             # at least two non-alpha parts. Strangely enough, without the leading "v", Perl
31             # takes .1.2 to mean v0.1.2, so when there is no "v", the leading part is optional
32             (?<dotted>
33             (?<has_v>v)
34             (?<ver>
35             (?<major>[0-9]+)
36             (?:
37             (?<minor_patch>(?:\.[0-9]+)+)
38             (?:_(?<alpha>[0-9]+))?
39             )?
40             )
41             |
42             (?<ver>
43             (?<major>[0-9]+)?
44             (?<minor_patch>(?:\.[0-9]+){2,})
45             (?:_(?<alpha>[0-9]+))?
46             )
47             )
48             |
49             (?<dotted>
50             (?<dotted_numified>
51             (?<dotted_numified_under>
52             (?<ver>
53             (?<release>
54             (?<major>[0-9]+)
55             (?<minor_patch>
56             \.
57             (?<minor>[0-9]{3})
58             (?:_(?<patch>[0-9]{3}))
59             )
60             )
61             )
62             )
63             |
64             (?<ver>
65             (?<release>
66             (?<major>[0-9]+)
67             (?<minor_patch>
68             \.
69             (?<minor>0[0-9]{2})
70             (?<patch>0[0-9]{2})
71             )
72             )
73             (?:_(?<alpha>[0-9]+))?
74             )
75             )
76             )
77             |
78             # Lax decimal version number. Just like the strict one except for allowing an
79             # alpha suffix or allowing a leading or trailing decimal-point
80             (?<decimal>
81             (?<ver>(?<release>(?<major>[0-9]+) (?: (?:\.(?<minor>[0-9]+)) | \. )?) (?:_(?<alpha>[0-9]+))?)
82             |
83             (?<ver>(?:\.(?<release>(?<major>[0-9]+))) (?:_(?<alpha>[0-9]+))?)
84             )
85             )/x;
86 20         58 our $DEFAULT_TYPE = 'dotted';
87             use overload (
88             '""' => \&as_string,
89             # '=' => \&clone,
90 0     0   0 '0+' => sub{ $_[0]->numify->as_string },
91             '<=>' => \&_compare,
92             'cmp' => \&_compare,
93             'bool' => \&_bool,
94 12     12   13506 '+' => sub { return( shift->_compute( @_, { op => '+' }) ); },
95 12     12   13691 '-' => sub { return( shift->_compute( @_, { op => '-' }) ); },
96 12     12   13532 '*' => sub { return( shift->_compute( @_, { op => '*' }) ); },
97 12     12   13522 '/' => sub { return( shift->_compute( @_, { op => '/' }) ); },
98 8     8   10040 '+=' => sub { return( shift->_compute( @_, { op => '+=' }) ); },
99 8     8   9280 '-=' => sub { return( shift->_compute( @_, { op => '-=' }) ); },
100 8     8   9070 '*=' => sub { return( shift->_compute( @_, { op => '*=' }) ); },
101 8     8   9118 '/=' => sub { return( shift->_compute( @_, { op => '/=' }) ); },
102 8     8   60 '++' => sub { return( shift->_compute( @_, { op => '++' }) ); },
103 8     8   55 '--' => sub { return( shift->_compute( @_, { op => '--' }) ); },
104             # We put it here so perl won't trigger the noop overload method
105 16     16   17991 '=' => sub { $_[0] },
106 20         614 'abs' => \&_noop,
107             'nomethod' => \&_noop,
108 20     20   44326469 );
  20         51  
109 20         496 our $VERSION = 'v0.2.0';
110             };
111              
112 20     20   141 use strict;
  20         35  
  20         582  
113 20     20   149 use warnings;
  20         38  
  20         15448  
114              
115             sub init
116             {
117 139     139 1 20652 my $self = shift( @_ );
118 139         1979 $self->{alpha} = undef;
119             # Used for other version types
120 139         379 $self->{beta} = undef;
121 139         337 $self->{compat} = 0;
122             # What version fragment to increase/decrease by default, such as when we do operations like $v++ or $v--
123 139         381 $self->{default_frag} = 'minor';
124 139         393 $self->{extra} = [];
125 139         346 $self->{major} = undef;
126 139         337 $self->{minor} = undef;
127 139         422 $self->{original} = undef;
128 139         357 $self->{padded} = 1;
129 139         328 $self->{patch} = undef;
130 139         281 $self->{pattern} = undef;
131 139         304 $self->{pretty} = 0;
132 139         333 $self->{qv} = 0;
133             # Release candidate used by non-perl open source softwares
134 139         328 $self->{rc} = undef;
135 139         330 $self->{target} = 'perl';
136 139         408 $self->{type} = undef;
137 139         891 my $keys = [qw( alpha beta compat default_frag extra major minor original patch qv rc target type _version )];
138 139         272 my $vstr;
139             # Changes::Version->new( 'v0.1.2_3' ); or
140             # Changes::Version->new( 'v0.1.2_3', alpha => 4 ); or
141             # Changes::Version->new( 'v0.1.2_3', { alpha => 4 } ); or
142             # Changes::Version->new( major => 0, minor => 1, patch => 2, alpha => 3, qv => 1 ); or
143             # Changes::Version->new({ major => 0, minor => 1, patch => 2, alpha => 3, qv => 1 }); or
144 139 100 33     2376 if( ( @_ == 1 && ref( $_[0] ) ne 'HASH' ) ||
      33        
      66        
      33        
      33        
145             ( @_ > 1 && ref( $_[0] ) ne 'HASH' && ( ( @_ % 2 ) || ref( $_[1] ) eq 'HASH' ) ) )
146             {
147 42         120 $vstr = shift( @_ );
148 42 50 33     340 return( $self->error( "version string provided is empty." ) ) if( !defined( $vstr ) || !length( "$vstr" ) );
149             # So we can get options like debug for parser
150 42         271 my $opts = $self->_get_args_as_hash( @_ );
151 42 100 66     6631 $self->debug( $opts->{debug} ) if( exists( $opts->{debug} ) && defined( $opts->{debug} ) && length( "$opts->{debug}" ) );
      66        
152             # A version string was provided, so we parse it
153 42         304 my $v = $self->parse( $vstr );
154 42 50       186 return( $self->pass_error ) if( !defined( $v ) );
155             # And we copy the collected value as default values for our new object, which can then be overriden by additional option passed here.
156 42         891 @$self{ @$keys } = @$v{ @$keys };
157             }
158 139         463 $self->{_init_strict_use_sub} = 1;
159 139         927 my $rv = $self->SUPER::init( @_ );
160 139 50       1394024 return( $self->pass_error ) if( !defined( $rv ) );
161 139         885 return( $self );
162             }
163              
164 419     419 1 42375 sub alpha { return( shift->reset(@_)->_set_get_number( { field => 'alpha', undef_ok => 1 }, @_ ) ); }
165              
166             sub as_string
167             {
168 309     309 1 217377 my $self = shift( @_ );
169 309 50 66     2182 if( !exists( $self->{_reset} ) ||
      33        
170             !defined( $self->{_reset} ) ||
171             !CORE::length( $self->{_reset} ) )
172             {
173 192 100 66     2380 if( exists( $self->{_cache_value} ) &&
    50 66        
      33        
174             defined( $self->{_cache_value} ) &&
175             length( $self->{_cache_value} ) )
176             {
177 68         716 return( $self->{_cache_value} );
178             }
179             elsif( defined( $self->{original} ) && length( "$self->{original}" ) )
180             {
181 124         1566 return( $self->{original}->scalar );
182             }
183             }
184 117         336 my $type = $self->type;
185 117         108437 my $str;
186 117 100 100     994 if( ( defined( $type ) && $type eq 'dotted' ) ||
      66        
      100        
187             ( !defined( $type ) && $DEFAULT_TYPE eq 'dotted' ) )
188             {
189 59         271 $str = $self->normal( raw => 1 );
190             }
191             else
192             {
193 58         1054 my $minor = $self->minor;
194 58         50714 my $patch = $self->patch;
195 58         49537 my $fmt = $self->pattern;
196 58 50 33     49550 if( defined( $fmt ) && length( $fmt ) )
197             {
198 0         0 $str = $self->format( $fmt );
199             }
200             else
201             {
202 58 50 33     357 if( defined( $minor ) &&
      33        
203             (
204             index( $minor, '_' ) != -1 ||
205             ( length( $minor ) == 3 && substr( $minor, 0, 1 ) eq '0' ) ||
206             length( $patch // '' ||
207             $self->padded )
208             ) )
209             {
210 58         24003 $str = $self->numify( raw => 1 );
211 58 100 66     199 if( !$self->padded && index( $str, '_' ) == -1 )
212             {
213 2         1605 return( $str * 1 );
214             }
215            
216 56 100 100     46214 if( $self->pretty && index( $str, '_' ) == -1 && !( length( [split( /\./, $str )]->[1] ) % 3 ) )
      66        
217             {
218             # $str = join( '_', grep{ $_ ne ''} split( /(...)/, $str ) );
219             # Credit: <https://stackoverflow.com/questions/33442240/perl-printf-to-use-commas-as-thousands-separator>
220 3         2394 while( $str =~ s/(\d+)(\d{3})/$1\_$2/ ){};
221             }
222             }
223             else
224             {
225 0         0 my $alpha = $self->alpha;
226 0 0       0 $str = $self->major . ( defined( $minor ) ? ".${minor}" : '' ) . ( defined( $alpha ) ? "_${alpha}" : '' );
    0          
227             }
228             }
229             }
230 115         42507 $self->{_cache_value} = $str;
231 115         331 CORE::delete( $self->{_reset} );
232 115         795 return( $str );
233             }
234              
235             {
236 20     20   170 no warnings 'once';
  20         41  
  20         44441  
237             *stringify = \&as_string;
238             }
239              
240 0     0 1 0 sub beta { return( shift->reset(@_)->_set_get_number( { field => 'beta', undef_ok => 1 }, @_ ) ); }
241              
242             # NOTE: clone() is inherited
243              
244 0     0 1 0 sub compat { return( shift->_set_get_boolean( 'compat', @_ ) ); }
245              
246 0     0 1 0 sub dec { return( shift->_inc_dec( 'dec', @_ ) ); }
247              
248 0     0 1 0 sub dec_alpha { return( shift->_inc_dec( 'dec' => 'alpha', @_ ) ); }
249              
250             # For non-perl open source softwares
251 0     0 1 0 sub dec_beta { return( shift->_inc_dec( 'dec' => 'beta', @_ ) ); }
252              
253 0     0 1 0 sub dec_major { return( shift->_inc_dec( 'dec' => 'major', @_ ) ); }
254              
255 0     0 1 0 sub dec_minor { return( shift->_inc_dec( 'dec' => 'minor', @_ ) ); }
256              
257 0     0 1 0 sub dec_patch { return( shift->_inc_dec( 'dec' => 'patch', @_ ) ); }
258              
259 192     192 1 374198 sub default_frag { return( shift->_set_get_scalar_as_object( 'default_frag', @_ ) ); }
260              
261 387     387 1 172031 sub extra { return( shift->_set_get_array_as_object( 'extra', @_ ) ); }
262              
263             sub format
264             {
265 0     0 1 0 my $self = shift( @_ );
266 0   0     0 my $fmt = shift( @_ ) ||
267             return( $self->error( "No pattern was provided to format this version." ) );
268             my $numify = sub
269             {
270 0   0 0   0 my $sep = shift( @_ ) || '';
271 0         0 my $minor = $self->minor;
272 0         0 my $patch = $self->patch;
273 0 0 0     0 if( defined( $minor ) && length( $minor ) )
    0 0        
274             {
275 0 0 0     0 if( defined( $patch ) && length( $patch ) )
276             {
277 0         0 return( sprintf( "%03d${sep}%03d", ( $minor + 0 ), ( $patch + 0 ) ) );
278             }
279             else
280             {
281 0         0 return( sprintf( "%03d${sep}%03d", ( $minor + 0 ), 0 ) );
282             }
283             }
284             elsif( defined( $patch ) && length( $patch ) )
285             {
286 0         0 return( sprintf( "%03d${sep}%03d", 0, ( $patch + 0 ) ) );
287             }
288 0         0 return( '' );
289 0         0 };
290              
291             my $dotted = sub
292             {
293 0     0   0 my $comp = $self->new_array;
294 0 0       0 if( !$self->extra->is_empty )
295             {
296 0         0 $comp->push( $self->extra->list );
297             }
298 0         0 for( qw( patch minor ) )
299             {
300 0   0     0 $comp->unshift( $self->$_ // 0 );
301             }
302 0 0       0 return( $comp->is_empty ? '' : $comp->map(sub{ 0 + $_ })->join( '.' )->scalar );
  0         0  
303 0         0 };
304              
305             my $map =
306             {
307             # alpha
308 0   0 0   0 'A' => sub{ return( $self->alpha // '' ); },
309             # alpha with leading underscore
310             'a' => sub
311             {
312 0   0 0   0 my $a = $self->alpha // '';
313 0 0       0 return( length( $a ) ? "_${a}" : '' );
314             },
315             # dotted versions like 1.2.3.4.5
316             'D' => sub
317             {
318 0     0   0 my $dots = $dotted->();
319 0 0       0 return( length( $dots ) ? $dots : '' );
320             },
321             # dotted versions with leading dot like .1.2.3.4.5
322             'd' => sub
323             {
324 0     0   0 my $dots = $dotted->();
325 0 0       0 return( length( $dots ) ? ( '.' . $dots ) : '' );
326             },
327             # minor
328 0   0 0   0 'M' => sub{ return( $self->minor // '' ); },
329             # numified without underscore. e.g.: 5.006001 -> 006001
330 0     0   0 'N' => sub{ return( $numify->( '_' ) ); },
331             # numified without underscore and with leading dot: 5.006001 -> .006001
332             'n' => sub
333             {
334 0     0   0 my $num = $numify->( '' );
335 0 0       0 return( length( $num ) ? ( '.' . $num ) : '' );
336             },
337             # patch
338 0   0 0   0 'P' => sub{ return( $self->patch // '' ); },
339             # major; R for release
340 0   0 0   0 'R' => sub{ return( $self->major // '' ); },
341             # numified with underscore. e.g.: 5.006_001 -> 006_001
342 0     0   0 'U' => sub{ return( $numify->( '_' ) ); },
343             # numified with underscore. e.g.: 5.006_001 -> .006_001
344             'u' => sub
345             {
346 0     0   0 my $num = $numify->( '_' );
347 0 0       0 return( length( $num ) ? ( '.' . $num ) : '' );
348             },
349 0         0 };
350 0         0 my $str;
351 0 0 0     0 if( $self->_is_array( $fmt ) )
    0 0        
352             {
353 0         0 foreach my $this ( @$fmt )
354             {
355 0 0       0 $this = substr( $this, 1 ) if( substr( $this, 0, 1 ) eq '%' );
356 0 0       0 if( !exists( $map->{ $this } ) )
357             {
358 0 0       0 warn( "Unknown formatter '$this'" ) if( $self->_is_warnings_enabled );
359 0         0 next;
360             }
361 0         0 $str .= $map->{ $this }->();
362             }
363             }
364             elsif( !ref( $fmt ) || ( ref( $fmt ) && overload::Method( $fmt, '""' ) ) )
365             {
366 0         0 ( $str = "$fmt" ) =~ s
367             {
368             \%([a-zA-Z])
369             }
370 0         0 {
371 0 0       0 my $this = $1;
372             if( exists( $map->{ $this } ) )
373 0         0 {
374             $map->{ $this }->();
375             }
376             else
377 0         0 {
378             "\%${this}";
379             }
380             }gexs;
381             }
382             else
383 0         0 {
384             return( $self->error( "Format must be a string or an array reference of pattern components." ) );
385 0         0 }
386             return( $str );
387             }
388 4     4 1 18  
389             sub inc { return( shift->_inc_dec( 'inc', @_ ) ); }
390 0     0 1 0  
391             sub inc_alpha { return( shift->_inc_dec( 'inc' => 'alpha', @_ ) ); }
392 0     0 1 0  
393             sub inc_beta { return( shift->_inc_dec( 'inc' => 'beta', @_ ) ); }
394 0     0 1 0  
395             sub inc_major { return( shift->_inc_dec( 'inc' => 'major', @_ ) ); }
396 0     0 1 0  
397             sub inc_minor { return( shift->_inc_dec( 'inc' => 'minor', @_ ) ); }
398 0     0 1 0  
399             sub inc_patch { return( shift->_inc_dec( 'inc' => 'patch', @_ ) ); }
400 0 0   0 1 0  
401             sub is_alpha { return( shift->alpha->length > 0 ? 1 : 0 ); }
402 0 0   0 1 0  
403             sub is_qv { return( shift->qv ? 1 : 0 ); }
404 333     333 1 626200  
405             sub major { return( shift->reset(@_)->_set_get_number( { field => 'major', undef_ok => 1 }, @_ ) ); }
406 408     408 1 4288357  
407             sub minor { return( shift->reset(@_)->_set_get_number( { field => 'minor', undef_ok => 1 }, @_ ) ); }
408              
409             sub normal
410 60     60 1 113 {
411 60         232 my $self = shift( @_ );
412 60   100     8128 my $opts = $self->_get_args_as_hash( @_ );
413 60         90 $opts->{raw} //= 0;
414 60 50 33     193 my $v;
  60         111  
  60         107  
  60         228  
  0         0  
  60         107  
  60         181  
  60         115  
415 60     60   87 try
416 60         1948 {
417 60 100       78954 my $clone = $self->clone;
418             if( !$self->qv )
419 1         788 {
420             $clone->qv(1);
421 60 100       49774 }
422             if( $opts->{raw} )
423 59         207 {
424             $v = $clone->_stringify;
425 59         730 # We already did it with stringify, so we return what we got
426             return( $v );
427             }
428             else
429 1         5 {
430 1         979 $clone->type( 'dotted' );
431             return( $clone );
432             }
433 60 0 0     280 }
  0 0 33     0  
  0 0       0  
  60 0       111  
  60 0       127  
  60 0       99  
  60 0       101  
  60 0       222  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 0       0  
  0 50       0  
  0         0  
  0         0  
  0         0  
  60         215  
  0         0  
  60         131  
  0         0  
  0         0  
  60         22477  
  60         279  
  60         153  
  60         184  
  0         0  
  0         0  
  0         0  
  0         0  
434 0     0   0 catch( $e )
435 0         0 {
436 20 0 0 20   179 return( $self->error( "Error normalising version $v: $e" ) );
  20 0 0     44  
  20 0 33     23296  
  0 0 66     0  
  0 0 33     0  
  0 0 33     0  
  0 0 33     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  60 0       196  
  0 0       0  
  60 50       497  
  60 50       327  
  60 50       244  
  0 0       0  
  0 50       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  60         688  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
437             }
438             }
439              
440             sub numify
441 59     59 1 123 {
442 59         269 my $self = shift( @_ );
443 59   100     8332 my $opts = $self->_get_args_as_hash( @_ );
444 59         100 $opts->{raw} //= 0;
445 59 50 33     190 my $v;
  59         97  
  59         89  
  59         276  
  0         0  
  59         117  
  59         170  
  59         138  
446 59     59   98 try
447 59 100       130 {
448             if( $opts->{raw} )
449             {
450             # If alpha is set, such as when we convert a dotted decimal into a decimal, we need to remove it and add it back later, because version mess it up
451 58         1909 # For example: version->parse( '1.0_3' )->normal yields v1.30.0 instead of v1.0.0_3 whereas version->parse( '1.0' )->normal yields correctly v1.0.0
452 58         75515 my $clone = $self->clone;
453 58         50622 my $alpha = $clone->alpha;
454 58         58504 $clone->alpha( undef );
455 58         1570 $v = $clone->_stringify;
456 58 100 66     415 my $str = version->parse( $v )->numify;
457 58         579 $str .= "_${alpha}" if( defined( $alpha ) && length( "$alpha" ) );
458             return( $str );
459             }
460             else
461 1         26 {
462             my $new = $self->clone;
463 1         1191 # This will also remove qv boolean
464 1         987 $new->type( 'decimal' );
465             return( $new );
466             }
467 59 0 0     293 }
  0 0 33     0  
  0 0       0  
  59 0       133  
  59 0       77  
  59 0       102  
  59 0       101  
  59 0       201  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 0       0  
  0 50       0  
  0         0  
  0         0  
  0         0  
  59         249  
  0         0  
  59         112  
  0         0  
  0         0  
  59         22068  
  59         349  
  59         146  
  59         182  
  0         0  
  0         0  
  0         0  
  0         0  
468 0     0   0 catch( $e )
469 0         0 {
470 20 0 0 20   162 return( $self->error( "Error numifying version $v: $e" ) );
  20 0 0     65  
  20 0 33     40728  
  0 0 66     0  
  0 0 33     0  
  0 0 33     0  
  0 0 33     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  59 0       172  
  0 0       0  
  59 50       517  
  59 50       367  
  59 50       267  
  0 0       0  
  0 50       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  59         756  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
471             }
472             }
473 96     96 1 2147249  
474             sub original { return( shift->_set_get_scalar_as_object( 'original', @_ ) ); }
475 86     86 1 895  
476             sub padded { return( shift->reset(@_)->_set_get_boolean( 'padded', @_ ) ); }
477              
478             sub parse
479 96     96 1 83514 {
480 96         282 my $self = shift( @_ );
481 96 50 33     865 my $str = shift( @_ );
482 96 50 33     935 return( $self->error( "No version string was provided." ) ) if( !defined( $str ) || !length( "$str" ) );
483             if( $] >= 5.008_001 && ref( \$str ) eq 'VSTRING' )
484 0         0 {
485 0         0 my $def = { original => $str };
486 0         0 $def->{type} = 'dotted';
487 0         0 $def->{qv} = 1;
  0         0  
488 0         0 my @frags = map{ ord( $_ ) } split( //, $str );
489 0         0 @$def{qw( major minor patch )} = splice( @frags, 0, 3 );
490 0         0 $def->{extra} = \@frags;
491 0         0 $def->{pattern} = '%R%d';
492 0         0 my $new = $self->new( %$def );
493 0         0 $new->{_version} = version->parse( $str );
494             return( $new );
495             }
496 96 50       4657  
497             if( $str =~ /^$VERSION_LAX_REGEX$/ )
498 96         3136 {
499 96         622 my $re = { %+ };
500 96         264 my $def = { original => $str };
501 96 100 66     1120 my $fmt = [];
    50 33        
502             if( defined( $re->{dotted} ) && length( $re->{dotted} ) )
503 13         45 {
504             $def->{type} = 'dotted';
505             }
506             elsif( defined( $re->{decimal} ) && length( $re->{decimal} ) )
507 83         358 {
508             $def->{type} = 'decimal';
509             }
510             else
511 0         0 {
512             return( $self->error( "No version types found. This should not happen." ) );
513 96         204 }
514 96 100 66     513 my $v;
515 96         281 $def->{qv} = 1 if( defined( $re->{has_v} ) && length( $re->{has_v} ) );
516 96 100 66     754 $def->{major} = $re->{major};
517 96 100 66     455 $def->{minor} = $re->{minor} if( defined( $re->{minor} ) && length( $re->{minor} ) );
518 96 100       526 $def->{alpha} = $re->{alpha} if( defined( $re->{alpha} ) && length( $re->{alpha} ) );
    50          
519             if( $def->{type} eq 'dotted' )
520 13         42 {
521 13 100       60 push( @$fmt, '%R' );
522             if( defined( $re->{dotted_numified} ) )
523 1         2 {
524 1         8 $def->{type} = 'decimal';
525             $v = version->parse( $re->{release} );
526 1 50       4 # e.g.: 5.006_001
527             if( defined( $re->{dotted_numified_under} ) )
528 0         0 {
529             push( @$fmt, '%u' );
530             }
531             else
532 1         3 {
533 1 50       5 push( @$fmt, '%n' );
534             push( @$fmt, '%a' ) if( defined( $re->{alpha} ) );
535 1         7 }
536 1 50       151 my $vstr = $v->normal;
537             if( $vstr =~ /^$VERSION_LAX_REGEX$/ )
538 1         23 {
539 1 50 33     12 my $re2 = { %+ };
540             if( defined( $re2->{dotted} ) && length( $re2->{dotted} ) )
541 1 50       3 {
542             if( defined( $re2->{minor_patch} ) )
543             {
544 1         3 # delete( $def->{alpha} );
545 1         4 $def->{major} = $re2->{major};
546 1         2 my @frags = split( /\./, $re2->{minor_patch} );
547 1         3 shift( @frags );
548 1         3 $def->{minor} = shift( @frags );
549 1         5 $def->{patch} = shift( @frags );
550             $def->{extra} = \@frags;
551             }
552             }
553             }
554             }
555             else
556 12         164 {
557             $v = version->parse( $re->{dotted} );
558 12         37 # Same as %M%P%E -> 5.3.4.5.6.7.8
559 12 100 66     62 push( @$fmt, '%d' );
560 12 50       48 push( @$fmt, '%a' ) if( defined( $def->{alpha} ) && length( $def->{alpha} ) );
561             if( defined( $re->{minor_patch} ) )
562 12         67 {
563             my @frags = split( /\./, $re->{minor_patch} );
564 12         34 # throw away the empty data because of the leading dot
565 12         36 shift( @frags );
566 12         29 $def->{minor} = shift( @frags );
567 12         41 $def->{patch} = shift( @frags );
568             $def->{extra} = \@frags;
569             }
570 13         66 }
571             $def->{pattern} = join( '', @$fmt );
572             }
573             elsif( $def->{type} eq 'decimal' )
574             {
575             # $def->{minor} = $re->{minor} if( defined( $re->{minor} ) );
576             # $re->{release} is the decimal version without the alpha information if it is smaller than 3
577             # This issue stems from decimal number having an underscore can either mean they have a version like
578             # 5.006_002 which would be equivalent v5.6.2 and in this case, "_002" is not an alpha information; and
579             # 1.002_03 where 03 is the alpha version and should be converted to 1.2_03, but instead becomes v1.2.30
580 83         299 # If compatibility with 'compat' is enabled, then we use the classic albeit erroneous way of converting the decimal version
581 83 100 66     978 push( @$fmt, '%R' );
582 83         340 push( @$fmt, '%M' ) if( defined( $def->{minor} ) && length( $def->{minor} ) );
583 83 50 33     496 $def->{pattern} = join( '', @$fmt );
      33        
584             if( defined( $def->{alpha} ) &&
585             length( $def->{alpha} ) < 3 &&
586             !$self->compat )
587 0         0 {
588             $v = version->parse( "$re->{release}" );
589             }
590             else
591 83         1035 {
592             $v = version->parse( "$str" );
593             }
594              
595             # if( (
596             # defined( $def->{alpha} ) &&
597             # ( $self->compat || length( $def->{alpha} ) == 3 )
598             # )
599             # ||
600             # ( defined( $def->{minor} ) &&
601             # length( $def->{minor} ) >= 3 &&
602             # substr( $def->{minor}, 0, 1 ) eq '0'
603             # ) )
604             # {
605             # my $vstr = $v->normal;
606             # if( $vstr =~ /^$VERSION_LAX_REGEX$/ )
607             # {
608             # my $re2 = { %+ };
609             # if( defined( $re2->{dotted} ) && length( $re2->{dotted} ) )
610             # {
611             # if( defined( $re2->{minor_patch} ) )
612             # {
613             # # delete( $def->{alpha} );
614             # $def->{major} = $re2->{major};
615             # my @frags = split( /\./, $re2->{minor_patch} );
616             # shift( @frags );
617             # $def->{minor} = shift( @frags );
618             # $def->{patch} = shift( @frags );
619             # $def->{extra} = \@frags;
620             # }
621             # }
622             # }
623             # }
624 96         826 }
625 96 50       1165 my $new = $self->new( %$def );
626 96 50       406 $new->{_version} = $v if( defined( $v ) );
627 96         338 return( $self->pass_error ) if( !defined( $new ) );
628 96         1186 CORE::delete( $new->{_reset} );
629             return( $new );
630             }
631             else
632 0         0 {
633             return( $self->error( "Invalid version '$str'" ) );
634             }
635             }
636 352     352 1 209652  
637             sub patch { return( shift->reset(@_)->_set_get_number( { field => 'patch', undef_ok => 1 }, @_ ) ); }
638 154     154 1 1862277  
639             sub pattern { return( shift->_set_get_scalar( 'pattern', @_ ) ); }
640 57     57 1 188  
641             sub pretty { return( shift->reset(@_)->_set_get_boolean( 'pretty', @_ ) ); }
642 448     448 1 166402  
643             sub qv { return( shift->reset(@_)->_set_get_boolean( 'qv', @_ ) ); }
644 0     0 1 0  
645             sub rc { return( shift->_set_get_scalar_as_object( 'rc', @_ ) ); }
646              
647             sub reset
648 2679     2679 1 3962 {
649 2679 100 33     18529 my $self = shift( @_ );
      100        
650             if( (
651             !exists( $self->{_reset} ) ||
652             !defined( $self->{_reset} ) ||
653             !CORE::length( $self->{_reset} )
654             ) && scalar( @_ ) )
655 170         614 {
656 170 100       684 $self->{_reset} = scalar( @_ );
657             if( defined( $self->{major} ) )
658 73         253 {
659 73 50 33     847 my $str = $self->_stringify;
  73         173  
  73         129  
  73         324  
  0         0  
  73         147  
  73         235  
  73         177  
660 73     73   132 try
661 73         692 {
662 73         528 my $v = version->parse( "$str" );
663             $self->{_version} = $v;
664 73 0 50     414 }
  73 0 33     251  
  73 0       264  
  73 0       179  
  73 0       95  
  73 0       153  
  73 0       145  
  73 0       283  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 0       0  
  0 50       0  
  0         0  
  0         0  
  0         0  
  73         322  
  0         0  
  73         128  
  0         0  
  0         0  
  73         334  
  73         349  
  73         157  
  73         222  
  0         0  
  0         0  
  0         0  
  0         0  
665 0     0   0 catch( $e )
666 0 0       0 {
667 20 0 0 20   169 warn( "Warning only: error trying to get a version object from version string '$str': $e\n" ) if( $self->_warnings_is_enabled );
  20 0 0     59  
  20 0 33     81479  
  0 0 33     0  
  0 0 33     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 33     0  
  0 0 33     0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  73 0       279  
  0 0       0  
  73 0       2105  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 50       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 50       0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  73         285  
  0         0  
  0         0  
  0         0  
  0         0  
  73         263  
668             }
669             }
670 2679         14428 }
671             return( $self );
672             }
673              
674             # Credit: Data::VString
675             sub satisfy
676 7     7 1 14313 {
677 7 50 33     32 my $this = shift( @_ );
678 7         24 my $self = ( __PACKAGE__->_is_object( $this ) && $this->isa( 'Changes::Version' ) ) ? $this : $this->parse( shift( @_ ) );
679             my $predicate = shift( @_ );
680 7         47 # spaces are irrelevant
681 7         37 $predicate =~ s/[[:blank:]\h\v]+//g;
682 7         36 my $vers = $self->_version;
683             my @p = split( ',', $predicate );
684             my $cmp =
685 0     0   0 {
686 1     1   9 '==' => sub{ $_[0] == $_[1] },
687 0     0   0 '!=' => sub{ $_[0] != $_[1] },
688 0     0   0 '<=' => sub{ $_[0] <= $_[1] },
689 1     1   6 '>=' => sub{ $_[0] >= $_[1] },
690 2     2   26 '<' => sub{ $_[0] < $_[1] },
691 7         120 '>' => sub{ $_[0] > $_[1] },
692 7         29 };
693             for( @p )
694 9 100       67 {
695             if( /^(\d+([._]\d+)*)$/ )
696 3 100       56 {
697 2         40 next if( $vers == version->parse( $1 ) );
698             return(0);
699 6 100       38 }
700             if( /^([=!<>]=|[<>])(\d+([._]\d+)*)$/ )
701 4 50       45 {
702 0         0 next if( $cmp->{ $1 }->( $vers, version->parse( $2 ) ) );
703             return(0);
704 2 50       17 }
705             if( /^(\d+([._]\d+)*)\.\.(\d+([._]\d+)*)$/ )
706 2 50 33     67 {
707             if( ( version->parse( $1 ) <= $vers ) &&
708             ( $vers <= version->parse( $3 ) ) )
709 2         12 {
710             next;
711 0         0 }
712             return(0);
713 0         0 }
714             return( $self->error( "Bad predicate '$_'" ) );
715 5         94 }
716             return(1);
717             }
718 0     0 1 0  
719             sub target { return( shift->_set_get_scalar_as_object( 'target', @_ ) ); }
720              
721             sub type { return( shift->reset(@_)->_set_get_scalar_as_object({
722             field => 'type',
723             callbacks =>
724             {
725             add => sub
726 99     99   83120 {
727 99 100       597 my $self = shift( @_ );
    50          
728             if( $self->{type} eq 'decimal' )
729 87         1319 {
730             $self->{qv} = 0;
731             }
732             elsif( $self->{type} eq 'dotted' )
733             {
734 12         311 # By default
735             $self->{qv} = 1;
736             }
737             }
738 492     492 1 1419233 }
739             }, @_ ) ); }
740              
741             sub _bool
742 0     0   0 {
743             my $self = shift( @_ );
744 0         0 # return( $self->_compare( $self->_version, version->new("0"), 1 ) );
745             return( $self->_compare( $self, "0", 1 ) );
746             }
747              
748             sub _bubble
749 0     0   0 {
750 0         0 my $self = shift( @_ );
751 0         0 my $frag = shift( @_ );
752             my $val = shift( @_ );
753 0 0 0     0 # We die, because this is an internal method and those cases should not happen unless this were a design bug
    0          
    0          
754             if( !defined( $frag ) || !length( $frag ) )
755 0         0 {
756             die( "No fragment was provided to cascade" );
757             }
758             elsif( $frag !~ /^(major|minor|patch|alpha|\d+)$/ )
759 0         0 {
760             die( "Unsupported version fragment '$frag'. Only use 'major', 'minor', 'patch' or 'alpha' or a number starting from 1 (1 = major, 2 = minor, etc)." );
761             }
762             # Not for us. We bubble only when a value is negative resulting from a cascading decrease
763             # e.g. 3.12.-1 -> 3.11.0, or 3.0.-1 -> 2.9.0, or 2.-1 -> 1.0
764             elsif( $val >= 0 )
765 0         0 {
766             return;
767 0         0 }
768 0         0 my $type = $self->type;
769 0 0       0 my $extra = $self->extra;
770 0         0 my $frag_is_int = ( $frag =~ /^\d+$/ ? 1 : 0 );
771             my $frag2num =
772             {
773             major => 1,
774             minor => 2,
775             patch => 3,
776 0         0 };
777             my $num2frag =
778             {
779             1 => 'major',
780             2 => 'minor',
781             3 => 'patch',
782             };
783 0 0       0  
784             if( $frag eq 'alpha' )
785 0         0 {
786 0         0 $self->alpha( undef );
787             return;
788 0 0 0     0 }
789 0 0       0 die( "Fragment provided '$frag' cannot be 0." ) if( $frag_is_int && $frag == 0 );
790             my $level = $frag_is_int ? $frag : $frag2num->{ $frag };
791              
792 0 0 0     0 # Should not be happening
793             if( $type eq 'decimal' && $level > 2 )
794 0         0 {
795 0         0 $self->patch( undef );
796 0         0 $self->alpha( undef );
797 0         0 @$extra = ();
798             return;
799             }
800 0         0  
801             for( my $i = $level; $level >= 1; $i-- )
802 0 0       0 {
803             if( $val < 0 )
804 0         0 {
805 0 0       0 my $new_val = 0;
806             unless( $i == 1 )
807 0         0 {
808 0         0 my $up_val;
809 0 0       0 my $j = $i - 1;
810             if( exists( $num2frag->{ $j } ) )
811             {
812 0   0     0 my $coderef = $self->can( $num2frag->{ $j } ) ||
813 0         0 die( "Cannot find reference for method ", $num2frag->{ $j } );
814             $up_val = $coderef->( $self );
815             }
816             else
817 0         0 {
818             $up_val = $extra->[ $j - 4 ];
819             }
820 0   0     0 # Set value for next iteration
821 0 0       0 $val = ( $up_val // 0 ) - 1;
822             $new_val = ( $up_val > 0 ) ? 9 : 0;
823             }
824 0 0       0  
825             if( exists( $num2frag->{ $i } ) )
826             {
827             # my $coderef = $self->can( $num2frag->{ $i } ) ||
828             # die( "Cannot find reference for method ", $num2frag->{ $i } );
829 0         0 # $coderef->( $self, 0 );
830             $self->{ $num2frag->{ $i } } = $new_val;
831             }
832             else
833 0         0 {
834             $extra->[ $i - 4 ] = $new_val;
835             }
836             }
837             else
838 0 0       0 {
839             if( exists( $num2frag->{ $i } ) )
840             {
841             # my $coderef = $self->can( $num2frag->{ $i } ) ||
842             # die( "Cannot find reference for method ", $num2frag->{ $i } );
843 0         0 # $coderef->( $self, 0 );
844             $self->{ $num2frag->{ $i } } = $val;
845             }
846             else
847 0         0 {
848             $extra->[ $i - 4 ] = $val;
849 0         0 }
850             last;
851             }
852 0         0 }
853             $self->_cascade( $level );
854             }
855              
856             sub _cascade
857 84     84   265 {
858 84         177 my $self = shift( @_ );
859             my $frag = shift( @_ );
860 84 50 33     576 # We die, because this is an internal method and those cases should not happen unless this were a design bug
    50          
861             if( !defined( $frag ) || !length( $frag ) )
862 0         0 {
863             die( "No fragment was provided to cascade" );
864             }
865             elsif( $frag !~ /^(major|minor|patch|alpha|\d+)$/ )
866 0         0 {
867             die( "Unsupported version fragment '$frag'. Only use 'major', 'minor', 'patch' or 'alpha' or a number starting from 1 (1 = major, 2 = minor, etc)." );
868 84         1657 }
869 84         77606 my $type = $self->type;
870 84 50       72807 my $extra = $self->extra;
871 84 100 33     1020 my $frag_is_int = ( $frag =~ /^\d+$/ ? 1 : 0 );
    100 66        
    100 33        
    50 66        
    0 33        
      66        
      0        
872             if( $frag eq 'major' || ( $frag_is_int && $frag == 1 ) )
873 21         354 {
874 21         23951 $self->alpha( undef );
875             $self->patch(0);
876 21         856386 # $self->patch( $type eq 'decimal' ? undef : 0 );
877             $self->minor(0);
878             }
879             elsif( $frag eq 'minor' || ( $frag_is_int && $frag == 2 ) )
880 21         591 {
881 21         23661 $self->alpha( undef );
882             $self->patch(0);
883             # $self->patch( $type eq 'decimal' ? undef : 0 );
884             }
885             elsif( $frag eq 'patch' || ( $frag_is_int && $frag == 3 ) )
886 21         810 {
887             $self->alpha( undef );
888             }
889             elsif( $frag eq 'alpha' )
890             {
891             # Nothing to do
892             }
893             elsif( $type eq 'dotted' && $frag_is_int )
894 0         0 {
895 0         0 my $offset = ( $frag - 4 );
896             my $len = $extra->length;
897             # Before the fragment offset, we set the value to 0 if it is undefined or empty, and
898 0 0       0 # after the fragment offset everything else is reset to 0
899             for( my $i = 0; $i < ( $offset < $len ? $len : $offset ); $i++ )
900 0 0 0     0 {
      0        
      0        
901             if( (
902             $i < $offset &&
903             ( !defined( $extra->[$i] ) || !length( $extra->[$i] ) )
904             ) || $i > $offset )
905 0         0 {
906             $extra->[$i] = 0;
907             }
908 0         0 }
909             $self->alpha( undef );
910             }
911             }
912              
913             sub _compare
914 40     40   25167 {
915 40         150 my( $left, $right, $swap ) = @_;
916 40 50       304 my $class = ref( $left );
917             unless( $left->_is_a( $right => $class ) )
918 40         978 {
919             $right = $class->new( $right, debug => $left->debug );
920             }
921 40 50       400  
922             if( $swap )
923 0         0 {
924             ( $left, $right ) = ( $right, $left );
925             }
926 40 50       242
927             unless( _verify( $left ) )
928 0 0       0 {
929             die( "Invalid version ", ( $swap ? 'format' : 'object ' . overload::StrVal( $left ) ), "." );
930 40 50       142 }
931             unless( _verify( $right ) )
932 0 0       0 {
933             die( "Invalid version ", ( $swap ? 'format' : 'object' . overload::StrVal( $right ) ), "." );
934 40         246 }
935 40         117 my $lv = $left->_version;
936             my $rv = $right->_version;
937             # TODO: better compare version. perl's version fails at comparing version that have alpha.
938             # For example, the documentation states:
939             # Note that "alpha" version objects (where the version string contains a trailing underscore segment) compare as less than the equivalent version without an underscore:
940             # $bool = version->parse("1.23_45") < version->parse("1.2345"); # TRUE
941             # However, this is not true. The above doc example will yield FALSE, not TRUE, and even the following too:
942             # perl -Mversion -lE 'my $v = version->parse("v1.2.3"); my $v2 = version->parse("v1.2.3_4"); say $v > $v2'
943             # See RT#145290: <https://rt.cpan.org/Ticket/Display.html?id=145290>
944             # return( $left->{_version} == $right->{_version} );
945 40         550 # return( $lv == $rv );
946             return( $lv <=> $rv );
947             }
948              
949             sub _compute
950 96     96   238 {
951 96         264 my $self = shift( @_ );
952 96         316 my $opts = pop( @_ );
953 96   50     257 my( $other, $swap, $nomethod, $bitwise ) = @_;
954 96 50       89237 my $frag = $self->default_frag // 'minor';
955 96 50 33     2464 $frag = 'minor' if( $frag !~ /^(major|minor|patch|alpha|\d+)$/ );
      33        
      33        
      33        
956             if( !defined( $opts ) ||
957             ref( $opts ) ne 'HASH' ||
958             !exists( $opts->{op} ) ||
959             !defined( $opts->{op} ) ||
960             !length( $opts->{op} ) )
961 0         0 {
962             die( "No argument 'op' provided" );
963 96         237 }
964 96         2411 my $op = $opts->{op};
965 96         121428 my $clone = $self->clone;
966 96 50       86322 my $extra = $self->extra;
967 96         1203 my $frag_is_int = ( $frag =~ /^\d+$/ ? 1 : 0 );
968             my $map =
969             {
970             1 => 'major',
971             2 => 'minor',
972             3 => 'patch',
973 96         161 };
974 96 50 33     764 my $coderef;
      33        
975             if( ( $frag_is_int && exists( $map->{ $frag } ) ) || !$frag_is_int )
976             {
977 96   50     272 $coderef = $self->can( $map->{ $frag } // $frag ) ||
978             die( "Cannot find code reference for method ", ( $frag_is_int ? $map->{ $frag } : $frag ) );
979 96 50       1975 }
980 96         83835 my $val = defined( $coderef ) ? $coderef->( $self ) : $extra->[ $frag - 4 ];
981 96 50       628 my $err;
    50          
982             if( !defined( $val ) )
983 0         0 {
984             $val = $self->new_number(0);
985             }
986             elsif( !$self->_is_a( $val => 'Module::Generic::Number' ) )
987 0         0 {
988 0 0       0 $val = $self->new_number( "$val" );
989             if( !defined( $val ) )
990 0         0 {
991             $err = $self->error->message;
992             }
993 96         4205 }
994 96         749 my $n = $val->scalar;
995 96 100 100     598 my $eval;
996             if( $op eq '++' || $op eq '--' )
997 16         43 {
998             $eval = "\$n${op}";
999             }
1000             else
1001 80 50       456 {
    50          
    100          
1002             $eval = $swap ? ( defined( $other ) ? $other : 'undef' ) . "${op} \$n" : "\$n ${op} " . ( defined( $other ) ? $other : 'undef' );
1003 96         6952 }
1004 96 50       629 my $rv = eval( $eval );
1005 96 50       316 $err = $@ if( $@ );
1006             if( defined( $err ) )
1007 0 0       0 {
1008             warn( $err, "\n" ) if( $self->_warnings_is_enabled );
1009             # Return unchanged
1010 0         0 # return( $swap ? $other : $self );
1011             return;
1012             }
1013 96 100       364
1014             if( $swap )
1015 16 50       129 {
1016             return( ref( $rv ) ? $rv->scalar : $rv );
1017             }
1018             else
1019 80         164 {
1020 80         149 my $new = $clone;
1021 80 100 100     449 my $new_val;
1022             if( $op eq '++' || $op eq '--' )
1023 16         36 {
1024 16         40 $new = $self;
1025             $new_val = $n;
1026             }
1027             else
1028 64         155 {
1029             $new_val = int( $rv );
1030             }
1031 80 50       280
1032             if( $new_val < 0 )
1033 0         0 {
1034             $new->_bubble( $frag, $new_val );
1035             }
1036             else
1037 80 50       210 {
1038             if( defined( $coderef ) )
1039 80         230 {
1040             $coderef->( $new, $new_val );
1041             }
1042             else
1043 0         0 {
1044             $extra->[( $frag - 4 )] = $new_val;
1045 80         3273520 }
1046             $new->_cascade( $frag );
1047 80         1656718 }
1048 80         1037 $new->reset(1);
1049             return( $new );
1050             }
1051             }
1052              
1053             sub _inc_dec
1054 4     4   7 {
1055 4   50     15 my $self = shift( @_ );
1056 4 50       26 my $op = shift( @_ ) || return( $self->error( "No op was provided." ) );
1057 4         8 return( $self->error( "Op can only be 'inc' or 'dec'" ) ) if( $op !~ /^(inc|dec)$/ );
1058 4         6 my $frag = shift( @_ );
1059 4 50 33     41 my $unit = shift( @_ );
    50          
1060             if( !defined( $frag ) || !length( "$frag" ) )
1061 0 0       0 {
1062             return( $self->error( "No version fragment was specified to ", ( $op eq 'inc' ? 'increase' : 'decrease' ), " the version number." ) );
1063             }
1064             elsif( $frag !~ /^(major|minor|patch|alpha|\d+)$/ )
1065 0 0       0 {
1066             return( $self->error( "Unsupported version fragment '$frag' to ", ( $op eq 'inc' ? 'increase' : 'decrease' ), ". Only use 'major', 'minor', 'patch' or 'alpha' or a number starting from 1 (1 = major, 2 = minor, etc)." ) );
1067 4 50 33     22 }
1068             if( defined( $unit ) && $unit !~ /^\d+$/ )
1069 0 0       0 {
1070             return( $self->error( "Unit to ", ( $op eq 'inc' ? 'increase' : 'decrease' ), " fragment $frag value must be an integer." ) );
1071 4         37 }
1072 4 50       3524 my $extra = $self->extra;
1073 4         20 my $frag_is_int = ( $frag =~ /^\d+$/ ? 1 : 0 );
1074             my $map =
1075             {
1076             1 => 'major',
1077             2 => 'minor',
1078             3 => 'patch',
1079 4         6 };
1080 4 50 33     24 my $coderef;
      33        
1081             if( ( $frag_is_int && exists( $map->{ $frag } ) ) || !$frag_is_int )
1082             {
1083 4   50     30 $coderef = $self->can( $map->{ $frag } // $frag ) ||
1084             die( "Cannot find code reference for method ", ( $frag_is_int ? $map->{ $frag } : $frag ) );
1085 4 50       42 }
1086             my $n = defined( $coderef ) ? $coderef->( $self ) : $extra->[ $frag - 4 ];
1087 4 50 33     3504 # The offset specified is out of bound
    50 33        
1088             if( $frag_is_int && ( $frag - 4 ) > $extra->size )
1089 0 0       0 {
    0          
1090             $n = (
1091             $op eq 'inc'
1092             ? ( defined( $unit ) ? $unit : 1 )
1093             : 0
1094             );
1095             }
1096             elsif( defined( $unit ) && $unit == 1 )
1097 0 0       0 {
1098             $op eq 'inc' ? ( $n += $unit ) : ( $n -= $unit );
1099             }
1100             else
1101 4 50       19 {
1102             $op eq 'inc' ? $n++ : $n--;
1103             }
1104 4 50       32
1105             if( defined( $coderef ) )
1106 4         7 {
1107             $coderef->( $self, $n );
1108             }
1109             else
1110 0         0 {
1111             $extra->[( $frag - 4 )] = $n;
1112 4         161865 }
1113 4         82365 $self->_cascade( $frag );
1114 4         20 $self->reset(1);
1115             return( $self );
1116             }
1117              
1118             sub _noop
1119 0     0   0 {
1120 0 0       0 my( $self, $other, $swap, $nomethod, $bitwise ) = @_;
1121             warn( "This operation $nomethod is not supported by Changes::Version\n" ) if( $self->_warnings_is_enabled );
1122             }
1123              
1124             sub _stringify
1125 190     190   378 {
1126 190         503 my $self = shift( @_ );
1127 190         3415 my $comp = $self->new_array;
1128 190         510 my $def = {};
1129             for( qw( major minor patch alpha ) )
1130 760         493619 {
1131             $def->{ $_ } = $self->$_;
1132 190         161671 }
1133 190 50 33     174025 my $type = $self->type;
1134 190 100 50     1756 $def->{major} = 0 if( !defined( $def->{major} ) || !length( $def->{major} ) );
    50 66        
      50        
1135             if( $self->qv || ( ( $type // '' ) eq 'dotted' ) )
1136 94 50 33     74608 {
1137 94 50 33     973 $def->{minor} = 0 if( !defined( $def->{minor} ) || !length( "$def->{minor}" ) );
1138             $def->{patch} = 0 if( !defined( $def->{patch} ) || !length( "$def->{patch}" ) );
1139             }
1140             elsif( ( $type // '' ) eq 'decimal' )
1141             {
1142             # We need to avoid the scenario where we would have a major and alpha, but not minor.
1143 96 0 33     78189 # For example: 3_6 would trigger version error "Invalid version format (alpha without decimal)"
      33        
      33        
1144             $def->{minor} = 0 if( ( !defined( $def->{minor} ) || !length( "$def->{minor}" ) ) && defined( $def->{alpha} ) && length( "$def->{alpha}" ) );
1145 190         1539 }
1146 190 50       623 my $ok = 0;
1147             if( !$self->extra->is_empty )
1148 0         0 {
1149 0         0 $ok++;
1150             $comp->push( $self->extra->list );
1151 190         117682 }
1152             for( qw( patch minor major ) )
1153 570 50 66     3158 {
1154             next if( !length( $def->{ $_ } ) && !$ok );
1155 567         2303 # We stop skipping version fragments as soon as one is defined
1156 567         1249 $ok++;
1157             $comp->unshift( $def->{ $_ } );
1158 190 100   567   1486 }
  567         565465  
1159 190 100 66     225049 my $v = ( $self->qv ? 'v' : '' ) . $comp->map(sub{ 0 + $_ })->join( '.' )->scalar;
1160 190         65352 $v .= '_' . $def->{alpha} if( defined( $def->{alpha} ) && length( $def->{alpha} ) );
1161             return( $v );
1162             }
1163              
1164             sub _verify
1165 80     80   157 {
1166 80 50 33     480 my $self = shift( @_ );
      33        
      33        
1167             if( defined( $self ) &&
1168 80         3313 Module::Generic->_is_a( $self => 'Changes::Version' ) &&
1169             eval{ exists( $self->{_version} ) } &&
1170             Module::Generic->_is_a( $self->{_version} => 'version' ) )
1171 80         2641 {
1172             return(1);
1173             }
1174             else
1175 0         0 {
1176             return(0);
1177             }
1178             }
1179              
1180             sub _version
1181 87     87   192 {
1182 87 50 33     659 my $self = shift( @_ );
    50          
1183             if( @_ )
1184 0         0 {
1185 0 0       0 my $v = shift( @_ );
1186             return( $self->error( "Value provided is not a version object." ) ) if( !$self->_is_a( $v => 'version' ) );
1187             }
1188             elsif( !exists( $self->{_version} ) || !defined( $self->{_version} ) )
1189 0         0 {
1190 0 0 0     0 my $str = $self->_stringify;
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
1191 0     0   0 try
1192 0         0 {
1193             $self->{_version} = version->parse( "$str" );
1194 0 0 0     0 }
  0 0 0     0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
1195 0     0   0 catch( $e )
1196 0 0       0 {
1197 20 0 0 20   190 warn( "Warning only: error trying to get a version object from version string '$str': $e\n" ) if( $self->_warnings_is_enabled );
  20 0 0     42  
  20 0 0     4615  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0 0     0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
1198             }
1199 87         239 }
1200             return( $self->{_version} );
1201             }
1202              
1203             1;
1204             # NOTE: POD
1205             __END__
1206              
1207             =encoding utf-8
1208              
1209             =head1 NAME
1210              
1211             Changes::Version - Version string object class
1212              
1213             =head1 SYNOPSIS
1214              
1215             use Changes::Version;
1216             my $v = Changes::Version->new(
1217             major => 1,
1218             minor => 2,
1219             patch => 3,
1220             alpha => 4,
1221             qv => 1,
1222             debug => 2,
1223             );
1224             # or
1225             my $v = Changes::Version->new( 'v0.1.2_3' );
1226             # or
1227             my $v = Changes::Version->new( 'v0.1.2_3', alpha => 4 );
1228             # or
1229             my $v = Changes::Version->new( 'v0.1.2_3', { alpha => 4 } );
1230             # or
1231             my $v = Changes::Version->new( major => 0, minor => 1, patch => 2, alpha => 3, qv => 1 );
1232             # or
1233             my $v = Changes::Version->new({ major => 0, minor => 1, patch => 2, alpha => 3, qv => 1 });
1234             die( Changes::Version->error ) if( !defined( $v ) );
1235             my $v = Changes::Version->parse( 'v1.2.3_4' );
1236             die( Changes::Version->error ) if( !defined( $v ) );
1237             my $type = $v->type;
1238             $v->type( 'decimal' );
1239             $v->padded(0);
1240             $v->pretty(1);
1241             $v->type( 'dotted' );
1242             $v++;
1243             # Updating 'minor'
1244             say "$v"; # v1.3.0
1245             $v += 2;
1246             $v->default_frag( 'major' );
1247             $v++;
1248             say "$v"; # v2.0.0
1249             $v->inc_patch;
1250             say $v->is_alpha; # false
1251             say $v->numify; # returns new Changes::Version object
1252             say $v->normal; # returns new Changes::Version object
1253             say $v->as_string; # same as say "$v";
1254             # 5.0.6_2
1255             say $v->format( "%R%d%A" );
1256              
1257             =head1 VERSION
1258              
1259             v0.2.0
1260              
1261             =head1 DESCRIPTION
1262              
1263             This class represents a software version based on perl's definition and providing for perl recommended C<dotted decimal> and also C<decimal> types. In the future, this will be expanded to other non-perl version formats.
1264              
1265             It allows for parsing and manipulation of version objects.
1266              
1267             =head1 CONSTRUCTOR
1268              
1269             =head2 new
1270              
1271             Provided with an optional version string and an optional hash or hash reference of options and this will instantiate a new L<Changes::Version> object.
1272              
1273             If an error occurs, it will return an L<error|Module::Generic/error>, so alway check for the definedness of the returned value.
1274              
1275             my $v = Changes::Version->new(
1276             major => 1,
1277             minor => 2,
1278             patch => 3,
1279             alpha => 4,
1280             );
1281             die( Changes::Version->error ) if( !defined( $v ) );
1282              
1283             Note that if you do:
1284              
1285             my $v = Changes::Version->new( ... ) || die( Changes::Version->error );
1286              
1287             would be dangerous, because you would be assessing the return version object in a boolean context that could return false if the version was C<0>.
1288              
1289             It supports the following options that can also be accessed or changed with their corresponding method.
1290              
1291             =over 4
1292              
1293             =item * C<alpha>
1294              
1295             Specifies the alpha fragment integer of the version. See L</alpha> for more information.
1296              
1297             my $v = Changes::Version->new(
1298             major => 1,
1299             minor => 2,
1300             patch => 3,
1301             alpha => 4,
1302             );
1303             my $alpha = $v->alpha; # 4
1304             $v->alpha(7);
1305             say "$v"; # v1.2.3_7
1306              
1307             =item * C<beta>
1308              
1309             Specifies the beta fragment integer of the version. See L</beta> for more information.
1310              
1311             Currently unused and reserved for future release.
1312              
1313             =item * C<compat>
1314              
1315             Boolean. When enabled, this will ensure the version formatting is strictly compliant with the L<version> module. Default to false.
1316              
1317             =item * C<default_frag>
1318              
1319             Specifies the fragment name or integer value used by overloaded operations.
1320              
1321             my $v = Changes::Version->new( 'v1.2.3_4' );
1322             my $default = $v->default_frag; # By default 'minor'
1323             $v->default_frag( 'major' );
1324             $v++; # Version is now v2.2.3_4
1325              
1326             =item * C<extra>
1327              
1328             Specifies the array reference of version fragments beyond C<patch>
1329              
1330             my $v = Changes::Version->new(
1331             major => 1,
1332             minor => 2,
1333             patch => 3,
1334             alpha => 12,
1335             extra => [qw( 4 5 6 7 )],
1336             );
1337             say "$v"; # v1.2.3.4.5.6.7_12
1338             my $a = $v->extra; # contains 4, 5, 6, 7
1339              
1340             =item * C<major>
1341              
1342             Specifies the C<major> fragment of the version string.
1343              
1344             my $v = Changes::Version->new(
1345             major => 1,
1346             minor => 2,
1347             patch => 3,
1348             alpha => 4,
1349             );
1350             my $major = $v->major; # 1
1351             say "$v"; # v1.2.3_4
1352             $v->major(3);
1353             say "$v"; # v3.0.0
1354              
1355             =item * C<minor>
1356              
1357             Specifies the C<minor> fragment of the version string.
1358              
1359             my $v = Changes::Version->new(
1360             major => 1,
1361             minor => 2,
1362             patch => 3,
1363             alpha => 4,
1364             );
1365             my $minor = $v->minor; # 2
1366             say "$v"; # v1.2.3_4
1367             $v->minor(3);
1368             say "$v"; # v1.3.0
1369              
1370             =item * C<original>
1371              
1372             Specifies an original version string. This is normally set by L</parse> and used by L</as_string> to bypass any formatting when nothing has been changed.
1373              
1374             =item * C<padded>
1375              
1376             Specifies whether version string of type decimal should be zero padded or not. Default to true.
1377              
1378             my $v = Change::Version->new(
1379             major => 1,
1380             minor => 20,
1381             patch => 300,
1382             type => 'decimal',
1383             );
1384             say "$v"; # 1.020300
1385             $v->padded(0);
1386             say "$v"; # 1.0203
1387              
1388             =item * C<patch>
1389              
1390             Specifies the C<patch> fragment of the version string.
1391              
1392             my $v = Changes::Version->new(
1393             major => 1,
1394             minor => 2,
1395             patch => 3,
1396             alpha => 4,
1397             );
1398             my $patch = $v->patch; # 3
1399             say "$v"; # v1.2.3_4
1400             $v->patch(7);
1401             say "$v"; # v1.3.7
1402              
1403             =item * C<pretty>
1404              
1405             Specifies whether version string of type C<decimal> should be formatted with an underscore (C<_>) separating thousands in the fraction part.
1406              
1407             my $v = Change::Version->new(
1408             major => 1,
1409             minor => 20,
1410             patch => 300,
1411             type => 'decimal',
1412             pretty => 1,
1413             );
1414             say "$v"; # 1.020_300
1415             $v->pretty(0);
1416             say "$v"; # 1.020300
1417              
1418             =item * C<qv>
1419              
1420             Specifies whether version string of type C<dotted> should be formatted with the prefix C<v>. Defaults to true.
1421              
1422             my $v = Changes::Version->new(
1423             major => 1,
1424             minor => 2,
1425             patch => 3,
1426             alpha => 4,
1427             );
1428             say "$v"; # v1.2.3_4
1429             $v->qv(0);
1430             say "$v"; # 1.2.3_4
1431              
1432             =item * C<rc>
1433              
1434             Specifies the release candidate value. This is currently unused and reserved for future release.
1435              
1436             =item * C<target>
1437              
1438             Specifies the target formatting for the version string. By default this is C<perl> and is the only supported value for now. In future release, other format types will be supported, such as C<opensource>.
1439              
1440             =item * C<type>
1441              
1442             Specifies the version type. Possible values are C<dotted> for dotted decimal versions such as C<v1.2.3> or C<decimal> for decimal versions such as C<1.002003>
1443              
1444             =back
1445              
1446             =head2 parse
1447              
1448             Provided with a version string, and this will parse it and return a new L<Changes::Version> object.
1449              
1450             Currently, only 2 version types are supported: C<dotted decimal> and C<decimal>
1451              
1452             v1.2
1453             1.2345.6
1454             v1.23_4
1455             1.2345
1456             1.2345_01
1457              
1458             are all legitimate version strings.
1459              
1460             If an error occurred, this will return an L<error|Module::Generic/error>.
1461              
1462             =head1 METHODS
1463              
1464             =head2 alpha
1465              
1466             Sets or gets the C<alpha> fragment integer of the version.
1467              
1468             Setting this to C<undef> effectively removes it.
1469              
1470             Returns a L<number object|Module::Generic::Number>
1471              
1472             =head2 as_string
1473              
1474             Returns a version string properly formatted according to the C<type> set with L</type> and other parameters sets such as L</qv>, L</padded> and L</pretty>
1475              
1476             Resulting value is cached, which means the second time this is called, the cached value will be returned for speed.
1477              
1478             Any change to the version object parameters, and this will force the re-formatting of the version string.
1479              
1480             For example:
1481              
1482             my $v = Changes::Version->new( 'v1.2.3_4' );
1483             # This is a version of type 'dotted' for dotted decimal
1484             say "$v"; # v1.2.3_4
1485             # Changing the patch level
1486             $v->inc( 'patch' );
1487             # Now forced to re-format
1488             say "$v"; # v1.2.4
1489             # No change, using the cache
1490             say "$v"; # v1.2.4
1491              
1492             =head2 beta
1493              
1494             The beta fragment integer of the version. This is currently unused and reserved for future release of this class.
1495              
1496             =head2 compat
1497              
1498             Boolean. When enabled, this will ensure the version formatting is strictly compliant with the L<version> module. Default to false.
1499              
1500             =head2 dec
1501              
1502             Provided with a version fragment, and an optiona integer, and this will decrease the version fragment value by as much. If no integer is provided, the default decrement is 1.
1503              
1504             my $v = Changes::Version->new(
1505             major => 1,
1506             minor => 2,
1507             patch => 3,
1508             alpha => 4,
1509             );
1510             say "$v"; # v1.2.3_4;
1511             $v->dec( 'alpha' );
1512             say "$v"; # v1.2.3_3;
1513             $v->dec( 'patch', 2 );
1514             say "$v"; # v1.2.1
1515              
1516             my $v = Changes::Version->new( 'v1.2.3.4.5.6.7_8' );
1517             # Decrease the 5th fragment
1518             $v->dec(5);
1519             say "$v"; # v1.2.3.4.4.0.0
1520              
1521             Any change to a fragment value will reset the lower fragment values to zero. Thus:
1522              
1523             =over 4
1524              
1525             =item * changing the C<major> value will reset C<minor> and C<patch> to 0 and C<alpha> to C<undef>
1526              
1527             =item * changing the C<minor> value will reset C<patch> to 0 and C<alpha> to C<undef>
1528              
1529             =item * changing the C<patch> value will reset C<alpha> to C<undef>
1530              
1531             =item * changing the nth fragment value will reset all fragment value after that to 0
1532              
1533             =back
1534              
1535             If you pass a fragment that is an integer and it is outside the maximum number of fragments, it will automatically expand the number of version fragments and initialise the intermediary fragments to 0. A fragment as an integer starts at 1.
1536              
1537             Using the example above:
1538              
1539             $v->dec(10);
1540             say "$v"; # v1.2.3.4.5.6.7.0.0.0
1541              
1542             The 10th element is set to 0 because it does not exist, so it cannot be decreased.
1543              
1544             =head2 dec_alpha
1545              
1546             This is a shortcut for calling L</dec> on fragment C<alpha>
1547              
1548             =head2 dec_beta
1549              
1550             This is a shortcut for calling L</dec> on fragment C<beta>
1551              
1552             =head2 dec_major
1553              
1554             This is a shortcut for calling L</dec> on fragment C<major>
1555              
1556             =head2 dec_minor
1557              
1558             This is a shortcut for calling L</dec> on fragment C<minor>
1559              
1560             =head2 dec_patch
1561              
1562             This is a shortcut for calling L</dec> on fragment C<patch>
1563              
1564             =head2 default_frag
1565              
1566             my $v = Changes::Version->new( 'v1.2.3_4' );
1567             my $default = $v->default_frag; # By default 'minor'
1568             $v->default_frag( 'major' );
1569             $v++; # Version is now v2.2.3_4
1570              
1571             String. Sets or gets the name or the integer value for the version fragment. Supported value can be C<major>, C<minor>. C<patch>, C<alpha>, or an integer.
1572              
1573             Returns a L<scalar object|Module::Generic::Scalar>
1574              
1575             =head2 extra
1576              
1577             Sets or gets an array reference of version fragments starting from C<1> for C<major>, C<2> for C<minor>, C<3> for C<patch>, etc. For example:
1578              
1579             my $v = Changes::Version->new( 'v1.2.3.4.5.6.7_8' );
1580             my $a = $v->extra; # contains 4, 5, 6, 7
1581              
1582             Note that C<alpha> is not accessible via digits, but only using L</alpha>
1583              
1584             You should not be accessing this directly.
1585              
1586             Returns an L<array object|Module::Generic::Array>
1587              
1588             =head2 format
1589              
1590             my $v = Changes::Version->parse( "5.0.6_2" );
1591             say $v->format( "%R%d" ); # 5.0.6
1592              
1593             This formats the version string. It takes a string representing a pattern, or an array reference of pattern elements and returns a regular string.
1594              
1595             If an error occurred, it sets an L<error object|Module::Generic::Exception> and returns C<undef> in scalar context, or an empty list in list context.
1596              
1597             See also L</pattern> to get or set a pattern used by L</as_string>
1598              
1599             See also below the L<possible patterns|/"PATTERNS">
1600              
1601             =head2 inc
1602              
1603             Same as L</dec>, but increasing instead of decreasing.
1604              
1605             =head2 inc_alpha
1606              
1607             This is a shortcut for calling L</inc> on fragment C<alpha>
1608              
1609             =head2 inc_beta
1610              
1611             This is a shortcut for calling L</inc> on fragment C<beta>
1612              
1613             =head2 inc_major
1614              
1615             This is a shortcut for calling L</inc> on fragment C<major>
1616              
1617             =head2 inc_minor
1618              
1619             This is a shortcut for calling L</inc> on fragment C<minor>
1620              
1621             =head2 inc_patch
1622              
1623             This is a shortcut for calling L</inc> on fragment C<patch>
1624              
1625             =head2 is_alpha
1626              
1627             Returns true if L</alpha> has a value set.
1628              
1629             =head2 is_qv
1630              
1631             Returns true if L</qv> is set to true, false otherwise.
1632              
1633             =head2 major
1634              
1635             Sets or gets the C<major> fragment of the version string.
1636              
1637             my $v = Changes::Version->new( 'v1.2.3_4' );
1638             my $major = $v->major; # 1
1639             $v->major(3);
1640             say "$v"; # v3.2.3_4
1641              
1642             Setting this to C<undef> effectively removes it.
1643              
1644             Returns a L<number object|Module::Generic::Number>
1645              
1646             =head2 minor
1647              
1648             Sets or gets the C<minor> fragment of the version string.
1649              
1650             my $v = Changes::Version->new( 'v1.2.3_4' );
1651             my $minor = $v->minor; # 2
1652             $v->minor(3);
1653             say "$v"; # v1.3.3_4
1654              
1655             Setting this to C<undef> effectively removes it.
1656              
1657             Returns a L<number object|Module::Generic::Number>
1658              
1659             =head2 normal
1660              
1661             Returns a new L<Changes::Version> object as a normalised version, which is a dotted decimal format with the C<v> prefix.
1662              
1663             If an error occurred, an L<error|Module::Generic/error> is returned.
1664              
1665             =head2 numify
1666              
1667             Returns a new L<Changes::Version> object as a number, which represent a decimal-type version
1668              
1669             Contrary to L<version> if there is an C<alpha> value set, it will add it to the numified version.
1670              
1671             my $v = Changes::Version->new(
1672             major => 1,
1673             minor => 2,
1674             patch => 3,
1675             alpha => 4,
1676             );
1677             say $v->numify; # 1.002003_4
1678              
1679             L<version> would yields a different, albeit wrong result:
1680              
1681             perl -Mversion -lE 'say version->parse("v1.2.3_4")->numify'
1682              
1683             would wrongly return C<1.002034> and not C<1.002003_4>
1684              
1685             perl -Mversion -lE 'say version->parse("1.002034")->normal'
1686              
1687             then yields C<v1.2.34>
1688              
1689             If an error occurred, an L<error|Module::Generic/error> is returned.
1690              
1691             =head2 original
1692              
1693             Sets or gets the original string. This is set by L</parse>
1694              
1695             Returns a L<scalar object|Module::Generic::Scalar>
1696              
1697             =head2 padded
1698              
1699             Boolean. Sets or ges whether the resulting version string of type C<decimal> should be '0' padded or not. Default to pad with zeroes decimal numbers.
1700              
1701             For example:
1702              
1703             my $v = Changes::Version->new(
1704             major => 1,
1705             minor => 2,
1706             patch => 30,
1707             type => 'decimal',
1708             padded => 1,
1709             );
1710             say "$v"; # 1.002030
1711             $v->padded(0);
1712             say "$v"; # 1.00203
1713              
1714             Returns a L<boolean object|Module::Generic::Boolean>
1715              
1716             =head2 patch
1717              
1718             Sets or gets the C<patch> fragment of the version string.
1719              
1720             my $v = Changes::Version->new( 'v1.2.3_4' );
1721             my $patch = $v->patch; # 3
1722             $v->patch(5);
1723             say "$v"; # v1.3.5_4
1724              
1725             Returns a L<number object|Module::Generic::Number>
1726              
1727             =head2 pattern
1728              
1729             Sets or gets a format pattern. This returns a regular string, or C<undef> if no pattern has been set.
1730              
1731             See also the L<list of patterns|/"PATTERNS">
1732              
1733             =head2 pretty
1734              
1735             Boolean. When enabled, this will render version number for decimal type a bit cleaner by separating blocks of 3 digits by an underscore (C<_>). This does not work on dotted decimal version numbers such as C<v1.2.3> or on version that have an C<alpha> set up.
1736              
1737             my $v = Changes::Version->new(
1738             major => 1,
1739             minor => 2,
1740             patch => 30,
1741             type => 'decimal',
1742             );
1743              
1744             Returns a L<boolean object|Module::Generic::Boolean>
1745              
1746             =head2 qv
1747              
1748             Boolean. When enabled, this will prepend the dotted decimal version strings with C<v>. This is true by default.
1749              
1750             my $v = Changes::Version->new(
1751             major => 1,
1752             minor => 2,
1753             patch => 3,
1754             alpha => 4,
1755             );
1756             say "$v"; # v1.2.3_4
1757             $v->qv(0);
1758             say "$v"; # 1.2.3_4
1759              
1760             Returns a L<boolean object|Module::Generic::Boolean>
1761              
1762             =head2 rc
1763              
1764             Sets or gets the release candidate value. This is currently unused and reserved for future releases.
1765              
1766             Returns a L<scalar object|Module::Generic::Scalar>
1767              
1768             =for Pod::Coverage reset
1769              
1770             =head2 satisfy
1771              
1772             $v->satisfy( $predicate );
1773              
1774             $v = Changes::Version->parse( '0.1.1' );
1775             $v->satisfy( '0.1.1' ); # true
1776             $v->satisfy( '0.1.1', '> 0, < 0.2, != 0.1.0' ); # true
1777             $v = Changes::Version->parse( '0.2.4' );
1778             $v->satisfy( '0.2.5..0.3.4' ); # false
1779             # or, using it as a class function:
1780             Changes::Version->satisfy( '0.1.1', '0.1.1' ); # true
1781             Changes::Version->satisfy( '0.1.1', '> 0, < 0.2, != 0.1.0' ); # true
1782             Changes::Version->satisfy( '0.2.4', '0.2.5..0.3.4' ); # false
1783              
1784             Determines if a v-string satisfy a predicate. The predicate is a list of simple predicates, each one must be satisfied (that is, an I<and>). Simple predicates takes one of three forms:
1785              
1786             '0.1.2' - exact match
1787             '>= 3.14.15' - (relational operator) (v-string)
1788             '5.6 .. 10.8' - meaning '>= 5.6, <= 10.8'
1789              
1790             A grammar for predicates in L<Parse::RecDescent>-like syntax is:
1791              
1792             <p> : <p0> (',' <p>)*
1793              
1794             <p0>: <v-string> # the same as '==' <v-string>
1795             | <op> <v-string>
1796             | <v-string> '..' <v-string> # the same as ">= <v-string1>, <= <v-string2>"
1797              
1798             <op>: '==' | '!=' | '<=' | '>=' | '<' | '>'
1799              
1800             Spaces are irrelevant in predicates.
1801              
1802             =head2 stringify
1803              
1804             This is an alias for L</as_string>
1805              
1806             =head2 target
1807              
1808             Sets or gets the target format. By default this is C<perl>. This means that L</as_string> will format the version string for C<perl>. In future release of this class, other format wil be supported, such as C<opensource>
1809              
1810             Returns a L<scalar object|Module::Generic::Scalar>
1811              
1812             =head2 type
1813              
1814             Sets or gets the version type. Currently, supported values are C<dotted> for dotted decimal versions such as C<v1.2.3>, and C<decimal> for decimal versions such as C<1.002003>.
1815              
1816             Returns a L<scalar object|Module::Generic::Scalar>
1817              
1818             =head1 OVERLOADED OPERATIONS
1819              
1820             The following operations are overloaded, and internally relies on L<version> to return the value. See also L<overload> for more information.
1821              
1822             Note that calling the version object with any operations other than those listed below will trigger a warning, if warnings are enabled with L<warnings> and C<undef> is return in scalar context or an empty list in list context.
1823              
1824             =over 4
1825              
1826             =item * C<stringification>
1827              
1828             Returns value from L</as_string>
1829              
1830             =item * C<0+>
1831              
1832             Returns value from L</numify>
1833              
1834             =item * C<< <=> >>
1835              
1836             Compares two versions. If the other version being compared is not a L<Changes::Version>, it is made one before comparison actually occurs.
1837              
1838             Note that, C<version> core module L<states in its documentation|version/"How to compare version objects"> that: "alpha" version objects (where the version string contains a trailing underscore segment) compare as less than the equivalent version without an underscore."
1839              
1840             $bool = version->parse("1.23_45") < version->parse("1.2345"); # TRUE
1841              
1842             However, as of perl v5.10, this is not true. The above will actually return false, not true. And so will the following:
1843              
1844             perl -Mversion -lE 'say version->parse("v1.002003") > version->parse("v1.002003_4");'
1845              
1846             This is on my bucket list of things to improve.
1847              
1848             =item * C<cmp>
1849              
1850             Same as above.
1851              
1852             =item * C<bool>
1853              
1854             =item * C<+>, C<->, C<*>, C</>
1855              
1856             When performing those operations, it will use the value of the fragment of the version set with L</default_frag>, which, by default, is C<minor>.
1857              
1858             It returns a new L<Changes::Version> object reflecting the new version value. However, if the operation is swapped, with the version object on the right-hand side instead of the left-hand side, this will return a regular number.
1859              
1860             my $vers = Changes::Version->new( 'v1.2.3_4' );
1861             my $new_version_object = $vers + 2; # Now v1.4.3_4 (minor has been bumped up by 2)
1862             $vers->default_frag( 'major' );
1863             my $new_version_object = $vers + 2; # Now v3.2.3_4 (this time, 'major' was increased)
1864              
1865             But, when swapped:
1866              
1867             my $vers = Changes::Version->new( 'v1.2.3_4' );
1868             my $n = 3 + $vers; # yields 5 (using the 'minor' fragment by default)
1869             $vers->default_frag( 'major' );
1870             my $n = 3 + $vers; # yields 4 (this time, using the 'major' fragment)
1871              
1872             =item * C<+=>, C<-=>, C<*=>, C</=>
1873              
1874             In this operations, it modifies the current object with the operand provided and returns the current object, instead of creating a new one.
1875              
1876             my $vers = Changes::Version->new( 'v1.2.3_4' );
1877             # By default, using the 'minor' fragment
1878             $vers += 1; # version is now v2.2.3_4
1879             $vers->default_frag( 'alpha' );
1880             $vers /= 2; # version is now v1.2.3_2
1881              
1882             =item * C<++>, C<-->
1883              
1884             When using those operations, it updates the current object directly and returns it. For example:
1885              
1886             my $vers = Changes::Version->new( 'v1.2.3_4' );
1887             # By default, using the 'minor' fragment
1888             $vers++; # version is now v1.3.3_4
1889              
1890             =back
1891              
1892             =head1 PATTERNS
1893              
1894             The following patterns can be used to format the version string.
1895              
1896             =over 4
1897              
1898             =item * C<%A>
1899              
1900             my $v = Changes::Version->parse( "5.0.6_2" );
1901             say $v->format( '%A' ); # _2
1902              
1903             This will return the alpha version, if any, prepended with an underscore.
1904              
1905             If there is no alpha version, it returns an empty string.
1906              
1907             =item * C<%a>
1908              
1909             my $v = Changes::Version->parse( "5.0.6_2" );
1910             say $v->format( '%a' ); # 2
1911              
1912             This will return the C<alpha> fragment value, if any.
1913              
1914             If there is no C<alpha> fragment value, it returns an empty string.
1915              
1916             =item * C<%D>
1917              
1918             my $v = Changes::Version->parse( "5.0.6.1.2.3.4_2" );
1919             say $v->format( '%D' ); # 0.6.1.2.3.4
1920             my $v = Changes::Version->parse( "5.0.6" );
1921             say $v->format( '%D' ); # 0.6
1922              
1923             This will return the C<minor>, C<patch>, and any extra fragments.
1924              
1925             This is designed for dotted-decimal types, and C<minor>, and C<patch> will always return a number, possibly C<0>
1926              
1927             =item * C<%d>
1928              
1929             my $v = Changes::Version->parse( "5.0.6.1.2.3.4_2" );
1930             say $v->format( '%D' ); # .0.6.1.2.3.4
1931             my $v = Changes::Version->parse( "5.0.6" );
1932             say $v->format( '%D' ); # .0.6
1933              
1934             This is similar to C<%D>, but will prepend a dot if the value is not null.
1935              
1936             This is designed so you can write:
1937              
1938             my $v = Changes::Version->parse( "5.0.6.1.2.3.4_2" );
1939             say $v->format( '%R%d%A' ); # 5.0.6.1.2.3.4_2
1940             my $v = Changes::Version->parse( "5" );
1941             say $v->format( '%R%d%A' ); # 5.0.0
1942              
1943             =item * C<%M>
1944              
1945             my $v = Changes::Version->parse( "5.0.6.1.2.3.4_2" );
1946             say $v->format( '%M' ); # 0
1947              
1948             This returns the C<minor> part of the version.
1949              
1950             If there is no C<minor> fragment value, it returns an empty string.
1951              
1952             =item * C<%N>
1953              
1954             my $v = Changes::Version->parse( "5.2.6" ):
1955             say $v->format( '%R.%N' ); # 5.002006
1956              
1957             This returns the C<minor> and C<patch> value of a dotted-decimal version as numified version.
1958              
1959             =item * C<%n>
1960              
1961             my $v = Changes::Version->parse( "5.2.6" ):
1962             say $v->format( '%R%n' ); # 5.002006
1963             say $v->format( '%n' ); # .002006
1964              
1965             This is similar to C<%N>, but will prepend a dot if the value is not null.
1966              
1967             =item * C<%P>
1968              
1969             This returns the C<patch> fragment value of the version, if any.
1970              
1971             If there is no C<patch> fragment value, it returns an empty string.
1972              
1973             =item * C<%R>
1974              
1975             This returns the C<major> fragment value of the version, if any.
1976              
1977             If there is no C<major> fragment value, it returns an empty string.
1978              
1979             =item * C<%U>
1980              
1981             my $v = Changes::Version->parse( "5.2.6" ):
1982             say $v->format( '%R.%U' ); # 5.002_006
1983             say $v->format( '%U' ); # 002_006
1984              
1985             This returns the C<minor> and C<patch> value of a dotted-decimal version as numified version using the underscore to separate the C<minor> and the C<patch> fragments.
1986              
1987             =item * C<%u>
1988              
1989             my $v = Changes::Version->parse( "5.2.6" ):
1990             say $v->format( '%R%u' ); # 5.002_006
1991             say $v->format( '%u' ); # .002_006
1992              
1993             This is similar to C<%U>, but will prepend a dot if the value is not null.
1994              
1995             =back
1996              
1997             =head1 AUTHOR
1998              
1999             Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>
2000              
2001             =head1 SEE ALSO
2002              
2003             L<Changes>, L<Changes::Release>, L<Changes::Group>, L<Changes::Change> and L<Changes::NewLine>
2004              
2005             L<version>, L<Perl::Version>, L<version::Internals>, L<Data::VString>, L<perldata/"Version Strings">
2006              
2007             L<CPAN::Meta::Spec/"Version Formats">
2008              
2009             L<http://www.modernperlbooks.com/mt/2009/07/version-confusion.html>
2010              
2011             L<https://xdg.me/version-numbers-should-be-boring/>
2012              
2013             L<https://en.wikipedia.org/wiki/Software_versioning>
2014              
2015             =head1 COPYRIGHT & LICENSE
2016              
2017             Copyright(c) 2022 DEGUEST Pte. Ltd.
2018              
2019             All rights reserved
2020              
2021             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
2022              
2023             =cut