File Coverage

blib/lib/CPANPLUS/Dist/Gentoo/Atom.pm
Criterion Covered Total %
statement 129 129 100.0
branch 81 88 92.0
condition 22 28 78.5
subroutine 20 20 100.0
pod 10 10 100.0
total 262 275 95.2


line stmt bran cond sub pod time code
1             package CPANPLUS::Dist::Gentoo::Atom;
2              
3 4     4   69458 use strict;
  4         9  
  4         160  
4 4     4   21 use warnings;
  4         7  
  4         176  
5              
6             =head1 NAME
7              
8             CPANPLUS::Dist::Gentoo::Atom - Gentoo atom object.
9              
10             =head1 VERSION
11              
12             Version 0.12
13              
14             =cut
15              
16             our $VERSION = '0.12';
17              
18             =head1 DESCRIPTION
19              
20             This class models Gentoo atoms.
21              
22             =cut
23              
24 4     4   20 use Carp ();
  4         5  
  4         50  
25 4     4   18 use Scalar::Util ();
  4         7  
  4         208  
26              
27             use overload (
28 4         41 '<=>' => \&_spaceship,
29             'cmp' => \&_cmp,
30             '""' => \&_stringify,
31 4     4   4877 );
  4         6099  
32              
33 4     4   2648 use CPANPLUS::Dist::Gentoo::Version;
  4         10  
  4         2291  
