File Coverage

blib/lib/Version/Dotted/Semantic.pm
Criterion Covered Total %
statement 34 34 100.0
branch 2 2 100.0
condition 2 3 66.6
subroutine 11 11 100.0
pod 1 1 100.0
total 50 51 98.0


line stmt bran cond sub pod time code
1             # ---------------------------------------------------------------------- copyright and license ---
2             #
3             # file: lib/Version/Dotted/Semantic.pm
4             #
5             # Copyright © 2016 Van de Bugger.
6             #
7             # This file is part of perl-Version-Dotted.
8             #
9             # perl-Version-Dotted is free software: you can redistribute it and/or modify it under the terms
10             # of the GNU General Public License as published by the Free Software Foundation, either version
11             # 3 of the License, or (at your option) any later version.
12             #
13             # perl-Version-Dotted is distributed in the hope that it will be useful, but WITHOUT ANY
14             # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15             # PURPOSE. See the GNU General Public License for more details.
16             #
17             # You should have received a copy of the GNU General Public License along with
18             # perl-Version-Dotted. If not, see .
19             #
20             # Chapter "Dotted Semantic Versioning" is licensed under CC BY 3.0
21             # .
22             #
23             # ---------------------------------------------------------------------- copyright and license ---
24              
25             #pod =for :this This is C module/class documentation. However, read
26             #pod C module/class documentation first, since it contains many relevant details.
27             #pod
28             #pod =for :those General topics like getting source, building, installing, bug reporting and some
29             #pod others are covered in the F.
30             #pod
31             #pod =for test_synopsis my ( $v, $int );
32             #pod
33             #pod =head1 SYNOPSIS
34             #pod
35             #pod use Version::Dotted::Semantic; # import nothing
36             #pod use Version::Dotted::Semantic 'qv'; # import qv
37             #pod
38             #pod # Construct:
39             #pod $v = Version::Dotted::Semantic->new( v1 ); # v1.0.0 (at least 3 parts)
40             #pod $v = qv( v1 ); # v1.0.0 (at least 3 parts)
41             #pod $v = qv( 'v1.2.3.4' ); # v1.2.3.4
42             #pod
43             #pod # Get parts by name (indexing also works):
44             #pod $int = $v->part( 'major' ); # Always defined.
45             #pod $int = $v->part( 'minor' ); # ditto
46             #pod $int = $v->part( 'patch' ); # ditto
47             #pod $int = $v->part( 'trial' ); # May be undefined.
48             #pod $int = $v->major; # Always defined.
49             #pod $int = $v->minor; # ditto
50             #pod $int = $v->patch; # ditto
51             #pod $int = $v->trial; # May be undefined.
52             #pod
53             #pod # Bump the version by name (indexing also works):
54             #pod $v->bump( 'trial' ); # Bump trial part.
55             #pod $v->bump( 'patch' ); # Bump patch and drop trial.
56             #pod $v->bump( 'minor' ); # Bump minor, reset patch and drop trial.
57             #pod $v->bump( 'major' ); # Bump major, reset minor and patch, drop trial.
58             #pod
59             #pod # Release status:
60             #pod if ( $v->is_trial ) { # If version has more than 3 parts.
61             #pod ...
62             #pod };
63             #pod
64             #pod # Other methods are inherited from Version::Dotted.
65             #pod
66             #pod =head1 DESCRIPTION
67             #pod
68             #pod This is subclass of C. Three features distinct it from the parent:
69             #pod
70             #pod =over
71             #pod
72             #pod =item *
73             #pod
74             #pod Version object always has at least 3 parts.
75             #pod
76             #pod $v = qv( v1 ); # == v1.0.0
77             #pod $v->part( 0 ) == 1; # Parts 0, 1, 2 are always defined.
78             #pod $v->part( 1 ) == 0; # Zero if not specified explicitly.
79             #pod $v->part( 2 ) == 0; # ditto
80             #pod $v->part( 3 ) == undef; # But may be defined.
81             #pod
82             #pod =item *
83             #pod
84             #pod First four parts have individual names:
85             #pod
86             #pod $v->part( 0 ) == $v->part( 'major' ) == $v->major;
87             #pod $v->part( 1 ) == $v->part( 'minor' ) == $v->minor;
88             #pod $v->part( 2 ) == $v->part( 'patch' ) == $v->patch;
89             #pod $v->part( 3 ) == $v->part( 'trial' ) == $v->trial;
90             #pod
91             #pod $v->bump( 'trial' ); # the same as $v->bump( 3 );
92             #pod
93             #pod =item *
94             #pod
95             #pod The number of parts defines release status: more than 3 parts denotes trial release.
96             #pod
97             #pod $v = qv( v1 ); # $v == v1.0.0
98             #pod $v->is_trial; # false
99             #pod $v->bump( 'trial' ); # $v == v1.0.0.1
100             #pod $v->is_trial; # true
101             #pod
102             #pod =back
103             #pod
104             #pod =head1 SEMANTIC VERSIONING
105             #pod
106             #pod See L. It sound very reasonable to
107             #pod me.
108             #pod
109             #pod Unfortunately, Semantic Versioning cannot be applied to Perl modules due to wider character set
110             #pod (letters, hyphens, plus signs, e. g. 1.0.0-alpha.3+8daebec8a8e1) and specific precedence rules
111             #pod (1.0.0-alpha < 1.0.0).
112             #pod
113             #pod =head1 DOTTED SEMANTIC VERSIONING
114             #pod
115             #pod Dotted Semantic Versioning is adaptation of Semantic Versioning for Perl realities.
116             #pod
117             #pod =head2 Summary
118             #pod
119             #pod Given a version number vI.I.I, increment the:
120             #pod
121             #pod =for :list
122             #pod * I version when you make incompatible API changes,
123             #pod * I version when you add functionality in a backwards-compatible manner, and
124             #pod * I version when you make backwards-compatible bug fixes.
125             #pod
126             #pod Additional labels for I versions are available as extension to the
127             #pod vI.I.I format.
128             #pod
129             #pod =head2 Introduction
130             #pod
131             #pod See L.
132             #pod
133             #pod =head2 Dotted Semantic Versioning Specification
134             #pod
135             #pod The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”,
136             #pod “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in L
137             #pod 2119|http://tools.ietf.org/html/rfc2119>.
138             #pod
139             #pod =over
140             #pod
141             #pod =item 1
142             #pod
143             #pod Software using Dotted Semantic Versioning MUST declare a public API. This API could be declared in
144             #pod the code itself or exist strictly in documentation. However it is done, it should be precise and
145             #pod comprehensive.
146             #pod
147             #pod =item 2
148             #pod
149             #pod A I version number MUST take the form vI.I.I where I, I, and I are
150             #pod non-negative integers, and MUST NOT contain leading zeroes. I is the I version, I is
151             #pod the I version, and I is the I version. Each element MUST increase numerically. For
152             #pod instance: v1.9.0 -> v1.10.0 -> v1.11.0.
153             #pod
154             #pod =item 3
155             #pod
156             #pod Once a versioned package has been released, the contents of that version MUST NOT be modified. Any
157             #pod modifications MUST be released as a new version.
158             #pod
159             #pod =item 4
160             #pod
161             #pod Major version zero (v0.I.I) is for initial development. Anything may change at any time. The
162             #pod public API should not be considered stable.
163             #pod
164             #pod =item 5
165             #pod
166             #pod Version v1.0.0 defines the public API. The way in which the version number is incremented after
167             #pod this release is dependent on this public API and how it changes.
168             #pod
169             #pod =item 6
170             #pod
171             #pod I version I (vI.I.I | I > 0) MUST be incremented if only backwards compatible
172             #pod bug fixes are introduced. A bug fix is defined as an internal change that fixes incorrect behavior.
173             #pod
174             #pod =item 7
175             #pod
176             #pod I version I (vI.I.I | I > 0) MUST be incremented if new, backwards compatible
177             #pod functionality is introduced to the public API. It MUST be incremented if any public API
178             #pod functionality is marked as deprecated. It MAY be incremented if substantial new functionality or
179             #pod improvements are introduced within the private code. It MAY include patch level changes. Patch
180             #pod version MUST be reset to 0 when minor version is incremented.
181             #pod
182             #pod =item 8
183             #pod
184             #pod I version I (vI.I.I | I > 0) MUST be incremented if any backwards
185             #pod incompatible changes are introduced to the public API. It MAY include minor and patch level
186             #pod changes. Patch and minor version MUST be reset to 0 when major version is incremented.
187             #pod
188             #pod =item 9
189             #pod
190             #pod A I version MAY be denoted by appending a dot and a series of dot separated numbers
191             #pod immediately following the patch version. Numbers are non-negative integers and MUST NOT include
192             #pod leading zeroes. A trial version indicates that the version is unstable and might not satisfy the
193             #pod intended compatibility requirements as denoted by its associated normal version. Examples:
194             #pod v1.0.0.1, v1.0.0.1.1, v1.0.0.0.3.7, v1.0.0.7.92.
195             #pod
196             #pod =item 10
197             #pod
198             #pod (Paragraph excluded, build metadata is not used.)
199             #pod
200             #pod =item 11
201             #pod
202             #pod Precedence refers to how versions are compared to each other when ordered. Precedence MUST be
203             #pod calculated by separating the version into numbers in order. Precedence is determined by the first
204             #pod difference when comparing each of these numbers from left to right. Example: v1.0.0 < v2.0.0 <
205             #pod v2.1.0 < v2.1.1. A larger set of parts has a higher precedence than a smaller set, if all of the
206             #pod preceding identifiers are equal. Example: v1.0.0 < v1.0.0.1 < v1.0.0.1.1 < v1.0.0.1.2 < v1.0.0.2 <
207             #pod v1.0.1.
208             #pod
209             #pod =back
210             #pod
211             #pod =head2 Why Use Dotted Semantic Versioning?
212             #pod
213             #pod See L.
214             #pod
215             #pod =head2 FAQ
216             #pod
217             #pod See L.
218             #pod
219             #pod =head2 About
220             #pod
221             #pod The Dotted Semantic Versioning specification is authored by Van de Bugger. It is adaptation of
222             #pod Semantic Versioning 2.0.0 for Perl modules.
223             #pod
224             #pod L is authored by L
225             #pod Preston-Werner|http://tom.preston-werner.com/>, inventor of Gravatars and cofounder of GitHub.
226             #pod
227             #pod =head1 ADAPTATION DETAILS
228             #pod
229             #pod Paragraphs 1..8 of Semantic Versioning define I version number and establish rules for
230             #pod I, I and I. I would say these paragraphs are core of Semantic Versioning.
231             #pod Happily they can be applied for versioning Perl modules with almost no modifications. I just added
232             #pod leading 'v' character to version numbers.
233             #pod
234             #pod Paragraphs 9..11 define auxiliary stuff (I, I) and version
235             #pod precedence rules. Unfortunately, these paragraphs cannot be applied as-is for versioning Perl
236             #pod modules, they require adaptation.
237             #pod
238             #pod =head2 Paragraph 9, pre-release version
239             #pod
240             #pod Semantic Versioning uses term I. I version is denoted by appending minus
241             #pod sign and a series of dot separated identifiers which comprise alphanumeric and hyphen.
242             #pod
243             #pod Dotted version cannot include letters and hyphens, a workaround is required.
244             #pod
245             #pod First, let us call it I (instead of I), it is more Perlish and CPANish. (BTW,
246             #pod it is also more correct term, because trial versions are actually released.)
247             #pod
248             #pod Second, let us reduce trial identifier alphabet to digits (instead of alphanumeric and hyphen; it
249             #pod fully meets Semantic Versioning, they call such identifiers "numeric").
250             #pod
251             #pod Third, let us denote I version by dot. Dot is already used to separate parts of I
252             #pod version: I, I, and I. However, the number of parts in I version is
253             #pod fixed, so we can easily distinguish I: the first 3 parts compose I version,
254             #pod everything behind I (if any) compose I.
255             #pod
256             #pod =head2 Paragraph 10, build metadata
257             #pod
258             #pod I is denoted by appending a plus sign and dot separated identifiers.
259             #pod
260             #pod Dotted version cannot include plus sign, a workaround is required (again).
261             #pod
262             #pod Replacement plus sign with dot (like replacing hyphen with dot for I versions) does not
263             #pod work: I would be indistinguishable from I version. Fortunately, I
264             #pod metadata> is not mandatory, so let us drop it completely.
265             #pod
266             #pod =head2 Paragraph 11, precedence
267             #pod
268             #pod This paragraph defines version precedence. It prescribes a I version has lower
269             #pod precedence than a I version with the same I, I, and I: 1.0.0-alpha <
270             #pod 1.0.0.
271             #pod
272             #pod This looks good for Semantic Versioning with hyphen and alphanumeric I identifiers,
273             #pod but it does not look good for Dotted Semantic Versioning with only dots and numeric I
274             #pod identifiers: 1.0.0.1 < 1.0.0.
275             #pod
276             #pod So, let us use natural precedence as it implemented by C module: 1.0.0 < 1.0.0.1. A
277             #pod I release can be placed before I release by choosing appropriate I, I,
278             #pod and I versions. For example, a series of I releases preceding version 1.0.0 could be
279             #pod 0.999.999.1, 0.999.999.2, 0.999.999.3, etc, a series of I releases preceding 1.1.0 could be
280             #pod 1.0.999.1, 1.0.999.2, etc.
281             #pod
282             #pod =cut
283              
284             package Version::Dotted::Semantic;
285              
286 2     2   34450 use strict;
  2         4  
  2         47  
