File Coverage

blib/lib/Anansi/Actor.pm
Criterion Covered Total %
statement 15 150 10.0
branch 0 104 0.0
condition n/a
subroutine 5 8 62.5
pod 2 2 100.0
total 22 264 8.3


line stmt bran cond sub pod time code
1             package Anansi::Actor;
2              
3              
4             =head1 NAME
5              
6             Anansi::Actor - A dynamic usage module definition
7              
8             =head1 SYNOPSIS
9              
10             use Anansi::Actor;
11             my $object = Anansi::Actor->new(
12             PACKAGE => 'Anansi::Example',
13             );
14             $object->someSubroutine() if(defined($object));
15              
16             use Anansi::Actor;
17             use Data::Dumper qw(Dumper);
18             my %modules = Anansi::Actor->modules();
19             if(defined($modules{DBI})) {
20             Anansi::Actor->new(
21             PACKAGE => 'DBI',
22             );
23             print Data::Dumper::Dumper(DBI->available_drivers());
24             }
25              
26             use Anansi::Actor;
27             use Data::Dumper qw(Dumper);
28             if(1 == Anansi::Actor->modules(
29             PACKAGE => 'DBI',
30             )) {
31             Anansi::Actor->new(
32             PACKAGE => 'DBI',
33             );
34             print Data::Dumper::Dumper(DBI->available_drivers());
35             }
36              
37             =head1 DESCRIPTION
38              
39             This is a dynamic usage module definition that manages the loading of a required
40             namespace and blessing of an object of the namespace as required. Uses L,
41             L, L and L.
42              
43             =cut
44              
45              
46             our $VERSION = '0.14';
47              
48 1     1   28709 use base qw(Anansi::Singleton);
  1         3  
  1         1121  
49              
50 1     1   12355 use Fcntl ':flock';
  1         2  
  1         282  
51 1     1   7 use File::Find;
  1         8  
  1         156  
52 1     1   1094 use File::Spec::Functions;
  1         1027  
  1         105  
53 1     1   1036 use FileHandle;
  1         17201  
  1         9  
