File Coverage

blib/lib/Decision/Depends/OO.pm
Criterion Covered Total %
statement 141 149 94.6
branch 43 64 67.1
condition 18 21 85.7
subroutine 18 18 100.0
pod 0 6 0.0
total 220 258 85.2


line stmt bran cond sub pod time code
1             # --8<--8<--8<--8<--
2             #
3             # Copyright (C) 2008 Smithsonian Astrophysical Observatory
4             #
5             # This file is part of Decision::Depends
6             #
7             # Decision-Depends is free software: you can redistribute it and/or modify
8             # it under the terms of the GNU General Public License as published by
9             # the Free Software Foundation, either version 3 of the License, or (at
10             # your option) any later version.
11             #
12             # This program is distributed in the hope that it will be useful,
13             # but WITHOUT ANY WARRANTY; without even the implied warranty of
14             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15             # GNU General Public License for more details.
16             #
17             # You should have received a copy of the GNU General Public License
18             # along with this program. If not, see .
19             #
20             # -->8-->8-->8-->8--
21              
22             package Decision::Depends::OO;
23              
24             require 5.005_62;
25 11     11   61 use strict;
  11         21  
  11         390  
26 11     11   58 use warnings;
  11         24  
  11         593  
27              
28             require Exporter;
29              
30             ## no critic ( ProhibitAccessOfPrivateData )
31              
32             our $VERSION = '0.20';
33              
34 11     11   55 use Carp;
  11         19  
  11         1788  
35 11     11   67 use Scalar::Util qw( reftype );
  11         21  
  11         1636  
36 11     11   11965 use Tie::IxHash;
  11         98509  
  11         450  
37              
38 11     11   8174 use Decision::Depends::State;
  11         41  
  11         399  
39 11     11   9204 use Decision::Depends::List;
  11         34  
  11         356  
40 11     11   6460 use Decision::Depends::Target;
  11         29  
  11         28111  