287 2     2   6 use warnings;
  2         3  
  2         66  
288              
289             # ABSTRACT: Use Semantic Versioning for your Perl modules
290             our $VERSION = 'v0.0.0_06'; # TRIAL VERSION
291              
292 2     2   360 use parent 'Version::Dotted';
  2         265  
  2         10  
293              
294 2     2   84 use Scalar::Util qw{};
  2         2  
  2         142  
295              
296 11     11   17 sub _min_len { 3 }; ## no critic ( ProhibitUnusedPrivateSubroutines, RequireFinalReturn )
297             #~ sub _max_len { 4 }; ## no critic ( ProhibitUnusedPrivateSubroutines, RequireFinalReturn )
298              
299             my $names = {
300             major => 0,
301             minor => 1,
302             patch => 2,
303             trial => 3,
304             };
305              
306             # --------------------------------------------------------------------------------------------------
307              
308             #pod =method major
309             #pod
310             #pod =method minor
311             #pod
312             #pod =method patch
313             #pod
314             #pod Returns the first, second, and third part of the version, respectively.
315             #pod
316             #pod $v->major; # the first part
317             #pod $v->minor; # the second part
318             #pod $v->patch; # the third part
319             #pod
320             #pod Since version always has at least 3 parts, these methods never return C.
321             #pod
322             #pod =method trial
323             #pod
324             #pod Returns the fourth part of the version.
325             #pod
326             #pod $v->trial; # the fourth part
327             #pod
328             #pod The method returns C if version has less than 4 parts.
329             #pod
330             #pod =cut
331              
332             while ( my ( $name, $idx ) = each( %$names ) ) {
333             my $sub = sub {
334 8     8   12 my ( $self ) = @_;
335 8         27 return $self->{ version }->[ $idx ];
336             };
337 2     2   6 no strict 'refs'; ## no critic ( ProhibitNoStrict )
  2         3  
  2         208  
338             *{ $name } = $sub;
339             };
340              
341             # --------------------------------------------------------------------------------------------------
342              
343             for my $name ( qw{ part bump } ) {
344             my $sub = sub {
345 22     22   710 my ( $self, $idx ) = @_;
346 22 100       66 if ( not Scalar::Util::looks_like_number( $idx ) ) {
347 14   66     30 $idx = $names->{ $idx } // do {
348 2         10 $self->_warn( "Invalid version part name '$idx'" );
349 2         6 return;
350             };
351             };
352 2     2   6 no strict 'refs'; ## no critic ( ProhibitNoStrict )
  2         3  
  2         94  
353 20         15 return &{ "Version::Dotted::$name" }( $self, $idx );
  20         70  
354             };
355 2     2   9 no strict 'refs'; ## no critic ( ProhibitNoStrict )
  2         3  
  2         175  
