File Coverage

blib/lib/CPAN/Queue.pm
Criterion Covered Total %
statement 35 86 40.7
branch 4 32 12.5
condition 0 6 0.0
subroutine 11 18 61.1
pod 0 11 0.0
total 50 153 32.6


line stmt bran cond sub pod time code
1             # -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*-
2 22     22   145 use strict;
  22         69  
  22         4956  
3             package CPAN::Queue::Item;
4              
5             # CPAN::Queue::Item::new ;
6             sub new {
7 7     7   45 my($class,@attr) = @_;
8 7         111 my $self = bless { @attr }, $class;
9 7         33 return $self;
10             }
11              
12             sub as_string {
13 7     7   21 my($self) = @_;
14 7         26 $self->{qmod};
15             }
16              
17             # r => requires, b => build_requires, c => commandline
18             sub reqtype {
19 7     7   17 my($self) = @_;
20 7         35 $self->{reqtype};
21             }
22              
23             sub optional {
24 7     7   21 my($self) = @_;
25 7         94 $self->{optional};
26             }
27              
28             package CPAN::Queue;
29              
30             # One use of the queue is to determine if we should or shouldn't
31             # announce the availability of a new CPAN module
32              
33             # Now we try to use it for dependency tracking. For that to happen
34             # we need to draw a dependency tree and do the leaves first. This can
35             # easily be reached by running CPAN.pm recursively, but we don't want
36             # to waste memory and run into deep recursion. So what we can do is
37             # this:
38              
39             # CPAN::Queue is the package where the queue is maintained. Dependencies
40             # often have high priority and must be brought to the head of the queue,
41             # possibly by jumping the queue if they are already there. My first code
42             # attempt tried to be extremely correct. Whenever a module needed
43             # immediate treatment, I either unshifted it to the front of the queue,
44             # or, if it was already in the queue, I spliced and let it bypass the
45             # others. This became a too correct model that made it impossible to put
46             # an item more than once into the queue. Why would you need that? Well,
47             # you need temporary duplicates as the manager of the queue is a loop
48             # that
49             #
50             # (1) looks at the first item in the queue without shifting it off
51             #
52             # (2) cares for the item
53             #
54             # (3) removes the item from the queue, *even if its agenda failed and
55             # even if the item isn't the first in the queue anymore* (that way
56             # protecting against never ending queues)
57             #
58             # So if an item has prerequisites, the installation fails now, but we
59             # want to retry later. That's easy if we have it twice in the queue.
60             #
61             # I also expect insane dependency situations where an item gets more
62             # than two lives in the queue. Simplest example is triggered by 'install
63             # Foo Foo Foo'. People make this kind of mistakes and I don't want to
64             # get in the way. I wanted the queue manager to be a dumb servant, not
65             # one that knows everything.
66             #
67             # Who would I tell in this model that the user wants to be asked before
68             # processing? I can't attach that information to the module object,
69             # because not modules are installed but distributions. So I'd have to
70             # tell the distribution object that it should ask the user before
71             # processing. Where would the question be triggered then? Most probably
72             # in CPAN::Distribution::rematein.
73              
74 22     22   159 use vars qw{ @All $VERSION };
  22         39  
  22         36328  