41              
42              
43              
44             # regular expression for a floating point number
45             our $RE_Float = qr/^[+-]?(\d+[.]?\d*|[.]\d+)([dDeE][+-]?\d+)?$/;
46              
47             sub new
48             {
49 57     57 0 163 my $class = shift;
50 57   33     603 $class = ref($class) || $class;
51              
52 57         504 my $self = { Attr => { Cache => 0,
53             DumpFiles => 0,
54             Pretend => 0,
55             Verbose => 0,
56             Force => 0,
57             File => undef
58             }
59             };
60 57         199 bless $self, $class;
61              
62 57         867 $self->{State} = Decision::Depends::State->new();
63              
64 57         1634 $self->configure( @_ );
65              
66 57         172 $self;
67             }
68              
69             sub Verbose
70             {
71 44     44 0 370 $_[0]->{State}->Verbose;
72             }
73              
74             sub Pretend
75             {
76 15     15 0 78 $_[0]->{State}->Pretend;
77             }
78              
79             sub configure
80             {
81 95     95 0 2712 my $self = shift;
82              
83 95 100       1916 return unless @_;
84              
85 38         343 my @opts = @_;
86 38         145 my %attr;
87 38         116 my ($key, $val);
88              
89 38         316 while ( @opts )
90             {
91 38         171 my $opt = shift @opts;
92              
93 38 50       593 if ( 'HASH' eq ref $opt )
    0          
94             {
95 38         541 my @notok = grep { ! exists $self->{Attr}{$_} } keys %$opt;
  94         694  
96 38 50       220 croak( __PACKAGE__, '->configure: unknown attribute(s): ',
97             join( ', ', @notok) ) if @notok;
98 38         984 $attr{$key} = $val while( ($key, $val) = each %$opt );
99             }
100              
101             elsif ( 'ARRAY' eq ref $opt )
102             {
103 0 0       0 croak( __PACKAGE__, '->configure: odd number of elements in arrayref' )
104             if @$opt %2;
105              
106 0         0 unshift @opts, @$opt;
107             }
108              
109             else
110             {
111 0 0       0 croak( __PACKAGE__,
112             '->configure: odd number of elements in options list' )
113             unless @opts;
114              
115 0 0       0 croak( __PACKAGE__, "->configure: unknown attribute: `$opt'" )
116             unless exists $self->{Attr}{$opt};
117              
118 0         0 $attr{$opt} = shift @opts;
119             }
120              
121             }
122              
123 38         949 $self->{Attr}{$key} = $val while( ($key, $val) = each %attr );
124 38         690 $self->{State}->SetAttr( \%attr );
125             }
126              
127             sub if_dep
128             {
129 20     20 0 41 my $self = shift;
130              
131 20         47 my ( $args, $run ) = @_;
132              
133 20 50       186 print STDOUT "\nNew dependency\n" if $self->Verbose;
134              
135 20         119 my @specs = $self->_build_spec_list( undef, undef, $args );
136              
137 20         119 my ( $deplist, $targets ) = $self->_traverse_spec_list( @specs );
138              
139 20         94 my $depends = $self->_depends( $deplist, $targets );
140              
141 20 100       81 if ( keys %$depends )
142             {
143             # clean up beforehand in case of Pretend
144 15         37 undef $@;
145 15 50       55 print STDOUT "Action required.\n" if $self->Verbose;
146 15 50       63 eval { &$run( $depends) } unless $self->Pretend;
  15         57  
147 15 100       478 if ( $@ )
148             {
149 2 100       398 croak $@ unless defined wantarray;
150 1         17 return 0;
151             }
152             else
153             {
154 13         58 $self->_update( $deplist, $targets );
155             }
156             }
157             else
158             {
159 5 50       20 print STDOUT "No action required.\n" if $self->Verbose;
160             }
161 18         368 1;
162             }
163              
164             sub test_dep
165             {
166 4     4 0 23 my $self = shift;
167 4         63 my ( @args ) = @_;
168              
169 4 50       28 print STDOUT "\nNew dependency\n" if $self->Verbose;
170              
171 4         47 my @specs = $self->_build_spec_list( undef, undef, \@args );
172              
173 4         24 my ( $deplist, $targets ) = $self->_traverse_spec_list( @specs );
174              
175 4         153 my $depends = $self->_depends( $deplist, $targets );
176              
177 3 50       57 wantarray ? %$depends : keys %$depends;
178             }
179              
180              
181             # spec format is
182              
183             # -attr1 => -attr2 => value1, ...
184             # where value may be of the form
185             # [ -attr3 => -attr4 => value2 ]
186             # attr1 and attr2 are attached to value2
187             # attributes may have values,
188             # '-attr=attr_value'
189             # by default the value is 1
190             # to undefine an attribute:
191             # -no_attr
192             # additionally, each value is given an attribute "id" representing its
193             # position in the list (independent of attributes) and in any sublists.
194             # id = [0], [0,0], [0,1,1], etc.
195              
196             sub _build_spec_list
197             {
198 83     83   1759 my $self = shift;
199 83         289 my ( $attrs, $levels, $specs ) = @_;
200              
201 83 100       1425 $attrs = [ Tie::IxHash->new() ] unless defined $attrs;
202 83 100       3239 $levels = [ -1 ] unless defined $levels;
203              
204 83         152 my @res;
205              
206             # process target attributes
207 83         319 foreach my $spec ( @$specs )
208             {
209 303         13859 my $ref = ref $spec;
210             # if it's an attribute, process it
211 303 100 100     8432 if ( ! $ref && $spec !~ /$RE_Float/ &&
    100 100        
    100 100        
    50 100        
212             $spec =~ /^-(no_)?(\w+)(?:\s*=\s*(.*))?/ )
213             {
214 154 100       641 if ( defined $1 )
215             {
216 1         8 $attrs->[-1]->Push( $2 => undef);
217             }
218             else
219             {
220 153 100       1644 $attrs->[-1]->Push( $2 => defined $3 ? $3 : 1);
221             }
222             }
223              
224             # maybe a nested level?
225             elsif ( 'ARRAY' eq $ref )
226             {
227 12         95 push @$attrs, Tie::IxHash->new();
228 12         509 $levels->[-1]++;
229 12         146 push @$levels, -1;
230 12         414 push @res, $self->_build_spec_list( $attrs, $levels, $spec );
231 12         33 pop @$attrs;
232 12         45 pop @$levels;
233              
234             # reset attributes
235 12         58 $attrs->[-1] = Tie::IxHash->new();
236             }
237              
238             # a value
239             elsif ( 'SCALAR' eq $ref || 'REF' eq $ref || ! $ref )
240             {
241 130 100       330 $spec = $$spec if $ref;
242              
243 130         207 $ref = ref $spec;
244              
245 130 50       711 if ( $ref !~ /^(|ARRAY|HASH)$/ )
246             {
247 0         0 croak( __PACKAGE__, '::_build_spec_list:',
248             "value can only be scalar or ref to scalar, hashref or arrayref!\n" );
249             }
250              
251              
252 130         218 $levels->[-1]++;
253 130         194 my %attr;
254 130         359 foreach my $lattr ( @$attrs )
255             {
256 169         1849 my ( $key, $val );
257 169         909 $attr{$_} = $lattr->FETCH($_) foreach $lattr->Keys;
258             }
259 130         4351 delete @attr{ grep { ! defined $attr{$_} } keys %attr };
  175         513  
260 130         1508 push @res, { id => [ @$levels ],
261             val => $spec ,
262             attr => \%attr };
263              
264             # reset attributes
265 130         600 $attrs->[-1] = Tie::IxHash->new();
266             }
267              
268             # hash; keys are values of last attribute specified
269             elsif( 'HASH' eq $ref )
270             {
271             # find last attribute specified; may have to search upwards through
272             # nested levels
273 7         22 ( my $lattr ) = grep { defined $_->Keys(-1) } reverse @$attrs;
  7         31  
274 7 50       79 croak( __PACKAGE__, '::_build_spec_list:',
275             "can't find an attribute to assign values to with this hash!\n" )
276             unless defined $lattr;
277              
278 7         38 my $attr = $lattr->Keys(-1);
279              
280             # create a new level
281 7         71 while ( my ( $attrval, $lspec ) = each %$spec )
282             {
283 18         64 push @$attrs, Tie::IxHash->new($attr => $attrval);
284 18         546 $levels->[-1]++;
285 18         30 push @$levels, -1;
286 18         69 push @res, $self->_build_spec_list( $attrs, $levels, [ $lspec ] );
287 18         40 pop @$attrs;
288 18         82 pop @$levels;
289             }
290              
291             # reset attributes
292 7         28 $attrs->[-1] = Tie::IxHash->new();
293              
294             }
295             }
296              
297 83         2189 @res;
298             }
299              
300              
301             sub _traverse_spec_list
302             {
303 52     52   218 my $self = shift;
304 52         326 my @list = @_;
305              
306 52         131 local $Carp::CarpLevel = $Carp::CarpLevel + 1;
307              
308             # two phases; first the targets, then the dependencies.
309             # the targets are identified as id 0.X
310              
311 52         1280 my $deplist = Decision::Depends::List->new( $self->{State} );
312              
313 52         115 my @targets;
314              
315 52         274 eval {
316              
317 52         280 for my $spec ( @list )
318             {
319 120 100 100     229 if ( (grep { exists $spec->{attr}{$_} } qw( target targets sfile slink )) ||
  480   66     2195  
320             (! exists $spec->{attr}{depend} && 0 == $spec->{id}[0] ) )
321             {
322 57         767 push @targets, Decision::Depends::Target->new( $self->{State}, $spec );
323             }
324              
325             else
326             {
327 63         125 my @match = grep { defined $spec->{attr}{$_} } qw( sig var time ) ;
  189         623  
328              
329 63 50       198 if ( @match > 1 )
330             {
331 0         0 $Carp::CarpLevel--;
332 0         0 croak( __PACKAGE__,
333             "::traverse_spec_list: too many dependency classes for `$spec->{val}'" )
334             }
335              
336 63 100       315 my $class = 'Decision::Depends::' .
337             ( @match ? ucfirst( $match[0]) : 'Time' );
338              
339 63         1180 $deplist->add( $class->new( $self->{State}, $spec ) );
340             }
341             }
342             };
343              
344 52 50       152 croak( $@ ) if $@;
345              
346 52 100       534 croak( __PACKAGE__, '::traverse_spec_list: no targets?' )
347             unless @targets;
348              
349             # should we require dependencies?
350             # croak( __PACKAGE__, '::traverse_spec_list: no dependencies?' )
351             # unless $deplist->ndeps;
352              
353 50         243 ( $deplist, \@targets );
354             }
355              
356             sub _depends
357             {
358 49     49   69923 my $self = shift;
359 49         97 my ( $deplist, $targets ) = @_;
360              
361 49         112 local $Carp::CarpLevel = $Carp::CarpLevel + 1;
362 49         244 $deplist->depends( $targets );
363             }
364              
365             sub _update
366             {
367 16     16   7452 my $self = shift;
368 16         31 my ( $deplist, $targets ) = @_;
369              
370 16         34 local $Carp::CarpLevel = $Carp::CarpLevel + 1;
371              
372 16         78 $deplist->update( $targets );
373              
374 16         60156 $_->update foreach @$targets;
375             }
376              
377             1;