File Coverage

blib/lib/PDLA/AutoLoader.pm
Criterion Covered Total %
statement 49 84 58.3
branch 19 50 38.0
condition 2 11 18.1
subroutine 7 9 77.7
pod 1 4 25.0
total 78 158 49.3


line stmt bran cond sub pod time code
1              
2             =head1 NAME
3              
4             PDLA::AutoLoader - MatLab style AutoLoader for PDLA
5              
6             =head1 SYNOPSIS
7              
8             use PDLA::AutoLoader;
9             $x = func1(...); # Load file func1.pdl
10             $y = func2(...); # Load file func2.pdl
11              
12             $PDLA::AutoLoader::Rescan = 1; # Enable re-scanning
13              
14             =head1 DESCRIPTION
15              
16             This module implements a MatLab style AutoLoader for PDLA. If an unknown
17             function C is called, PDLA looks for a file called C.
18             If it finds one, it compiles the file and calls the function C.
19              
20             The list of directories to search in is given by the shell environment
21             variable C. This is a colon-separated list of directories. On
22             MSWindows systems, is it a I -separated list of directories.
23              
24             For example, in csh:
25              
26             setenv PDLALIB "/home/joe/pdllib:/local/pdllib"
27              
28             B: This variable is unrelated to Perl's C.
29              
30             If you add a leading '+' on a directory name, PDLA will search the
31             entire directory tree below that point. Internally, PDLA stores the
32             directory list in the variable C<@PDLALIB>, which can be modified at
33             run time.
34              
35             For example, in csh:
36              
37             setenv PDLALIB "+/home/joe/PDLA"
38              
39             will search /home/joe/PDLA and all its subdirectories for .pdl files.
40              
41             =head2 AUTO-SCANNING
42              
43             The variable C<$PDLA::AutoLoader::Rescan> controls whether files
44             are automatically re-scanned for changes at the C or
45             C command line.
46              
47             If C<$PDLA::AutoLoader::Rescan == 1> and the file is changed
48             then the new definition is reloaded auto-matically before
49             executing the C or C command line. Which means
50             in practice you can edit files, save changes and have C
51             or C see the changes automatically.
52              
53             The default is '0' - i.e. to have this feature disabled.
54              
55             As this feature is only pertinent to the PDLA shell it imposes
56             no overhead on PDLA scripts. Yes Bob you can have your cake and
57             eat it too!
58              
59             Note: files are only re-evaled if they are determined to have
60             been changed according to their date/time stamp.
61              
62             No doubt this interface could be improved upon some more. :-)
63              
64             =head2 Sample file:
65              
66             sub foo { # file 'foo.pdl' - define the 'foo' function
67             my $x=shift;
68             return sqrt($x**2 + $x**3 + 2);
69             }
70             1; # File returns true (i.e. loaded successfully)
71              
72             =head1 AUTHOR
73              
74             Copyright(C) 1997 Karl Glazebrook (kgb@aaoepp.aao.gov.au);
75             several extensions by Craig DeForest (deforest@boulder.swri.edu)
76             All rights reserved. There is no warranty. You are allowed
77             to redistribute this software / documentation under certain
78             conditions. For details, see the file COPYING in the PDLA
79             distribution. If this file is separated from the PDLA distribution,
80             the copyright notice should be included in the file.
81              
82             =head1 BUGS
83              
84             No doubt this interface could be improved upon some more. :-)
85              
86             Will probably be quite slow if C<$PDLA::AutoLoader::Rescan == 1>
87             and thousands of functions have been autoloaded.
88              
89             There could be a race condition in which the file changes
90             while the internal autoloader code is being executed but it
91             should be harmless.
92              
93             Probably has not been tested enough!
94              
95             =head1 SEE ALSO
96              
97             For an alternative approach to managing a personal collaction of
98             modules and functions, see L.
99              
100             =cut
101              
102             BEGIN{
103 1 50   1   2060 if (defined $ENV{"PDLALIB"}) {
104 0 0       0 if ( $^O eq 'MSWin32' ) { # win32 flavors
105 0         0 @PDLALIB = (".",split(';',$ENV{"PDLALIB"}));
106 0         0 s/"//g for @PDLALIB;
107             } else { # unixen systems
108 0         0 @PDLALIB = (".",split(':',$ENV{"PDLALIB"}));
109             }
110 0         0 @PDLALIB = grep length, @PDLALIB;
111             }
112 1         3 $PDLA::AutoLoader::Rescan=0;
113 1         910 %PDLA::AutoLoader::FileInfo = ();
114             }
115              
116             # Code to reload stuff if changed
117              
118             sub PDLA::AutoLoader::reloader {
119 0 0   0 0 0 return unless $PDLA::AutoLoader::Rescan;
120              
121             # Now check functions and reload if changed
122              
123 0         0 my ($file, $old_t);
124 0         0 for my $func (keys %PDLA::AutoLoader::FileInfo) {
125 0         0 ($file, $old_t) = @{ $PDLA::AutoLoader::FileInfo{$func} };
  0         0  
126 0 0       0 if ( (stat($file))[9]>$old_t ) { # Reload
127 0 0       0 print "Reloading $file as file changed...\n" if $PDLA::verbose;
128 0         0 &PDLA::AutoLoader::autoloader_do($file);
129 0         0 $PDLA::AutoLoader::FileInfo{$func} = [ $file, (stat($file))[9] ];
130             }
131             }
132             }
133              
134             # Used for Beta, and should probably be used generall in this mod
135             #use File::Spec;
136              
137             sub PDLA::AutoLoader::import {
138              
139             # Beta folder support
140             # foreach (@INC) {
141             # $Beta_dir = File::Spec->catfile($_, 'PDLA', 'Beta');
142             # push @PDLALIB, "+$Beta_dir" if -d $Beta_dir;
143             # }
144              
145 1     1   13 my $pkg = (caller())[0];
146 1         3 my $toeval = "package $pkg;\n";
147              
148             # Make sure that the eval gets NiceSlice if we have it in this level
149             # (it's a drag that preprocessors aren't transitive...)
150 1 50       5 $toeval .= "use PDLA::NiceSlice;\n" if(defined $PDLA::NiceSlice::VERSION);
151              
152 1         4 $toeval .= <<'EOD';
153             $PDLALIB_CT = 0;
154              
155             push @PERLDL::AUTO, \&PDLA::AutoLoader::reloader;
156              
157              
158             sub AUTOLOAD {
159             local @INC = @INC;
160             my @args = @_;
161             $AUTOLOAD =~ /::([^:]*)$/;
162             my $func = $1;
163              
164             # Trap spurious calls from 'use UnknownModule'
165              
166             goto &$AUTOLOAD if ord($func)==0;
167              
168             # Check if the PDLALIB needs to be expanded and, if so, expand it.
169             # This only updates when PDLALIB changes size, which should be OK
170             # for most things but doesn't catch new directories in expanded
171             # directory trees. It seems like an OK compromise between never
172             # catching anything and always thrashing through the directories.
173             if($PDLALIB_CT != scalar(@PDLALIB)) {
174             @PDLALIB_EXPANDED = PDLA::AutoLoader::expand_path(@PDLALIB);
175             $PDLALIB_CT = scalar(@PDLALIB);
176             }
177              
178             print "Loading $func.pdl ..." if $PDLA::verbose;
179             my $file;
180              
181             my $s = "PDLA AutoLoader: Undefined subroutine $func() cannot be autoloaded.\n";
182              
183             for my $dir (@PDLALIB_EXPANDED) {
184             $file = $dir . "/" . "$func.pdl";
185             if (-e $file) {
186            
187             print "found $file\n" if $PDLA::verbose;
188              
189             &PDLA::AutoLoader::autoloader_do($file);
190            
191            
192             # Remember autoloaded functions and do some reasonably
193             # smart cacheing of file/directory change times
194            
195             if ($PDLA::AutoLoader::Rescan) {
196             $PDLA::AutoLoader::FileInfo{$func} = [ $file, (stat($file))[9] ];
197             }
198            
199             # Now go to the autoload function
200             ##goto &$AUTOLOAD(@args) unless ($@ || !defined(&{$AUTOLOAD}));
201             return &$AUTOLOAD(@args) unless ($@ || !defined(&{$AUTOLOAD}));
202              
203             die $s."\tWhile parsing file `$file':\n$@\n" if($@);
204             die $s."\tFile `$file' doesn't \n\tdefine ${AUTOLOAD}().\n"
205            
206             }
207             }
208              
209             die $s."\tNo file `$func.pdl' was found in your \@PDLALIB path.\n";
210             }
211              
212             EOD
213              
214 1 50 33 1   7 eval $toeval;
  1 50   1   2  
  1 50       8  
  1 50       60  
  1 50       55  
  1 50       111  
  1 0       83  
  1 50       8  
  1         5  
  1         4  
  1         6  
  1         4  
  1         4  
  1         2  
  1         4  
  1         3  
  1         4  
  1         22  
  1         4  
  1         5  
  1         5  
  0         0  
  1         5  
  1         25  
  0         0  
  0         0  
  0         0  
215              
216             }
217              
218              
219             # Simple 'do' doesn't work with preprocessing -- this replaces
220             # "do file" and sticks NiceSlice in manually if it's needed (yuck).
221              
222             sub PDLA::AutoLoader::autoloader_do {
223 1     1 0 3 my ($file) = shift;
224            
225 1 50       5 if(defined($PDLA::NiceSlice::VERSION)) {
226            
227 1 50       113 print "AutoLoader: NiceSlice enabled...\n" if($PDLA::debug);
228            
229 1 50       52 if(open(AUTOLOAD_FILE,"<$file")) {
230 1         30 my($script) = &PDLA::NiceSlice::perldlpp("PDLA::NiceSlice", join("",));
231 1     1   98 eval $script;
  1         3  
  1         98  
232             }
233             } else {
234 0 0       0 print "AutoLoader: no NiceSlice...\n" if($PDLA::debug);
235 0         0 do $file;
236             }
237             }
238              
239              
240             # Expand directories recursively...
241             sub PDLA::AutoLoader::expand_dir {
242 0     0 0 0 local $d;
243 0         0 local @list;
244 0         0 local @subdirs;
245              
246 0         0 local $dir = shift;
247            
248 0 0       0 if(! -d $dir) { return undef; }
  0         0  
249 0         0 push(@list,$dir);
250              
251 0         0 opendir(FOO,$dir);
252              
253 0   0     0 @subdirs = grep((!m/^\./ && ($_="$dir/$_") && (-d $_)), readdir(FOO));
254 0         0 closedir FOO;
255              
256 0         0 while(defined ($d = shift @subdirs)) {
257 0         0 push(@list,&PDLA::AutoLoader::expand_dir($d));
258             }
259 0         0 return @list;
260             }
261              
262              
263             =head2 PDLA::AutoLoader::expand_path
264              
265             =for ref
266              
267             Expand a compactified path into a dir list
268              
269             You supply a pathlist and leading '+' and '~' characters get expanded into
270             full directories. Normally you don't want to use this -- it's internal to the
271             autoloader -- but some utilities, like the online documentation searcher, need
272             to be able to use it.
273              
274             =cut
275              
276             sub PDLA::AutoLoader::expand_path {
277 2     2 1 6 my @PDLALIB = @_;
278 2         5 my @PDLALIB_EXPANDED;
279            
280 2 50       245 print "AutoLoader: Expanding directories from ".join(':',@PDLALIB)."...\n"
281             if($PDLA::debug);
282 2         11 local $_;
283 2         6 foreach $_(@PDLALIB) {
284             # Expand ~{name} and ~ conventions.
285 2 100       16 if(s/^(\+?)\~(\+||[a-zA-Z0-9]*)//) {
286 1 50       8 if($2 eq '+') {
    50          
287             # Expand shell '+' to CWD.
288 0   0     0 $_= $1 . ($ENV{'PWD'} || '.');
289             } elsif(!$2) {
290             # No name mentioned -- use current user.
291             # Ideally would use File::HomeDir->my_home() here
292 1   33     7 $_ = $1 . ( $ENV{'HOME'} || (( getpwnam( getlogin || getpwuid($<) ))[7]) ) . $_;
293             } else {
294             # Name mentioned - try to get that user's home directory.
295 0         0 $_ = $1 . ( (getpwnam($2))[7] ) . $_;
296             }
297             }
298            
299             # If there's a leading '+', include all subdirs too.
300 2 50       13 push(@PDLALIB_EXPANDED,
301             s/^\+// ? &PDLA::AutoLoader::expand_dir($_) : $_
302             );
303             }
304              
305 2 50       224 print "AutoLoader: returning ",join(",",@PDLALIB_EXPANDED),"\n" if($PDLA::debug);
306 2         55 @PDLALIB_EXPANDED;
307             }
308              
309              
310             ;# Exit with OK status
311              
312             1;