75             $VERSION = "5.5003";
76              
77             # CPAN::Queue::queue_item ;
78             sub queue_item {
79 7     7 0 76 my($class,@attr) = @_;
80 7         88 my $item = "$class\::Item"->new(@attr);
81 7         37 $class->qpush($item);
82 7         22 return 1;
83             }
84              
85             # CPAN::Queue::qpush ;
86             sub qpush {
87 7     7 0 24 my($class,$obj) = @_;
88 7         18 push @All, $obj;
89             CPAN->debug(sprintf("in new All[%s]",
90 7 50       31 join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @All),
  0         0  
91             )) if $CPAN::DEBUG;
92             }
93              
94             # CPAN::Queue::first ;
95             sub first {
96 12     12 0 72 my $obj = $All[0];
97 12         168 $obj;
98             }
99              
100             # CPAN::Queue::delete_first ;
101             sub delete_first {
102 5     5 0 49 my($class,$what) = @_;
103 5         33 my $i;
104 5         49 for my $i (0..$#All) {
105 4 50       42 if ( $All[$i]->{qmod} eq $what ) {
106 4         36 splice @All, $i, 1;
107 4         23 last;
108             }
109             }
110             CPAN->debug(sprintf("after delete_first mod[%s] All[%s]",
111             $what,
112 5 50       368 join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @All)
  0         0  
113             )) if $CPAN::DEBUG;
114             }
115              
116             # CPAN::Queue::jumpqueue ;
117             sub jumpqueue {
118 0     0 0 0 my $class = shift;
119 0         0 my @what = @_;
120             CPAN->debug(sprintf("before jumpqueue All[%s] what[%s]",
121 0         0 join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @All),
122 0 0       0 join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @what),
  0         0  
123             )) if $CPAN::DEBUG;
124 0 0       0 unless (defined $what[0]{reqtype}) {
125             # apparently it was not the Shell that sent us this enquiry,
126             # treat it as commandline
127 0         0 $what[0]{reqtype} = "c";
128             }
129 0 0       0 my $inherit_reqtype = $what[0]{reqtype} =~ /^(c|r)$/ ? "r" : "b";
130 0         0 WHAT: for my $what_tuple (@what) {
131 0         0 my($qmod,$reqtype,$optional) = @$what_tuple{qw(qmod reqtype optional)};
132 0 0 0     0 if ($reqtype eq "r"
133             &&
134             $inherit_reqtype eq "b"
135             ) {
136 0         0 $reqtype = "b";
137             }
138 0         0 my $jumped = 0;
139 0         0 for (my $i=0; $i<$#All;$i++) { #prevent deep recursion
140 0 0       0 if ($All[$i]{qmod} eq $qmod) {
141 0         0 $jumped++;
142             }
143             }
144             # high jumped values are normal for popular modules when
145             # dealing with large bundles: XML::Simple,
146             # namespace::autoclean, UNIVERSAL::require
147 0 0       0 CPAN->debug("qmod[$qmod]jumped[$jumped]") if $CPAN::DEBUG;
148 0         0 my $obj = "$class\::Item"->new(
149             qmod => $qmod,
150             reqtype => $reqtype,
151             optional => !! $optional,
152             );
153 0         0 unshift @All, $obj;
154             }
155             CPAN->debug(sprintf("after jumpqueue All[%s]",
156 0 0       0 join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @All)
  0         0  
157             )) if $CPAN::DEBUG;
158             }
159              
160             # CPAN::Queue::exists ;
161             sub exists {
162 0     0 0 0 my($self,$what) = @_;
163 0         0 my @all = map { $_->{qmod} } @All;
  0         0  
164 0         0 my $exists = grep { $_->{qmod} eq $what } @All;
  0         0  
165             # warn "in exists what[$what] all[@all] exists[$exists]";
166 0         0 $exists;
167             }
168              
169             # CPAN::Queue::delete ;
170             sub delete {
171 1     1 0 10 my($self,$mod) = @_;
172 1         9 @All = grep { $_->{qmod} ne $mod } @All;
  1         12  
173             CPAN->debug(sprintf("after delete mod[%s] All[%s]",
174             $mod,
175 1 50       6 join("",map {sprintf " %s\[%s][%s]\n",$_->{qmod},$_->{reqtype},$_->{optional}} @All)
  0            
176             )) if $CPAN::DEBUG;
177             }
178              
179             # CPAN::Queue::nullify_queue ;
180             sub nullify_queue {
181 0     0 0   @All = ();
182             }
183              
184             # CPAN::Queue::size ;
185             sub size {
186 0     0 0   return scalar @All;
187             }
188              
189             sub reqtype_of {
190 0     0 0   my($self,$mod) = @_;
191 0           my $best = "";
192 0           for my $item (grep { $_->{qmod} eq $mod } @All) {
  0            
193 0           my $c = $item->{reqtype};
194 0 0         if ($c eq "c") {
    0          
    0          
195 0           $best = $c;
196 0           last;
197             } elsif ($c eq "r") {
198 0           $best = $c;
199             } elsif ($c eq "b") {
200 0 0         if ($best eq "") {
201 0           $best = $c;
202             }
203             } else {
204 0           die "Panic: in reqtype_of: reqtype[$c] seen, should never happen";
205             }
206             }
207 0           return $best;
208             }
209              
210             sub iterator {
211 0     0 0   my $i = 0;
212             return sub {
213 0   0 0     until ($All[$i] || $i > $#All) {
214 0           $i++;
215             }
216 0 0         return if $i > $#All;
217 0           return $All[$i++]
218 0           };
219             }
220              
221             1;
222              
223             __END__