34              
35             my $range_rx = qr/(?:<|<=|=|>=|>)/;
36             my $name_rx = qr/[a-zA-Z0-9_+-]+/;
37             my $category_rx = $name_rx;
38             my $version_rx = $CPANPLUS::Dist::Gentoo::Version::version_rx;
39              
40             =head1 METHODS
41              
42             =head2 C<< new category => $category, name => $name [, version => $version, range => $range, ebuild => $ebuild ] >>
43              
44             Creates a new L object from the supplied C<$category>, C<$name>, C<$version>, C<$range> and C<$ebuild>.
45              
46             =cut
47              
48             sub new {
49 311     311 1 9730 my $class = shift;
50 311   33     1301 $class = ref($class) || $class;
51              
52 311         1169 my %args = @_;
53              
54 311         418 my ($range, $category, $name, $version);
55 311 100       998 if (defined $args{name}) {
    100          
56 32         70 ($range, $category, $name, $version) = @args{qw};
57 32 100       244 Carp::confess('Category unspecified') unless defined $category;
58 31 100       582 Carp::confess('Invalid category') unless $category =~ /^$category_rx$/o;
59 29 100       490 Carp::confess('Invalid name') unless $name =~ /^$name_rx$/o;
60             } elsif (defined $args{atom}) {
61 277         418 my $atom = $args{atom};
62 277 100       14284 $atom =~ m{^($range_rx)?($category_rx)/($name_rx)(?:-($version_rx))?$}o
63             or Carp::confess('Invalid atom');
64 228         838 ($range, $category, $name, $version) = ($1, $2, $3, $4);
65             } else {
66 2         479 Carp::confess('Not enough information for building an atom object');
67             }
68              
69 255 100       698 if (defined $version) {
70 216 100 100     727 unless (Scalar::Util::blessed($version)
71             and $version->isa('CPANPLUS::Dist::Gentoo::Version')) {
72 215         817 $version = CPANPLUS::Dist::Gentoo::Version->new($version);
73             }
74             }
75              
76 254 100       610 if (defined $version) {
77 215 100       381 if (defined $range) {
78 65 100       590 Carp::confess("Invalid range $range") unless $range =~ /^$range_rx$/o;
79             } else {
80 150         233 $range = '>=';
81             }
82             } else {
83 39 100 100     512 Carp::confess('Range atoms require a valid version')
84             if defined $range and length $range;
85             }
86              
87 251         2264 bless {
88             category => $category,
89             name => $name,
90             version => $version,
91             range => $range,
92             ebuild => $args{ebuild},
93             }, $class;
94             }
95              
96             =head2 C
97              
98             Creates a new L object by inferring the category, name and version from the given C<$ebuild>
99              
100             =cut
101              
102             sub new_from_ebuild {
103 5     5 1 2405 my $class = shift;
104 5   33     35 $class = ref($class) || $class;
105              
106 5         8 my $ebuild = shift;
107 5 100       16 $ebuild = '' unless defined $ebuild;
108              
109 5 100       687 $ebuild =~ m{/($category_rx)/($name_rx)/\2-($version_rx)\.ebuild$}o
110             or Carp::confess('Invalid ebuild');
111 2         10 my ($category, $name, $version) = ($1, $2, $3);
112              
113 2         10 return $class->new(
114             category => $category,
115             name => $name,
116             version => $version,
117             ebuild => $ebuild,
118             );
119             }
120              
121             BEGIN {
122 4     4 1 4376 eval "sub $_ { \$_[0]->{$_} }" for qw;
  2086     2086 1 60396  
  10     10 1 284  
  2086     2086 1 8534  
  798     798 1 3799  
  1714     1714   34765  
123             }
124              
125             =head2 C
126              
127             Read-only accessor to the atom category.
128              
129             =head2 C
130              
131             Read-only accessor to the atom name.
132              
133             =head2 C
134              
135             Read-only accessor to the L object associated with the atom.
136              
137             =head2 C
138              
139             Read-only accessor to the atom range.
140              
141             =head2 C
142              
143             Read-only accessor to the path of an optional ebuild associated with the atom.
144              
145             =head2 C
146              
147             Returns the qualified name for the atom, i.e. C<$category/$name>.
148              
149             =cut
150              
151 2076     2076 1 87412 sub qualified_name { join '/', $_[0]->category, $_[0]->name }
152              
153             sub _spaceship {
154 418     418   12484 my ($a1, $a2, $r) = @_;
155              
156 418         16033 my $v1 = $a1->version;
157              
158 418         490 my $v2;
159 418         1119 my $blessed = Scalar::Util::blessed($a2);
160 418 100 100     3257 unless ($blessed and $a2->isa(__PACKAGE__)) {
161 152 100 66     599 if ($blessed and $a2->isa('CPANPLUS::Dist::Gentoo::Version')) {
162 48         57 $v2 = $a2;
163 48         81 $a2 = undef;
164             } else {
165 104         186 my $maybe_atom = eval { __PACKAGE__->new(atom => $a2) };
  104         320  
166 104 100       7295 if (my $err = $@) {
167 48         76 $v2 = eval { CPANPLUS::Dist::Gentoo::Version->new($a2) };
  48         189  
168 48 50       151 Carp::confess("Can't compare an atom against something that's not an atom, an atom string ($err), a version or a version string ($@)") if $@;
169 48         96 $a2 = undef;
170             } else {
171 56         100 $a2 = $maybe_atom;
172             }
173             }
174             }
175              
176 418 100       1076 if (defined $a2) {
177 322         8341 $v2 = $a2->version;
178              
179 322         633 my $p1 = $a1->qualified_name;
180 322         731 my $p2 = $a2->qualified_name;
181 322 50       1015 Carp::confess("Atoms for different packages $p1 and $p2") unless $p1 eq $p2;
182             }
183              
184 418 50       744 ($v1, $v2) = ($v2, $v1) if $r;
185              
186 418 100 100     2325 return (defined $v1 or 0) <=> (defined $v2 or 0) unless defined $v1
      100        
      100        
187             and defined $v2;
188              
189 346         1001 return $v1 <=> $v2;
190             }
191              
192             sub _cmp {
193 224     224   10428 my ($a1, $a2, $r) = @_;
194              
195 224 50       500 if (defined $a2) {
196 224         583 my $p1 = $a1->qualified_name;
197              
198 224 100 66     1474 unless (Scalar::Util::blessed($a2) && $a2->isa(__PACKAGE__)) {
199 168         273 $a2 = eval { __PACKAGE__->new(atom => $a2) };
  168         474  
200 168 50       383 Carp::confess("Can't compare an atom against something that's not an atom or an atom string ($@)") if $@;
201             }
202 224         434 my $p2 = $a2->qualified_name;
203              
204 224 100       923 if (my $c = $p1 cmp $p2) {
205 112 100       1571 return $r ? -$c : $c;
206             }
207             }
208              
209 112         252 $a1 <=> $a2;
210             }
211              
212             sub _stringify {
213 772     772   156952 my ($a) = @_;
214              
215 772         1501 my $atom = $a->qualified_name;
216              
217 772         20507 my $version = $a->version;
218 772 100       18721 $atom = $a->range . $atom . '-' . $version if defined $version;
219              
220 772         5111 return $atom;
221             }
222              
223             my %order = (
224             '<' => -2,
225             '<=' => -1,
226             '=' => 0,
227             '>=' => 1,
228             '>' => 2,
229             );
230              
231             =head2 C
232              
233             Compute the ranged atom representing the logical AND between C<@atoms> with the same category and name.
234              
235             =cut
236              
237             sub and {
238 76 100   76 1 405 shift unless length ref $_[0];
239              
240 76         89 my $a1 = shift;
241 76 100       168 return $a1 unless @_;
242              
243 72         75 my $a2 = shift;
244 72 100       150 $a2 = $a2->and(@_) if @_;
245              
246 72         122 my $p1 = $a1->qualified_name;
247 72         170 my $p2 = $a2->qualified_name;
248 72 50       185 Carp::confess("Atoms for different packages $p1 and $p2") unless $p1 eq $p2;
249              
250 72         1793 my $v1 = $a1->version;
251 72 100       184 return $a2 unless defined $v1;
252 64         1630 my $r1 = $a1->range; # Defined if $v1 is defined
253              
254 64         1692 my $v2 = $a2->version;
255 64 100       158 return $a1 unless defined $v2;
256 58         1471 my $r2 = $a2->range; # defined if $v2 is defined
257              
258 58         138 my $o1 = $order{$r1};
259 58         66 my $o2 = $order{$r2};
260              
261 58 100       1183 Carp::confess("Incompatible ranges $r1$p1 and $r2$p2") if $o1 * $o2 < 0;
262              
263 50 100       101 if ($r2 eq '=') {
264 18         31 ($a1, $a2) = ($a2, $a1);
265 18         31 ($v1, $v2) = ($v2, $v1);
266 18         32 ($r1, $r2) = ($r2, $r1);
267 18         25 ($o1, $o2) = ($o2, $o1);
268             }
269              
270 50 100       115 if ($r1 eq '=') {
    100          
271 26 100       53 my $r = $r2 eq '=' ? '==' : $r2;
272 26 100       1391 Carp::confess("Version mismatch $v1 $r $v2") unless eval "\$a1 $r \$a2";
273 12         55 return $a1;
274             } elsif ($o1 > 0) {
275 9 100       19 return $a1 < $a2 ? $a2 : $a1;
276             } else {
277 15 100       38 return $a1 < $a2 ? $a1 : $a2;
278             }
279             }
280              
281             =head2 C
282              
283             Returns a list built from C<@atoms> but where there's only one atom for a given category and name.
284              
285             =cut
286              
287             sub fold {
288 3 50   3 1 15 shift unless length ref $_[0];
289              
290 3         5 my %seen;
291 3         4 for my $atom (@_) {
292 12         22 my $key = $atom->qualified_name;
293              
294 12         17 my $cur = $seen{$key};
295 12 100       31 $seen{$key} = defined $cur ? $cur->and($atom) : $atom;
296             }
297              
298 3         25 return map $seen{$_}, sort keys %seen;
299             }
300              
301             =pod
302              
303             This class provides overloaded methods for numerical comparison, string comparison and stringification.
304              
305             =head1 SEE ALSO
306              
307             L, L.
308              
309             =head1 AUTHOR
310              
311             Vincent Pit, C<< >>, L.
312              
313             You can contact me by mail or on C (vincent).
314              
315             =head1 BUGS
316              
317             Please report any bugs or feature requests to C, or through the web interface at L.
318             I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
319              
320             =head1 SUPPORT
321              
322             You can find documentation for this module with the perldoc command.
323              
324             perldoc CPANPLUS::Dist::Gentoo
325              
326             =head1 COPYRIGHT & LICENSE
327              
328             Copyright 2009,2010,2011,2012 Vincent Pit, all rights reserved.
329              
330             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
331              
332             =cut
333              
334             1; # End of CPANPLUS::Dist::Gentoo::Atom