File Coverage

blib/lib/Algorithm/Dependency.pm
Criterion Covered Total %
statement 60 63 95.2
branch 20 24 83.3
condition n/a
subroutine 14 15 93.3
pod 8 8 100.0
total 102 110 92.7


line stmt bran cond sub pod time code
1             package Algorithm::Dependency;
2              
3             =pod
4              
5             =head1 NAME
6              
7             Algorithm::Dependency - Base class for implementing various dependency trees
8              
9             =head1 SYNOPSIS
10              
11             use Algorithm::Dependency;
12             use Algorithm::Dependency::Source::File;
13            
14             # Load the data from a simple text file
15             my $data_source = Algorithm::Dependency::Source::File->new( 'foo.txt' );
16            
17             # Create the dependency object, and indicate the items that are already
18             # selected/installed/etc in the database
19             my $dep = Algorithm::Dependency->new(
20             source => $data_source,
21             selected => [ 'This', 'That' ]
22             ) or die 'Failed to set up dependency algorithm';
23            
24             # For the item 'Foo', find out the other things we also have to select.
25             # This WON'T include the item we selected, 'Foo'.
26             my $also = $dep->depends( 'Foo' );
27             print $also
28             ? "By selecting 'Foo', you are also selecting the following items: "
29             . join( ', ', @$also )
30             : "Nothing else to select for 'Foo'";
31            
32             # Find out the order we need to act on the items in.
33             # This WILL include the item we selected, 'Foo'.
34             my $schedule = $dep->schedule( 'Foo' );
35              
36             =head1 DESCRIPTION
37              
38             Algorithm::Dependency is a framework for creating simple read-only
39             dependency heirachies, where you have a set of items that rely on other
40             items in the set, and require actions on them as well.
41              
42             Despite the most visible of these being software installation systems like
43             the CPAN installer, or debian apt-get, they are usefull in other situations.
44             This module intentionally uses implementation-neutral words, to avoid
45             confusion.
46              
47             =head2 Terminology
48              
49             The term C refers to a single entity, such as a single software
50             package, in the overall set of possible entities. Internally, this is a
51             fairly simple object. See L for details.
52              
53             The term C
54             already been acted up in the required way. For example, if the software
55             package had already been installed, and didn't need to be re-installed,
56             it would be C.
57              
58             The term C refers to a location that contains the master set of
59             items. This will be very application specific, and might be a flat file,
60             some form of database, the list of files in a folder, or generated
61             dynamically.
62              
63             =head2 General Description
64              
65             Algorithm::Dependency implements algorithms relating to dependency
66             heirachies. To use this framework, all you need is a source for the master
67             list of all the items, and a list of those already selected. If your
68             dependency heirachy doesn't require the concept of items that are already
69             selected, simply don't pass anything to the constructor for it.
70              
71             Please note that the class Algorithm::Dependency does NOT implement an
72             ordering, for speed and simplicity reasons. That is, the C it
73             provides is not in any particular order. If item 'A' depends on item 'B',
74             it will not place B before A in the schedule. This makes it unsuitable for
75             things like software installers, as they typically would need B to be
76             installed before A, or the installation of A would fail.
77              
78             For dependency heirachies requiring the items to be acted on in a particular
79             order, either top down or bottom up, see L.
80             It should be more applicable for your needs. This is the the subclass you
81             would probably use to implement a simple ( non-versioned ) package
82             installation system. Please note that an ordered heirachy has additional
83             constraints. For example, circular dependencies ARE legal in a
84             non-ordered heirachy, but ARE NOT legal in an ordered heirachy.
85              
86             =head2 Extending
87              
88             A module for creating a source from a simple flat file is included. For
89             details see L. Information on creating
90             a source for your particular use is in L.
91              
92             =head1 METHODS
93              
94             =cut
95              
96 8     8   203592 use 5.005;
  8         28  
  8         316  
97 8     8   46 use strict;
  8         14  
  8         259  
98 8     8   5034 use Algorithm::Dependency::Item ();
  8         23  
  8         139  
99 8     8   5046 use Algorithm::Dependency::Source ();
  8         28  
  8         278  
100 8     8   62 use Params::Util qw{_INSTANCE _ARRAY};
  8         15  
  8         822  
