File Coverage

lib/Module/Dependency/Info.pm
Criterion Covered Total %
statement 92 94 97.8
branch 30 38 78.9
condition 16 24 66.6
subroutine 20 20 100.0
pod 10 12 83.3
total 168 188 89.3


line stmt bran cond sub pod time code
1             package Module::Dependency::Info;
2              
3 5     5   69212 use strict;
  5         11  
  5         174  
4              
5 5     5   25 use File::Basename;
  5         9  
  5         521  
6 5     5   13518 use Storable qw/nstore retrieve/;
  5         26367  
  5         503  
7 5     5   48 use base qw(Exporter);
  5         12  
  5         622  
8              
9 5     5   27 use vars qw/$UNIFIED $LOADED/;
  5         12  
  5         406  
10              
11 5     5   27 use constant MAX_DEPTH => 1000;
  5         9  
  5         7527  
12              
13             our $VERSION = (q$Revision: 6643 $ =~ /(\d+)/g)[0];
14             our $unified_file = $ENV{PERL_PMD_DB} || '/var/tmp/dependence/unified.dat';
15              
16             sub setIndex {
17 7     7 1 9061 my $file = shift;
18 7         78 TRACE("Trying to set index to <$file>");
19 7 50       90 return unless $file;
20 7         32 $unified_file = $file;
21 7         21 $LOADED = 0;
22 7         18 $UNIFIED = undef;
23 7         51 return 1;
24             }
25              
26             sub retrieveIndex {
27 7     7 1 30 TRACE("retrieving index");
28 7   50     72 $UNIFIED = retrieve($unified_file) || return (undef);
29 6         1627 $LOADED = 1;
30 6         19 return $UNIFIED;
31             }
32              
33             sub storeIndex {
34 2     2 0 4 my ($data) = @_;
35 2 50       7 $UNIFIED = $data if $data;
36 2         6 TRACE("storing to disk");
37 2         87 my $CACHEDIR = dirname($unified_file);
38 2 50 0     44 mkdir( $CACHEDIR, 0777 ) or die("Can't make data directory $CACHEDIR: $!")
39             unless -d $CACHEDIR;
40 2 50       15 nstore( $UNIFIED, $unified_file ) or die("Problem with nstore writing to $unified_file! $!");
41             }
42              
43             sub allItems {
44 2     2 1 4 my $force = shift;
45 2 50 33     29 if ( !$LOADED || $force ) { retrieveIndex(); }
  0         0  
46 2         4 return [ keys %{ $UNIFIED->{'allobjects'} } ];
  2         31  
47             }
48              
49             sub allScripts {
50 2     2 1 5 my $force = shift;
51 2 50 33     21 if ( !$LOADED || $force ) { retrieveIndex(); }
  0         0  
52 2         29 return $UNIFIED->{'scripts'};
53             }
54              
55             sub getItem {
56 166     166 1 246 my ( $packname, $force ) = @_;
57 166 100 66     661 if ( !$LOADED || $force ) { retrieveIndex(); }
  4         12  
58 166         438 TRACE("Getting record for <$packname>");
59 166 100       477 if ( exists $UNIFIED->{'allobjects'}->{$packname} ) {
60 152         678 return $UNIFIED->{'allobjects'}->{$packname};
61             }
62             else {
63 14         53 return undef;
64             }
65             }
66              
67             sub getFilename {
68 5   100 5 1 3664 my $obj = getItem(@_) || return (undef);
69 3         32 return $obj->{'filename'};
70             }
71              
72             sub getChildren {
73 27   100 27 1 453 my $obj = getItem(@_) || return (undef);
74 20         51 return $obj->{'depends_on'};
75             }
76              
77             sub getParents {
78 26   100 26 1 48 my $obj = getItem(@_) || return (undef);
79 24         66 return $obj->{'depended_upon_by'};
80             }
81              
82             sub dropIndex {
83 2     2 1 8 $LOADED = 0;
84 2         61 undef $UNIFIED;
85 2         49 return 1;
86             }
87              
88             sub relationship {
89 8     8 1 518 my ( $itemName, $otherItem ) = @_;
90 8         34 TRACE("relationship for $itemName / $otherItem");
91 8   100     19 my $obj = getItem($itemName) || return (undef);
92              
93 7         20 my ( $isParent, $isChild ) =
94             ( _isParent( $itemName, $otherItem, {}, 0 ), _isChild( $itemName, $otherItem, {}, 0 ) );
95              
96 7         18 my $rel;
97 7 100 100     40 if ( $isParent && $isChild ) { $rel = 'CIRCULAR'; }
  1 100       3  
    100          
98 1         1 elsif ($isParent) { $rel = 'PARENT'; }
99 2         523 elsif ($isChild) { $rel = 'CHILD'; }
100 3         12 else { $rel = 'NONE'; }
101              
102 7         47 return $rel;
103             }
104              
105             ### PRIVATE
106              
107             sub _isParent {
108 21     21   33 my ( $itemName, $otherItem, $seen, $depth ) = @_;
109 21         78 TRACE("_isParent for $itemName / $otherItem");
110 21 100       71 return 0 if $seen->{$itemName}++;
111 19         37 my $parents = getParents($itemName);
112 19         40 foreach (@$parents) {
113 17 100       48 return 1 if ( $_ eq $otherItem );
114             }
115 17         32 TRACE("...not directly, recursing");
116 17         23 foreach (@$parents) {
117 14 50       36 die "Deep recursion detected" if ( $depth > MAX_DEPTH );
118 14 100       41 return 1 if _isParent( $_, $otherItem, $seen, $depth++ );
119             }
120 15         340 return 0;
121             }
122              
123             sub _isChild {
124 28     28   48 my ( $itemName, $otherItem, $seen, $depth ) = @_;
125 28         71 TRACE("_isChild for $itemName / $otherItem");
126 28 100       92 return 0 if $seen->{$itemName}++;
127 21         32 my $children = getChildren($itemName);
128 21         38 foreach (@$children) {
129 32 100       82 return 1 if ( $_ eq $otherItem );
130             }
131 18         34 TRACE("...not directly, recursing");
132 18         31 foreach (@$children) {
133 21 50       40 die "Deep recursion detected" if ( $depth > MAX_DEPTH );
134 21 100       70 return 1 if _isChild( $_, $otherItem, $seen, $depth++ );
135             }
136 17         51 return 0;
137             }
138              
139 274     274 0 328 sub TRACE { }
140              
141             =head1 NAME
142              
143             Module::Dependency::Info - retrieve dependency information for scripts and modules
144              
145             =head1 SYNOPSIS
146              
147             use Module::Dependency::Info;
148             Module::Dependency::Info::setIndex( '/var/tmp/dependence/unified.dat' );
149            
150             # load the index (actually it's loaded automatically if needed so this is optional)
151             Module::Dependency::Info::retrieveIndex();
152             # or
153             $refToEntireDatabase = Module::Dependency::Info::retrieveIndex();
154            
155             $listref = Module::Dependency::Info::allItems();
156             $listref = Module::Dependency::Info::allScripts();
157            
158             # note the syntax here - the path of perl scripts, but the package name of modules.
159             $dependencyInfo = Module::Dependency::Info::getItem( 'Foo::Bar' [, $forceReload ] );
160             # and
161             $dependencyInfo = Module::Dependency::Info::getItem( './blahblah.pl' [, $forceReload ] );
162            
163             $filename = Module::Dependency::Info::getFilename( 'Foo::Bar' [, $forceReload ] );
164             $listref = Module::Dependency::Info::getChildren( $node [, $forceReload ] );
165             $listref = Module::Dependency::Info::getParents( $node [, $forceReload ] );
166            
167             $value = Module::Dependency::Info::relationship( 'Foo::Bar', 'strict' );
168            
169             Module::Dependency::Info::dropIndex();
170              
171             =head1 DESCRIPTION
172              
173             This module is used to access the data structures created by Module::Dependency::Indexer
174             B a third-party application that creates databases of the correct format.
175             Although you can get at the database structure itself you should use the accessor methods.
176              
177             =head1 METHODS
178              
179             =over 4
180              
181             =item setIndex( $filename );
182              
183             This tells the module where the database is.
184             The default is $ENV{PERL_PMD_DB} or else /var/tmp/dependence/unified.dat
185              
186             =item retrieveIndex();
187              
188             Loads the database into memory. You only have to do this once - after that it's there in
189             RAM ready for use. This routine is called automatically if needed anyway.
190             Incidentally it returns a reference to the entire data structure, but don't use it directly, use this...
191              
192             =item $listref = Module::Dependency::Info::allItems();
193              
194             Returns a reference to an array of all the items in the currently loaded datafile. The order is whatever
195             keys() gives us. The entries in the array are things like 'foo.pl' and 'Bar::Baz'.
196              
197             =item $listref = Module::Dependency::Info::allScripts();
198              
199             Returns a reference ot an array of all the scripts in the currently loaded datafile. The order is whatever
200             it is in the datafile.
201              
202             =item $record = Module::Dependency::Info::getItem( $name [, $forceReload ] );
203              
204             Returns entire record for the thing you name, or undef if no such entry can be found
205             (remember modules are referred to like 'Foo::Bar' whereas scripts like 'foo.pl').
206             Implicity loads the datafile from disk, using the current setting
207             of the data location, if it isn't loaded. Pass in a 1 as the second argument
208             if you want to force a reload - this may be relevant in long-lived perl processes
209             like mod_perl, but only do it when you need to, like every 10 minutes
210             or whatever makes sense for your application.
211              
212             =item $filename = Module::Dependency::Info::getFilename( $node [, $forceReload ] );
213              
214             Gets the full filename for the package/script named, or undef if no record could be found.
215              
216             =item $listref = Module::Dependency::Info::getChildren( $node [, $forceReload ] );
217              
218             Gets a list of all dependencies, i.e. packages that this item depends on, for the
219             package/script named, or undef if no record could be found.
220              
221             =item $listref = Module::Dependency::Info::getParents( $node [, $forceReload ] );
222              
223             Gets a list of all reverse dependencies, i.e. packages that depend upon this item, for
224             the package/script named, or undef if no record could be found.
225              
226             =item $value = Module::Dependency::Info::relationship( $itemName, $otherItem );
227              
228             Tells you whether, according to the current database, $itemName is related to $otherItem.
229             $itemName is a module or script in the database (i.e. it's a file that has been indexed).
230             Return values are:
231              
232             undef if $itemName is not in the database
233              
234             'NONE' if no link can be found (may be a false negative if links between the 2 items are not in the index)
235              
236             'PARENT' if the $otherItem depends upon $itemName
237              
238             'CHILD' if $itemName depends upon $otherItem
239              
240             'CIRCULAR' if $otherItem is both 'PARENT' and 'CHILD'.
241              
242             =item dropIndex
243              
244             drops the current database - you generally have no need to do this unless you're trying to save
245             memory. Usually all you need to do is setIndex followed by a retrieveIndex, get* or all* function.
246              
247             =back
248              
249             =head1 DATA RECORDS
250              
251             The database contains a list of all scripts (.pl and .plx files) encountered. We treat
252             these as special because they form the 'top' of the dependency tree - they 'use' things,
253             but they are not 'use'd themselves. It's just an array of all their nodenames (the filename,
254             excluding the path to the file, e.g. 'foo.pl').
255              
256             The main bit is a hash. The keys of the hash are one of two things: a) keys to module records
257             are the name of the package, e.g. 'Foo::Bar'; b) keys to script records are the nodename of
258             the file, e.g. 'foo.pl'.
259              
260             A data records looks like the right-hand half of these:
261              
262             # lots of Data::Dumper output snipped
263             'IFL::Beasts::Evol::RendererUtils' => {
264             'filename' => '/home/system/cgi-bin/lib/IFL/Beasts/Evol/RendererUtils.pm',
265             'package' => 'IFL::Beasts::Evol::RendererUtils',
266             'depended_upon_by' => [
267             'IFL::Beasts::Evol::TextSkin',
268             'IFL::Beasts::Evol::HTMLSkin'
269             ],
270             'depends_on' => [
271             'lib',
272             'Exporter',
273             'Carp',
274             'IFL::Beasts::Evol::LanguageUtils',
275             'IFL::Beasts::Evol::MathUtils',
276             'EDUtemplate'
277             ]
278             },
279             # lots of Data::Dumper output snipped
280              
281             Or like this, for a script file:
282              
283             # lots of Data::Dumper output snipped
284             'csv_validator.pl' => {
285             'filename' => '/home/system/cgi-bin/education/user_reg/csv_validator.pl',
286             'package' => 'csv_validator.pl',
287             'depends_on' => [
288             'CGI',
289             'EDUprofile',
290             'LWP::Simple',
291             'File::Find'
292             ]
293             },
294             # lots of Data::Dumper output snipped
295              
296             But of course you should use the accessor methods to get at the information.
297              
298             =head1 DEBUGGING
299              
300             There is a TRACE stub function, and the module uses TRACE() to log activity. Override our
301             TRACE with your own routine, e.g. one that prints to STDERR, to see these messages.
302              
303             =head1 SEE ALSO
304              
305             Module::Dependency and the README files.
306              
307             =head1 VERSION
308              
309             $Id: Info.pm 6643 2006-07-12 20:23:31Z timbo $
310              
311             =cut
312              
313