File Coverage

blib/lib/Algorithm/Dependency/Objects.pm
Criterion Covered Total %
statement 61 67 91.0
branch 16 22 72.7
condition 4 8 50.0
subroutine 16 18 88.8
pod 12 12 100.0
total 109 127 85.8


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             package Algorithm::Dependency::Objects;
4              
5 1     1   68568 use strict;
  1         3  
  1         39  
6 1     1   6 use warnings;
  1         3  
  1         54  
7              
8             our $VERSION = '0.04';
9              
10 1     1   7 use Scalar::Util qw/blessed/;
  1         8  
  1         68  
11 1     1   6 use Carp qw/croak/;
  1         2  
  1         57  
12              
13 1     1   6 use Set::Object;
  1         2  
  1         930  
14              
15             sub _to_set {
16 5     5   13 my ( $class, $objects ) = @_;
17              
18 5 50       19 if ( ref $objects ) {
19 5 100 66     66 $objects = Set::Object->new(@$objects) if not blessed $objects and ref $objects eq 'ARRAY';
20              
21 5 50 33     64 if ( blessed $objects and $objects->isa("Set::Object") ) {
22 5         113 return $objects;
23             }
24             }
25              
26 0         0 return;
27             }
28              
29             sub new {
30 4     4 1 63788 my ($class, %params) = @_;
31              
32 4 50       25 my $objects = $class->_to_set($params{objects}) or
33             croak "The 'objects' parameter must be an array reference or a Set::Object";
34            
35 4 100       53 my $selected = exists($params{selected})
    50          
36             ? $class->_to_set($params{selected})
37             : Set::Object->new()
38             or croak "If provided, the 'selected' parameter must be an array reference or a Set::Object";
39            
40             # all the contents of the Set::Object must have depends methods
41 4         41 $class->assert_can_get_deps($objects);
42              
43 4         26 $objects = $class->verify_input_set($objects);
44              
45 4         40 return bless {
46             objects => $objects,
47             selected => $selected,
48             }, $class;
49             }
50              
51 41     41 1 16871 sub objects { (shift)->{objects} }
52 67     67 1 600 sub selected { (shift)->{selected} }
53              
54             sub get_deps {
55 129     129 1 189 my ( $self, $obj ) = @_;
56 129         293 $obj->depends;
57             }
58              
59             sub can_get_deps {
60 32     32 1 40 my ( $self, $obj ) = @_;
61 32         165 $obj->can("depends");
62             }
63              
64             sub assert_can_get_deps {
65 4     4 1 11 my ( $self, $objs ) = @_;
66 4   50     45 $self->can_get_deps($_) || croak "Objects must have a 'depends' method" for $objs->members;
67             }
68              
69             sub depends {
70 37     37 1 41080 my ( $self, @objs ) = @_;
71              
72 37         64 my @queue = @objs;
73              
74 37         191 my $selected_now = Set::Object->new;
75 37         86 my $selected_previously = $self->selected;
76              
77 37         85 my $all_objects = $self->objects;
78              
79 37         99 while (@queue){
80 97         158 my $obj = shift @queue;
81              
82 97 50       269 $self->unknown_object($obj) unless $all_objects->contains($obj);
83              
84 97 100       598 next if $selected_now->contains($obj);
85 89 100       496 next if $selected_previously->contains($obj);
86              
87 83         413 push @queue, $self->get_deps($obj);
88              
89 83         697 $selected_now->insert($obj);
90             }
91              
92 37         160 $selected_now->remove(@objs);
93              
94 37 100       256 return wantarray ? $selected_now->members : $selected_now;
95             }
96              
97             sub verify_input_set {
98 4     4 1 9 my ( $self, $objects ) = @_;
99              
100 4         20 my $dependant = Set::Object->new(map { $self->get_deps($_) } $objects->members);
  32         178  
101              
102 4         53 my $unresolvable = $dependant->difference($objects);
103              
104 4 50       156 if ($unresolvable->size){
105 0         0 return $self->handle_missing_objects($unresolvable, $objects);
106             }
107              
108 4         33 return $objects;
109             }
110              
111              
112             sub handle_missing_objects {
113 0     0 1 0 my ( $self, $missing, $objects ) = @_;
114              
115 0         0 croak "Unresolvable objects " . join(", ", $missing->members);
116              
117             # return $objects->union($missing);
118             }
119              
120             sub unknown_object {
121 0     0 1 0 my ( $self, $obj ) = @_;
122 0         0 croak "$obj is not in the input objects";
123             }
124              
125             sub schedule {
126 21     21 1 28136 my ( $self, @desired ) = @_;
127              
128 21         158 my $desired = Set::Object->new(@desired);
129              
130 21         64 my $selected = $self->selected;
131              
132 21         78 my $missing = $desired->difference($selected);
133              
134 21         586 $self->depends(@desired)->union($missing)->members;
135             }
136              
137             sub schedule_all {
138 2     2 1 15226 my $self = shift;
139 2         20 $self->objects->difference($self->selected)->members;
140             }
141              
142             __PACKAGE__
143              
144             __END__
145              
146             =pod
147              
148             =head1 NAME
149              
150             Algorithm::Dependency::Objects - An implementation of an Object Dependency Algorithm
151              
152             =head1 SYNOPSIS
153              
154             use Algorithm::Dependency::Objects;
155              
156             my $o = Algorithm::Dependency::Objects->new(
157             objects => \@objects,
158             selected => \@selected, # objects which are already taken care of
159             );
160              
161             my @needed = $o->schedule( $objects[0] );
162              
163             # need to take care of @needed for $objecs[0] to be resolved
164              
165             =head1 DESCRIPTION
166              
167             This modules is a re-implementation of L<Algorithm::Dependency> using only
168             objects instead of object ids, making use of L<Set::Object> for book-keeping.
169              
170             =head1 METHODS
171              
172             =over 4
173              
174             =item B<new>
175              
176             Duh.
177              
178             =item B<objects>
179              
180             =item B<selected>
181              
182             Returns the L<Set::Object> representing this collection. Objects is an
183             enumeration of all the object who we're dependo-frobnicating, and selected is
184             those that don't need to be run.
185              
186             =item B<depends>
187              
188             =item B<schedule>
189              
190             =item B<schedule_all>
191              
192             See L<Algorithm::Dependency>'s corresponding methods.
193              
194             =item B<verify_input_set> $object_set
195              
196             Make sure that the dependencies of every object in the set are also in the set.
197              
198             =item B<handle_missing_objects> $missing_set, $input_set
199              
200             Called by C<verify_input_set> when objects are missing from the input set.
201              
202             You can override this method to simply return
203              
204             $input_set->union($missing_set);
205              
206             making all dependencies of the input objects implicit input objects themselves.
207              
208             =item B<unknown_object> $object
209              
210             Called when a new object pops out of the blue in the middle of processing (it
211             means C<get_deps> is returning inconsistent values).
212              
213             =item B<get_deps> $object
214              
215             Extract the dependencies out of an object. Calls C<depends> on the object.
216              
217             =item B<can_get_deps> $object
218              
219             Default implementation is
220              
221             $object->can("depends");
222              
223             =item B<assert_can_get_deps> $object_set
224              
225             Croaks if C<can_get_deps> doesn't return true for every object in the set.
226              
227              
228             =back
229              
230             =head1 SEE ALSO
231              
232             Adam Kennedy's excellent L<Algorithm::Dependency> module, upon which this is based.
233              
234             =head1 BUGS
235              
236             None that we are aware of. Of course, if you find a bug, let us know, and we will be sure to fix it.
237              
238             =head1 CODE COVERAGE
239              
240             We use Devel::Cover to test the code coverage of our tests, below is the Devel::Cover report on this module test suite.
241              
242             =head1 AUTHORS
243              
244             Yuval Kogman
245              
246             Stevan Little
247              
248             =head1 COPYRIGHT AND LICENSE
249              
250             Copyright (C) 2005 Yuval Kogman, Stevan Little
251              
252             This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
253              
254             =cut