101              
102 8     8   49 use vars qw{$VERSION};
  8         15  
  8         360  
103             BEGIN {
104 8     8   8169 $VERSION = '1.110';
105             }
106              
107              
108              
109              
110              
111             #####################################################################
112             # Constructor
113              
114             =pod
115              
116             =head2 new %args
117              
118             The constructor creates a new context object for the dependency algorithms to
119             act in. It takes as argument a series of options for creating the object.
120              
121             =over 4
122              
123             =item source => $Source
124              
125             The only compulsory option is the source of the dependency items. This is
126             an object of a subclass of L. In practical terms,
127             this means you will create the source object before creating the
128             Algorithm::Dependency object.
129              
130             =item selected => [ 'A', 'B', 'C', etc... ]
131              
132             The C option provides a list of those items that have already been
133             'selected', acted upon, installed, or whatever. If another item depends on one
134             in this list, we don't have to include it in the output of the C or
135             C methods.
136              
137             =item ignore_orphans => 1
138              
139             Normally, the item source is expected to be largely perfect and error free.
140             An 'orphan' is an item name that appears as a dependency of another item, but
141             doesn't exist, or has been deleted.
142              
143             By providing the C flag, orphans are simply ignored. Without
144             the C flag, an error will be returned if an orphan is found.
145              
146             =back
147              
148             The C constructor returns a new Algorithm::Dependency object on success,
149             or C on error.
150              
151             =cut
152              
153             sub new {
154 18     18 1 7436 my $class = shift;
155 18         67 my %args = @_;
156 18 100       186 my $source = _INSTANCE($args{source}, 'Algorithm::Dependency::Source')
157             or return undef;
158              
159             # Create the object
160 17         130 my $self = bless {
161             source => $source, # Source object
162             selected => {},
163             }, $class;
164              
165             # Were we given the 'ignore_orphans' flag?
166 17 100       62 if ( $args{ignore_orphans} ) {
167 5         15 $self->{ignore_orphans} = 1;
168             }
169              
170             # Done, unless we have been given some selected items
171 17 100       131 _ARRAY($args{selected}) or return $self;
172              
173             # Make sure each of the selected ids exists
174 5         11 my %selected = ();
175 5         11 foreach my $id ( @{ $args{selected} } ) {
  5         15  
176             # Does the item exist?
177 15 50       45 return undef unless $source->item($id);
178              
179             # Is it a duplicate
180 15 50       31 return undef if $selected{$id};
181              
182             # Add to the selected index
183 15         50 $selected{$id} = 1;
184             }
185              
186 5         17 $self->{selected} = \%selected;
187 5         21 $self;
188             }
189              
190              
191              
192              
193              
194             #####################################################################
195             # Basic methods
196              
197             =pod
198              
199             =head2 source
200              
201             The C method retrieves the L object
202             for the algorithm context.
203              
204             =cut
205              
206 10     10 1 5002 sub source { $_[0]->{source} }
207              
208             =pod
209              
210             =head2 selected_list
211              
212             The C method returns, as a list and in alphabetical order,
213             the list of the names of the selected items.
214              
215             =cut
216              
217 5     5 1 12 sub selected_list { sort keys %{$_[0]->{selected}} }
  5         50  