356             *{ $name } = $sub;
357             };
358              
359             # --------------------------------------------------------------------------------------------------
360              
361             #pod =method is_trial
362             #pod
363             #pod Returns true (C<1>) if version has more than 3 parts, and false (C) otherwise.
364             #pod
365             #pod If version has no more than 4 parts, C is equal to evaluating C in boolean
366             #pod context. However, if version has more than 4 parts, it is not always true:
367             #pod
368             #pod $v = Version::Dotted::Semantic( v1.2.3.0.5 );
369             #pod $v->trial; # false (0)
370             #pod $v->is_trial; # true
371             #pod
372             #pod =cut
373              
374             sub is_trial {
375 2     2 1 3 my ( $self ) = @_;
376 2         2 return @{ $self->{ version } } > 3;
  2         7  
377             };
378              
379             # --------------------------------------------------------------------------------------------------
380              
381             1;
382              
383             # --------------------------------------------------------------------------------------------------
384              
385             #pod =head1 SEE ALSO
386             #pod
387             #pod =for :list
388             #pod = L
389             #pod = L
390             #pod
391             #pod =head1 COPYRIGHT AND LICENSE
392             #pod
393             #pod =over
394             #pod
395             #pod =item Everything except "Dotted Semantic Versioning" chapter
396             #pod
397             #pod Copyright (C) 2016 Van de Bugger
398             #pod
399             #pod License GPLv3+: The GNU General Public License version 3 or later
400             #pod .
401             #pod
402             #pod This is free software: you are free to change and redistribute it. There is
403             #pod NO WARRANTY, to the extent permitted by law.
404             #pod
405             #pod
406             #pod =item "Dotted Semantic Versioning" chapter
407             #pod
408             #pod Licensed under L.
409             #pod
410             #pod =back
411             #pod
412             #pod =cut
413              
414             # ------------------------------------------------------------------------------------------------
415             #
416             # file: doc/what.pod
417             #
418             # This file is part of perl-Version-Dotted.
419             #
420             # ------------------------------------------------------------------------------------------------
421              
422             #pod =encoding UTF-8
423             #pod
424             #pod =head1 WHAT?
425             #pod
426             #pod C and its subclasses complement standard C class with version
427             #pod modification operations, which can be useful in distribution release tools.
428             #pod
429             #pod =cut
430              
431             # end of file #
432              
433              
434             # end of file #
435              
436             __END__