File Coverage

inc/Module/Pluggable/Object.pm
Criterion Covered Total %
statement 173 230 75.2
branch 49 122 40.1
condition 28 63 44.4
subroutine 21 23 91.3
pod 0 8 0.0
total 271 446 60.7


line stmt bran cond sub pod time code
1             #line 1
2             package Module::Pluggable::Object;
3 4     4   1774  
  4         9  
  4         110  
4 4     4   22 use strict;
  4         10  
  4         70  
5 4     4   23 use File::Find ();
  4         9  
  4         344  
6 4     4   1722 use File::Basename;
  4         14  
  4         352  
7 4     4   31 use File::Spec::Functions qw(splitdir catdir curdir catfile abs2rel);
  4         8  
  4         272  
8 4     4   1767 use Carp qw(croak carp confess);
  4         14  
  4         192  
9 4     4   26 use Devel::InnerPackage;
  4         8  
  4         208  
10             use vars qw($VERSION $MR);
11 4     4   25  
  4         13  
  4         23  
12             use if $] > 5.017, 'deprecate';
13              
14             $VERSION = '5.2';
15              
16 4     4   30 BEGIN {
  4         2069  
17 4 50       7475 eval { require Module::Runtime };
18 4         31 unless ($@) {
19             Module::Runtime->import('require_module');
20             } else {
21 0         0 *require_module = sub {
22 0         0 my $module = shift;
23 0         0 my $path = $module . ".pm";
24 0         0 $path =~ s{::}{/}g;
25 0         0 require $path;
26             };
27             }
28             }
29              
30              
31 9     9 0 188 sub new {
32 9         34 my $class = shift;
33             my %opts = @_;
34 9         46  
35             return bless \%opts, $class;
36              
37             }
38              
39             ### Eugggh, this code smells
40             ### This is what happens when you keep adding patches
41             ### *sigh*
42              
43              
44 38     38 0 369 sub plugins {
45 38         91 my $self = shift;
46             my @args = @_;
47              
48 38 50       186 # override 'require'
49             $self->{'require'} = 1 if $self->{'inner'};
50 38         110  
51 38         100 my $filename = $self->{'filename'};
52             my $pkg = $self->{'package'};
53              
54 38         184 # Get the exception params instantiated
55             $self->_setup_exceptions;
56              
57 38         198 # automatically turn a scalar search path or namespace into a arrayref
58 76 100 100     428 for (qw(search_path search_dirs)) {
59             $self->{$_} = [ $self->{$_} ] if exists $self->{$_} && !ref($self->{$_});
60             }
61              
62 38   50     141 # default search path is '::::Plugin'
63             $self->{'search_path'} ||= ["${pkg}::Plugin"];
64              
65 38   100 0   190 # default error handler
  0         0  
  0         0  
  0         0  
66 38   100 0   152 $self->{'on_require_error'} ||= sub { my ($plugin, $err) = @_; carp "Couldn't require $plugin : $err"; return 0 };
  0         0  
  0         0  
  0         0  
67             $self->{'on_instantiate_error'} ||= sub { my ($plugin, $err) = @_; carp "Couldn't instantiate $plugin: $err"; return 0 };
68              
69 38 100       136 # default whether to follow symlinks
70             $self->{'follow_symlinks'} = 1 unless exists $self->{'follow_symlinks'};
71              
72 38 100 66     595 # check to see if we're running under test
  407         1058  
73             my @SEARCHDIR = exists $INC{"blib.pm"} && defined $filename && $filename =~ m!(^|/)blib/! && !$self->{'force_search_all_paths'} ? grep {/blib/} @INC : @INC;
74              
75 38 50       138 # add any search_dir params
  0         0  
76             unshift @SEARCHDIR, @{$self->{'search_dirs'}} if defined $self->{'search_dirs'};
77              
78 38         236 # set our @INC up to include and prefer our search_dirs if necessary
79 38 50       88 my @tmp = @INC;
  38         184  
80 38 50       145 unshift @tmp, @{$self->{'search_dirs'} || []};
81             local @INC = @tmp if defined $self->{'search_dirs'};
82 38         179  
83 38         87 my @plugins = $self->search_directories(@SEARCHDIR);
  38         297  
84 38         71 push(@plugins, $self->handle_inc_hooks($_, @SEARCHDIR)) for @{$self->{'search_path'}};
  38         134  
85             push(@plugins, $self->handle_innerpackages($_)) for @{$self->{'search_path'}};
86            
87 38 50       108 # return blank unless we've found anything
88             return () unless @plugins;
89              
90             # remove duplicates
91 38         101 # probably not necessary but hey ho
92 38         83 my %plugins;
93 346 50       601 for(@plugins) {
94 346         737 next unless $self->_is_legit($_);
95             $plugins{$_} = 1;
96             }
97              
98 38 50       117 # are we instantiating or requiring?
99 0         0 if (defined $self->{'instantiate'}) {
100 0         0 my $method = $self->{'instantiate'};
101 0         0 my @objs = ();
102 0 0       0 foreach my $package (sort keys %plugins) {
103 0         0 next unless $package->can($method);
  0         0  
104 0 0       0 my $obj = eval { $package->$method(@_) };
105 0 0       0 $self->{'on_instantiate_error'}->($package, $@) if $@;
106             push @objs, $obj if $obj;
107 0         0 }
108             return @objs;
109             } else {
110 38         246 # no? just return the names
111 38         350 my @objs= sort keys %plugins;
112             return @objs;
113             }
114             }
115              
116 38     38   92 sub _setup_exceptions {
117             my $self = shift;
118 38         153  
119             my %only;
120 38         0 my %except;
121 38         0 my $only;
122             my $except;
123 38 50       137  
124 0 0       0 if (defined $self->{'only'}) {
    0          
    0          
125 0         0 if (ref($self->{'only'}) eq 'ARRAY') {
  0         0  
  0         0  
126             %only = map { $_ => 1 } @{$self->{'only'}};
127 0         0 } elsif (ref($self->{'only'}) eq 'Regexp') {
128             $only = $self->{'only'}
129 0         0 } elsif (ref($self->{'only'}) eq '') {
130             $only{$self->{'only'}} = 1;
131             }
132             }
133            
134 38 50       146  
135 0 0       0 if (defined $self->{'except'}) {
    0          
    0          
136 0         0 if (ref($self->{'except'}) eq 'ARRAY') {
  0         0  
  0         0  
137             %except = map { $_ => 1 } @{$self->{'except'}};
138 0         0 } elsif (ref($self->{'except'}) eq 'Regexp') {
139             $except = $self->{'except'}
140 0         0 } elsif (ref($self->{'except'}) eq '') {
141             $except{$self->{'except'}} = 1;
142             }
143 38         179 }
144 38         111 $self->{_exceptions}->{only_hash} = \%only;
145 38         96 $self->{_exceptions}->{only} = $only;
146 38         135 $self->{_exceptions}->{except_hash} = \%except;
147             $self->{_exceptions}->{except} = $except;
148            
149             }
150              
151 692     692   1031 sub _is_legit {
152 692         940 my $self = shift;
153 692 50       957 my $plugin = shift;
  692         1855  
154 692 50       1052 my %only = %{$self->{_exceptions}->{only_hash}||{}};
  692         1467  
155 692         1085 my %except = %{$self->{_exceptions}->{except_hash}||{}};
156 692         971 my $only = $self->{_exceptions}->{only};
157 692         2078 my $except = $self->{_exceptions}->{except};
158             my $depth = () = split '::', $plugin, -1;
159 692 50 33     1779  
160 692 50 33     1480 return 0 if (keys %only && !$only{$plugin} );
161             return 0 unless (!defined $only || $plugin =~ m!$only! );
162 692 0 33     1221  
163 692 50 33     1440 return 0 if (keys %except && $except{$plugin} );
164             return 0 if (defined $except && $plugin =~ m!$except! );
165 692 50 33     1419
166 692 50 33     1462 return 0 if defined $self->{max_depth} && $depth>$self->{max_depth};
167             return 0 if defined $self->{min_depth} && $depth<$self->{min_depth};
168 692         1754  
169             return 1;
170             }
171              
172 38     38 0 93 sub search_directories {
173 38         139 my $self = shift;
174             my @SEARCHDIR = @_;
175 38         62  
176             my @plugins;
177 38         110 # go through our @INC
178 149         380 foreach my $dir (@SEARCHDIR) {
179             push @plugins, $self->search_paths($dir);
180 38         195 }
181             return @plugins;
182             }
183              
184              
185 149     149 0 237 sub search_paths {
186 149         258 my $self = shift;
187 149         239 my $dir = shift;
188             my @plugins;
189 149   33     903  
190             my $file_regex = $self->{'file_regex'} || qr/\.pm$/;
191              
192              
193 149         281 # and each directory in our search path
  149         367  
194             foreach my $searchpath (@{$self->{'search_path'}}) {
195 149         1173 # create the search directory in a cross platform goodness way
196             my $sp = catdir($dir, (split /::/, $searchpath));
197              
198 149 100 66     3352 # if it doesn't exist or it's not a dir then skip it
199             next unless ( -e $sp && -d _ ); # Use the cached stat the second time
200 38         212  
201             my @files = $self->find_files($sp);
202              
203 38         135 # foreach one we've found
204             foreach my $file (@files) {
205 124 50       984 # untaint the file; accept .pm only
206             next unless ($file) = ($file =~ /(.*$file_regex)$/);
207 124         3287 # parse the file to get the name
208             my ($name, $directory, $suffix) = fileparse($file, $file_regex);
209 124 50 33     669  
210             next if (!$self->{include_editor_junk} && $self->_is_editor_junk($name));
211 124         461  
212             $directory = abs2rel($directory, $sp);
213              
214             # If we have a mixed-case package name, assume case has been preserved
215             # correctly. Otherwise, root through the file to locate the case-preserved
216 124         314 # version of the package name.
217 124 50 33     712 my @pkg_dirs = ();
218 0         0 if ( $name eq lc($name) || $name eq uc($name) ) {
219 0 0       0 my $pkg_file = catfile($sp, $directory, "$name$suffix");
220 0         0 open PKGFILE, "<$pkg_file" or die "search_paths: Can't open $pkg_file: $!";
221 0         0 my $in_pod = 0;
222 0 0       0 while ( my $line = ) {
223 0 0       0 $in_pod = 1 if $line =~ m/^=\w/;
224 0 0 0     0 $in_pod = 0 if $line =~ /^=cut/;
225 0 0       0 next if ($in_pod || $line =~ /^=cut/); # skip pod text
226 0 0       0 next if $line =~ /^\s*#/; # and comments
227 0 0       0 if ( $line =~ m/^\s*package\s+(.*::)?($name)\s*;/i ) {
228 0         0 @pkg_dirs = split /::/, $1 if defined $1;;
229 0         0 $name = $2;
230             last;
231             }
232 0         0 }
233             close PKGFILE;
234             }
235              
236 124 50       694 # then create the class name in a cross platform way
237 124         306 $directory =~ s/^[a-z]://i if($^O =~ /MSWin32|dos/); # remove volume
238 124 50       291 my @dirs = ();
239 124         470 if ($directory) {
240 124 100       354 ($directory) = ($directory =~ /(.*)/);
241             @dirs = grep(length($_), splitdir($directory))
242 124         271 unless $directory eq curdir();
243 11         20 for my $d (reverse @dirs) {
244 11 50       24 my $pkg_dir = pop @pkg_dirs;
245 0         0 last unless defined $pkg_dir;
246             $d =~ s/\Q$pkg_dir\E/$pkg_dir/i; # Correct case
247             }
248 0         0 } else {
249             $directory = "";
250 124         504 }
251             my $plugin = join '::', $searchpath, @dirs, $name;
252 124 50       459  
253             next unless $plugin =~ m!(?:[a-z\d]+)[a-z\d]*!i;
254 124         388  
255             $self->handle_finding_plugin($plugin, \@plugins)
256             }
257              
258             # now add stuff that may have been in package
259             # NOTE we should probably use all the stuff we've been given already
260 38         198 # but then we can't unload it :(
261             push @plugins, $self->handle_innerpackages($searchpath);
262             } # foreach $searchpath
263 149         775  
264             return @plugins;
265             }
266              
267 124     124   236 sub _is_editor_junk {
268 124         217 my $self = shift;
269             my $name = shift;
270              
271             # Emacs (and other Unix-y editors) leave temp files ending in a
272 124 50       364 # tilde as a backup.
273             return 1 if $name =~ /~$/;
274             # Emacs makes these files while a buffer is edited but not yet
275 124 50       325 # saved.
276             return 1 if $name =~ /^\.#/;
277 124 50       312 # Vim can leave these files behind if it crashes.
278             return 1 if $name =~ /\.sw[po]$/;
279 124         422  
280             return 0;
281             }
282              
283 346     346 0 526 sub handle_finding_plugin {
284 346         499 my $self = shift;
285 346         461 my $plugin = shift;
286 346   100     865 my $plugins = shift;
287             my $no_req = shift || 0;
288 346 50       730
289 346 100 66     1222 return unless $self->_is_legit($plugin);
290 13         25 unless (defined $self->{'instantiate'} || $self->{'require'}) {
291 13         38 push @$plugins, $plugin;
292             return;
293             }
294 333 50 0     666  
295 333 100       665 $self->{before_require}->($plugin) || return if defined $self->{before_require};
296 111         189 unless ($no_req) {
297 111         211 my $tmp = $@;
  111         359  
298 111         3079 my $res = eval { require_module($plugin) };
299 111         189 my $err = $@;
300 111 50       279 $@ = $tmp;
301 0 0       0 if ($err) {
302 0 0       0 if (defined $self->{on_require_error}) {
303             $self->{on_require_error}->($plugin, $err) || return;
304 0         0 } else {
305             return;
306             }
307             }
308 333 50 0     692 }
309 333         853 $self->{after_require}->($plugin) || return if defined $self->{after_require};
310             push @$plugins, $plugin;
311             }
312              
313 38     38 0 79 sub find_files {
314 38         85 my $self = shift;
315 38   33     285 my $search_path = shift;
316             my $file_regex = $self->{'file_regex'} || qr/\.pm$/;
317              
318              
319             # find all the .pm files in it
320             # this isn't perfect and won't find multiple plugins per file
321 38         97 #my $cwd = Cwd::getcwd;
322             my @files = ();
323 38         91 { # for the benefit of perl 5.6.1's Find, localize topic
  38         81  
324             local $_;
325             File::Find::find( { no_chdir => 1,
326             follow => $self->{'follow_symlinks'},
327             wanted => sub {
328 168 100   168   6380 # Inlined from File::Find::Rule C< name => '*.pm' >
329 124         299 return unless $File::Find::name =~ /$file_regex/;
330 124         3157 (my $path = $File::Find::name) =~ s#^\\./##;
331             push @files, $path;
332 38         5194 }
333             }, $search_path );
334             }
335 38         322 #chdir $cwd;
336             return @files;
337              
338             }
339              
340 38     38 0 84 sub handle_inc_hooks {
341 38         85 my $self = shift;
342 38         114 my $path = shift;
343             my @SEARCHDIR = @_;
344 38         66  
345 38         88 my @plugins;
346 149 50 33     382 for my $dir ( @SEARCHDIR ) {
  0         0  
347             next unless ref $dir && eval { $dir->can( 'files' ) };
348 0         0  
349 0         0 foreach my $plugin ( $dir->files ) {
350 0         0 $plugin =~ s/\.pm$//;
351 0 0       0 $plugin =~ s{/}{::}g;
352 0         0 next unless $plugin =~ m!^${path}::!;
353             $self->handle_finding_plugin( $plugin, \@plugins );
354             }
355 38         104 }
356             return @plugins;
357             }
358              
359 76     76 0 157 sub handle_innerpackages {
360 76 50 33     231 my $self = shift;
361             return () if (exists $self->{inner} && !$self->{inner});
362 76         142  
363 76         107 my $path = shift;
364             my @plugins;
365 76         294  
366 222         528 foreach my $plugin (Devel::InnerPackage::list_packages($path)) {
367             $self->handle_finding_plugin($plugin, \@plugins, 1);
368 76         295 }
369             return @plugins;
370              
371             }
372              
373             1;
374              
375             #line 428
376