218              
219             =pod
220              
221             =head2 selected $name
222              
223             Given an item name, the C method will return true if the item is
224             selected, false is not, or C if the item does not exist, or an error
225             occurs.
226              
227             =cut
228              
229 13     13 1 67 sub selected { $_[0]->{selected}->{$_[1]} }
230              
231             =pod
232              
233             =head2 item $name
234              
235             The C method fetches and returns the item object, as specified by the
236             name argument.
237              
238             Returns an L object on success, or C if
239             an item does not exist for the argument provided.
240              
241             =cut
242              
243 10     10 1 56 sub item { $_[0]->{source}->item($_[1]) }
244              
245              
246              
247              
248              
249             #####################################################################
250             # Main algorithm methods
251              
252             =pod
253              
254             =head2 depends $name1, ..., $nameN
255              
256             Given a list of one or more item names, the C method will return
257             a reference to an array containing a list of the names of all the OTHER
258             items that also have to be selected to meet dependencies.
259              
260             That is, if item A depends on B and C then the C method would
261             return a reference to an array with B and C. ( C<[ 'B', 'C' ]> )
262              
263             If multiple item names are provided, the same applies. The list returned
264             will not contain duplicates.
265              
266             The method returns a reference to an array of item names on success, a
267             reference to an empty array if no other items are needed, or C
268             on error.
269              
270             =cut
271              
272             sub depends {
273 279     279 1 111054 my $self = shift;
274 279 50       782 my @stack = @_ or return undef;
275 279         371 my @depends = ();
276 279         392 my %checked = ();
277              
278             # Process the stack
279 279         686 while ( my $id = shift @stack ) {
280             # Does the id exist?
281 772 100       2745 my $Item = $self->{source}->item($id)
    100          
282             or $self->{ignore_orphans} ? next : return undef;
283              
284             # Skip if selected or checked
285 763 100       1856 next if $checked{$id};
286              
287             # Add its depends to the stack
288 686         1817 push @stack, $Item->depends;
289 686         1566 $checked{$id} = 1;
290              
291             # Add anything to the final output that wasn't one of
292             # the original input.
293 686 100       935 unless ( scalar grep { $id eq $_ } @_ ) {
  750         2798  
294 396         1347 push @depends, $id;
295             }
296             }
297              
298             # Remove any items already selected
299 272         422 my $s = $self->{selected};
300 272         644 return [ sort grep { ! $s->{$_} } @depends ];
  396         1491  
301             }
302              
303             =pod
304              
305             =head2 schedule $name1, ..., $nameN
306              
307             Given a list of one or more item names, the C method will return,
308             as a reference to an array, the ordered list of items you should act upon.
309              
310             This would be the original names provided, plus those added to satisfy
311             dependencies, in the prefered order of action. For the normal algorithm,
312             where order it not important, this is alphabetical order. This makes it
313             easier for someone watching a program operate on the items to determine
314             how far you are through the task and makes any logs easier to read.
315              
316             If any of the names you provided in the arguments is already selected, it
317             will not be included in the list.
318              
319             The method returns a reference to an array of item names on success, a
320             reference to an empty array if no items need to be acted upon, or C
321             on error.
322              
323             =cut
324              
325             sub schedule {
326 156     156 1 67133 my $self = shift;
327 156 50       499 my @items = @_ or return undef;
328              
329             # Get their dependencies
330 156 100       340 my $depends = $self->depends( @items ) or return undef;
331              
332             # Now return a combined list, removing any items already selected.
333             # We are allowed to return an empty list.
334 154         248 my $s = $self->{selected};
335 154         269 return [ sort grep { ! $s->{$_} } @items, @$depends ];
  351         1338  
336             }
337              
338             =pod
339              
340             =head2 schedule_all;
341              
342             The C method acts the same as the C method, but
343             returns a schedule that selected all the so-far unselected items.
344              
345             =cut
346              
347             sub schedule_all {
348 0     0 1   my $self = shift;
349 0           $self->schedule( map { $_->id } $self->source->items );
  0            
350             }
351              
352             1;
353              
354             =pod
355              
356             =head1 TO DO
357              
358             Add the C method, to verify the integrity of the source.
359              
360             Possibly add Algorithm::Dependency::Versions, to implement an ordered
361             dependency tree with versions, like for perl modules.
362              
363             Currently readonly. Make the whole thing writable, so the module can be
364             used as the core of an actual dependency application, as opposed to just
365             being a tool.
366              
367             =head1 SUPPORT
368              
369             Bugs should be submitted via the CPAN bug tracker, located at
370              
371             L
372              
373             For general comments, contact the author.
374              
375             =head1 AUTHOR
376              
377             Adam Kennedy Eadamk@cpan.orgE
378              
379             =head1 SEE ALSO
380              
381             L, L,
382             L, L
383              
384             =head1 COPYRIGHT
385              
386             Copyright 2003 - 2009 Adam Kennedy.
387              
388             This program is free software; you can redistribute
389             it and/or modify it under the same terms as Perl itself.
390              
391             The full text of the license can be found in the
392             LICENSE file included with this module.
393              
394             =cut