54              
55              
56             my $ACTOR = Anansi::Actor->SUPER::new();
57              
58              
59             =head1 METHODS
60              
61             =cut
62              
63              
64             =head2 Anansi::Class
65              
66             See L for details. A parent module of L.
67              
68             =cut
69              
70              
71             =head3 DESTROY
72              
73             See L for details. Overridden by L.
74              
75             =cut
76              
77              
78             =head3 finalise
79              
80             See L for details. A virtual method.
81              
82             =cut
83              
84              
85             =head3 implicate
86              
87             See L for details. A virtual method.
88              
89             =cut
90              
91              
92             =head3 import
93              
94             See L for details.
95              
96             =cut
97              
98              
99             =head3 initialise
100              
101             See L for details. A virtual method.
102              
103             =cut
104              
105              
106             =head3 new
107              
108             See L for details. Overridden by L.
109              
110             =cut
111              
112              
113             =head3 old
114              
115             See L for details.
116              
117             =cut
118              
119              
120             =head3 used
121              
122             See L for details.
123              
124             =cut
125              
126              
127             =head3 uses
128              
129             See L for details.
130              
131             =cut
132              
133              
134             =head3 using
135              
136             See L for details.
137              
138             =cut
139              
140              
141             =head2 Anansi::Singleton
142              
143             See L for details. A parent module of L.
144              
145             =cut
146              
147              
148             =head3 Anansi::Class
149              
150             See L for Class. A parent module of L.
151              
152             =cut
153              
154              
155             =head3 DESTROY
156              
157             See L for details. Overrides L.
158              
159             =cut
160              
161              
162             =head3 fixate
163              
164             See L for details. A virtual method.
165              
166             =cut
167              
168              
169             =head3 new
170              
171             See L for details. Overrides L. Overridden by L.
172              
173             =cut
174              
175              
176             =head3 reinitialise
177              
178             See L for details. A virtual method.
179              
180             =cut
181              
182              
183             =head2 modules
184              
185             my %MODULES = $object->modules();
186              
187             use Anansi::Actor;
188             my %MODULES = Anansi::Actor->modules(
189             INTERVAL => 3600,
190             );
191              
192             if(1 == $object->modules(
193             PACKAGE => [
194             'Some::Module::Namespace',
195             'Another::Module::Namespace',
196             'Yet::Another::Module::Namespace'
197             ],
198             )) {
199             print 'The modules have been found.'."\n";
200             }
201              
202             use Anansi::Actor;
203             my $MODULE = 'Some::Module::Namespace';
204             if(0 == Anansi::Actor->modules(
205             PACKAGE => $MODULE,
206             INTERVAL => 43200,
207             )) {
208             print 'The "'.$MODULE.'" module has not been found.'."\n";
209             }
210              
211             =over 4
212              
213             =item self I<(Blessed Hash, Required)>
214              
215             An object of this namespace.
216              
217             =item parameters I<(Hash)>
218              
219             Named parameters.
220              
221             =over 4
222              
223             =item INTERVAL I<(String, Optional)>
224              
225             Specifies a refresh interval in seconds. Defaults to 86400 seconds (1 day).
226              
227             =item PACKAGE I<(Array B String, Optional)>
228              
229             An ARRAY of module namespaces or a module namespace to find on the operating
230             system.
231              
232             =back
233              
234             =back
235              
236             Builds a HASH of all the modules and their paths that are available on the
237             operating system and either returns the module HASH or a B<1> I<(one)> on
238             success and a B<0> I<(zero)> on failure when determining the existence of the
239             modules that are specified in the I parameter. A temporary file
240             "Anansi-Actor.#" will be created if at all possible to improve the speed of this
241             subroutine by storing the module HASH. The temporary file will automatically
242             be updated when this subroutine is subsequently run when the number of seconds
243             specified in the I parameter or a full day has passed. Deleting the
244             temporary file will also cause an update to occur.
245              
246             =cut
247              
248              
249             sub modules {
250 0     0 1   my ($self, %parameters) = @_;
251 0 0         if(defined($parameters{PACKAGE})) {
252 0 0         $parameters{PACKAGE} = [($parameters{PACKAGE})] if(ref($parameters{PACKAGE}) =~ /^$/);
253 0 0         return 0 if(ref($parameters{PACKAGE}) !~ /^ARRAY$/i);
254 0 0         return 0 if(0 == scalar(@{$parameters{PACKAGE}}));
  0            
255 0           foreach my $package (@{$parameters{PACKAGE}}) {
  0            
256 0 0         return 0 if(ref($package) !~ /^$/);
257 0 0         return 0 if($package !~ /^[a-zA-Z]+[a-zA-Z0-9_]*(::[a-zA-Z]+[a-zA-Z0-9_]*)*$/);
258             }
259             }
260 0 0         $ACTOR->{INTERVAL} = 86400 if(!defined($ACTOR->{INTERVAL}));
261 0 0         if(!defined($parameters{INTERVAL})) {
    0          
    0          
    0          
262             } elsif(ref($parameters{INTERVAL}) !~ /^$/) {
263             } elsif($parameters{INTERVAL} !~ /^\s*[\-+]?\d+\s*$/) {
264             } elsif(0 + $parameters{INTERVAL} <= 0) {
265             } else {
266 0           $ACTOR->{INTERVAL} = 0 + $parameters{INTERVAL};
267             }
268 0           my $TIMESTAMP = time();
269 0           my $filename;
270 0           my $refresh = 0;
271 0           my $update = 0;
272 0 0         if(opendir(DIRECTORY, File::Spec->tmpdir())) {
    0          
    0          
273 0           my @files = reverse(sort(grep(/^Anansi-Actor\.\d+$/, readdir(DIRECTORY))));
274 0           closedir(DIRECTORY);
275 0           $filename = 'Anansi-Actor.'.$TIMESTAMP;
276 0 0         if(0 < scalar(@files)) {
277 0           my $timestamp = (split(/\./, $files[0]))[1];
278 0 0         if(!defined($ACTOR->{TIMESTAMP})) {
    0          
279 0 0         if(0 + $TIMESTAMP < 0 + $timestamp + $ACTOR->{INTERVAL}) {
280 0           $filename = shift(@files);
281 0           $ACTOR->{TIMESTAMP} = 0 + $timestamp;
282 0           $refresh = 1;
283             } else {
284 0           $ACTOR->{TIMESTAMP} = 0 + $TIMESTAMP;
285 0           $update = 1;
286             }
287             } elsif(0 + $TIMESTAMP < 0 + $ACTOR->{TIMESTAMP} + $ACTOR->{INTERVAL}) {
288 0 0         if(0 + $ACTOR->{TIMESTAMP} <= 0 + $timestamp) {
289 0           $filename = shift(@files);
290 0           $ACTOR->{TIMESTAMP} = 0 + $timestamp;
291 0           $refresh = 1;
292             } else {
293 0           $filename = 'Anansi-Actor.'.$ACTOR->{TIMESTAMP};
294             }
295             } else {
296 0           $ACTOR->{TIMESTAMP} = 0 + $TIMESTAMP;
297 0           $update = 1;
298             }
299 0           foreach my $file (@files) {
300 0           $file = File::Spec->catfile(File::Spec->splitdir(File::Spec->tmpdir()), $file);
301 0           unlink($file);
302             }
303             }
304 0           $filename = File::Spec->catfile(File::Spec->splitdir(File::Spec->tmpdir()), $filename);
305 0 0         $refresh = 1 if(!defined($ACTOR->{MODULES}));
306 0 0         if($refresh) {
307 0 0         if(open(FILE_HANDLE, '<'.$filename)) {
308 0           flock(FILE_HANDLE, LOCK_EX);
309 0           my @contents = ;
310 0           my $content = join(',', @contents);
311 0           flock(FILE_HANDLE, LOCK_UN);
312 0           close(FILE_HANDLE);
313 0           %{$ACTOR->{MODULES}} = split(',', $content);
  0            
314             } else {
315 0           $update = 1;
316             }
317             }
318             } elsif(!defined($ACTOR->{TIMESTAMP})) {
319 0           $ACTOR->{TIMESTAMP} = $TIMESTAMP;
320 0           $update = 1;
321             } elsif(0 + $ACTOR->{TIMESTAMP} + $ACTOR->{INTERVAL} < 0 + $TIMESTAMP) {
322 0           $ACTOR->{TIMESTAMP} = $TIMESTAMP;
323 0           $update = 1;
324             }
325 0 0         if($update) {
326 0           $ACTOR->{MODULES} = {};
327             File::Find::find(
328             {
329             wanted => sub {
330 0     0     my $path = File::Spec->canonpath($_);
331 0 0         return if($path !~ /\.pm$/);
332 0 0         return if(!open(FILE, $path));
333 0           my $package;
334 0           my $pod = 0;
335 0           while() {
336 0           chomp;
337 0 0         if(/^=cut.*$/) {
338 0           $pod = 0;
339 0           next;
340             }
341 0 0         $pod = 1 if(/^=[a-zA-Z]+.*$/);
342 0 0         next if($pod);
343 0 0         next if($_ !~ /^\s*package\s+[a-zA-Z0-9_:]+\s*;.*$/);
344 0           ($package = $_) =~ s/^\s*package\s+([a-zA-Z0-9_:]+)\s*;.*$/$1/;
345             }
346 0           close(FILE);
347 0 0         return if(!defined($package));
348 0 0         return if(defined(${$ACTOR->{MODULES}}{$package}));
  0            
349 0           ${$ACTOR->{MODULES}}{$package} = $path;
  0            
350             },
351 0           follow => 1,
352             follow_skip => 2,
353             no_chdir => 1,
354             },
355             @INC
356             );
357             }
358 0 0         if(defined($filename)) {
359 0 0         if(open(FILE_HANDLE, '<'.$filename)) {
360 0           close(FILE_HANDLE);
361             } else {
362 0           my $content = join(',', @{[%{$ACTOR->{MODULES}}]});
  0            
  0            
363 0 0         if(open(FILE_HANDLE, '+>'.$filename)) {
364 0           FILE_HANDLE->autoflush(1);
365 0           flock(FILE_HANDLE, LOCK_EX);
366 0           print FILE_HANDLE $content;
367 0           flock(FILE_HANDLE, LOCK_UN);
368 0           close(FILE_HANDLE);
369             }
370             }
371             }
372 0 0         if(defined($parameters{PACKAGE})) {
373 0           foreach my $package (@{$parameters{PACKAGE}}) {
  0            
374 0 0         return 0 if(!defined(${$ACTOR->{MODULES}}{$package}));
  0            
375             }
376 0           return 1;
377             }
378 0           return %{$ACTOR->{MODULES}};
  0            
379             }
380              
381              
382             =head2 new
383              
384             my $object = Anansi::Actor->new(
385             PACKAGE => 'Anansi::Example',
386             );
387              
388             =over 4
389              
390             =item class I<(Blessed Hash B String, Required)>
391              
392             Either an object or a string of this namespace.
393              
394             =item parameters I<(Hash)>
395              
396             Named parameters.
397              
398             =over 4
399              
400             =item BLESS I<(String, Optional)>
401              
402             The name of the subroutine within the namespace that creates a blessed object of
403             the namespace. Defaults to I<"new">.
404              
405             =item IMPORT I<(Array, Optional)>
406              
407             An array of the names to import from the loading module.
408              
409             =item PACKAGE I<(String, Required)>
410              
411             The namespace of the module to load.
412              
413             =item PARAMETERS I<(Array B Hash, Optional)>
414              
415             Either An array or a hash of the parameters to pass to the blessing subroutine.
416              
417             =back
418              
419             =back
420              
421             Overrides I<(L)>. Instantiates
422             an object instance of a dynamically loaded module.
423              
424             =cut
425              
426              
427             sub new {
428 0     0 1   my ($class, %parameters) = @_;
429 0 0         return if(!defined($parameters{PACKAGE}));
430 0 0         return if(ref($parameters{PACKAGE}) !~ /^$/);
431 0 0         return if($parameters{PACKAGE} !~ /^[a-zA-Z]+[a-zA-Z0-9_]*(::[a-zA-Z]+[a-zA-Z0-9_]*)*$/);
432 0 0         if(!defined($parameters{BLESS})) {
433 0           $parameters{BLESS} = 'new';
434             } else {
435 0 0         return if(ref($parameters{BLESS}) !~ /^$/);
436 0 0         return if($parameters{BLESS} !~ /^[a-zA-Z]+[a-zA-Z0-9_]*$/);
437             }
438 0 0         if(defined($parameters{PARAMETERS})) {
439 0 0         $parameters{PARAMETERS} = [(%{$parameters{PARAMETERS}})] if(ref($parameters{PARAMETERS}) =~ /^HASH$/i);
  0            
440 0 0         return if(ref($parameters{PARAMETERS}) !~ /^ARRAY$/i);
441             }
442 0 0         if(defined($parameters{IMPORT})) {
443 0 0         return if(ref($parameters{IMPORT}) !~ /^ARRAY$/i);
444 0           foreach my $import (@{$parameters{IMPORT}}) {
  0            
445 0 0         return if(ref($import) !~ /^$/);
446 0 0         return if($import !~ /^[a-zA-Z_]+[a-zA-Z0-9_]*$/);
447             }
448             }
449 0           my $package = $parameters{PACKAGE};
450 0           my $bless = $parameters{BLESS};
451 0           my $self;
452             eval {
453 0           (my $file = $package) =~ s/::/\//g;
454 0           require $file.'.pm';
455 0 0         if(defined($parameters{IMPORT})) {
456 0           $package->import(@{$parameters{IMPORT}});
  0            
457             } else {
458 0           $package->import();
459             }
460 0 0         if(defined($parameters{PARAMETERS})) {
461 0           $self = $package->$bless(@{$parameters{PARAMETERS}});
  0            
462             } else {
463 0           $self = $package->$bless();
464             }
465 0           1;
466 0 0         } or do {
467 0           my $error = $@;
468 0           return ;
469             };
470 0           return $self;
471             }
472              
473              
474             =head1 NOTES
475              
476             This module is designed to make it simple, easy and quite fast to code your
477             design in perl. If for any reason you feel that it doesn't achieve these goals
478             then please let me know. I am here to help. All constructive criticisms are
479             also welcomed.
480              
481             =cut
482              
483              
484             =head1 AUTHOR
485              
486             Kevin Treleaven treleaven I net>
487              
488             =cut
489              
490              
491             1;