File Coverage

inc/Module/Pluggable/Object.pm
Criterion Covered Total %
statement 112 154 72.7
branch 30 84 35.7
condition 15 42 35.7
subroutine 17 18 94.4
pod 0 7 0.0
total 174 305 57.0


line stmt bran cond sub pod time code
1             #line 1
2             package Module::Pluggable::Object;
3 5     5   35  
  5         10  
  5         260  
4 5     5   31 use strict;
  5         12  
  5         373  
5 5     5   33 use File::Find ();
  5         10  
  5         623  
6 5     5   6108 use File::Basename;
  5         4794  
  5         580  
7 5     5   36 use File::Spec::Functions qw(splitdir catdir curdir catfile abs2rel);
  5         15  
  5         287  
8 5     5   6640 use Carp qw(croak carp);
  5         26423  
  5         293  
9 5     5   5963 use Devel::InnerPackage;
  5         62089  
  5         403  
10 5     5   44 use Data::Dumper;
  5         1614  
  5         9638  
11             use vars qw($VERSION);
12              
13             $VERSION = '3.6';
14              
15              
16 3     3 0 6 sub new {
17 3         13 my $class = shift;
18             my %opts = @_;
19 3         16  
20             return bless \%opts, $class;
21              
22             }
23              
24             ### Eugggh, this code smells
25             ### This is what happens when you keep adding patches
26             ### *sigh*
27              
28              
29 6     6 0 12 sub plugins {
30             my $self = shift;
31              
32 6 50       53 # override 'require'
33             $self->{'require'} = 1 if $self->{'inner'};
34 6         12  
35 6         11 my $filename = $self->{'filename'};
36             my $pkg = $self->{'package'};
37              
38 6         12 # automatically turn a scalar search path or namespace into a arrayref
39 12 50 66     68 for (qw(search_path search_dirs)) {
40             $self->{$_} = [ $self->{$_} ] if exists $self->{$_} && !ref($self->{$_});
41             }
42              
43              
44              
45              
46 6 50       21 # default search path is '::::Plugin'
47             $self->{'search_path'} = ["${pkg}::Plugin"] unless $self->{'search_path'};
48              
49              
50             #my %opts = %$self;
51              
52              
53 6 50 33     84 # check to see if we're running under test
  0         0  
54             my @SEARCHDIR = exists $INC{"blib.pm"} && defined $filename && $filename =~ m!(^|/)blib/! ? grep {/blib/} @INC : @INC;
55              
56 6 50       18 # add any search_dir params
  0         0  
57             unshift @SEARCHDIR, @{$self->{'search_dirs'}} if defined $self->{'search_dirs'};
58              
59 6         24  
60             my @plugins = $self->search_directories(@SEARCHDIR);
61              
62             # push @plugins, map { print STDERR "$_\n"; $_->require } list_packages($_) for (@{$self->{'search_path'}});
63            
64 6 50       22 # return blank unless we've found anything
65             return () unless @plugins;
66              
67              
68 6         11 # exceptions
69             my %only;
70 0         0 my %except;
71 0         0 my $only;
72             my $except;
73 6 50       22  
74 0 0       0 if (defined $self->{'only'}) {
    0          
    0          
75 0         0 if (ref($self->{'only'}) eq 'ARRAY') {
  0         0  
  0         0  
76             %only = map { $_ => 1 } @{$self->{'only'}};
77 0         0 } elsif (ref($self->{'only'}) eq 'Regexp') {
78             $only = $self->{'only'}
79 0         0 } elsif (ref($self->{'only'}) eq '') {
80             $only{$self->{'only'}} = 1;
81             }
82             }
83            
84 6 50       31  
85 0 0       0 if (defined $self->{'except'}) {
    0          
    0          
86 0         0 if (ref($self->{'except'}) eq 'ARRAY') {
  0         0  
  0         0  
87             %except = map { $_ => 1 } @{$self->{'except'}};
88 0         0 } elsif (ref($self->{'except'}) eq 'Regexp') {
89             $except = $self->{'except'}
90 0         0 } elsif (ref($self->{'except'}) eq '') {
91             $except{$self->{'except'}} = 1;
92             }
93             }
94              
95              
96             # remove duplicates
97 6         21 # probably not necessary but hey ho
98 6         13 my %plugins;
99 21 50 33     54 for(@plugins) {
100 21 50 33     62 next if (keys %only && !$only{$_} );
101             next unless (!defined $only || m!$only! );
102 21 50 33     47  
103 21 50 33     45 next if (keys %except && $except{$_} );
104 21         48 next if (defined $except && m!$except! );
105             $plugins{$_} = 1;
106             }
107              
108 6 50       20 # are we instantiating or requring?
109 0         0 if (defined $self->{'instantiate'}) {
110 0 0       0 my $method = $self->{'instantiate'};
  0         0  
111             return map { ($_->can($method)) ? $_->$method(@_) : () } keys %plugins;
112             } else {
113 6         47 # no? just return the names
114             return keys %plugins;
115             }
116              
117              
118             }
119              
120 6     6 0 9 sub search_directories {
121 6         26 my $self = shift;
122             my @SEARCHDIR = @_;
123 6         19  
124             my @plugins;
125 6         13 # go through our @INC
126 66         140 foreach my $dir (@SEARCHDIR) {
127             push @plugins, $self->search_paths($dir);
128             }
129 6         29  
130             return @plugins;
131             }
132              
133              
134 66     66 0 79 sub search_paths {
135 66         80 my $self = shift;
136 66         61 my $dir = shift;
137             my @plugins;
138 66   33     386  
139             my $file_regex = $self->{'file_regex'} || qr/\.pm$/;
140              
141              
142 66         81 # and each directory in our search path
  66         128  
143             foreach my $searchpath (@{$self->{'search_path'}}) {
144 77         361 # create the search directory in a cross platform goodness way
145             my $sp = catdir($dir, (split /::/, $searchpath));
146              
147 77 100 66     2044 # if it doesn't exist or it's not a dir then skip it
148             next unless ( -e $sp && -d _ ); # Use the cached stat the second time
149 6         31  
150             my @files = $self->find_files($sp);
151              
152 6         15 # foreach one we've found
153             foreach my $file (@files) {
154 18 50       163 # untaint the file; accept .pm only
155             next unless ($file) = ($file =~ /(.*$file_regex)$/);
156 18         501 # parse the file to get the name
157             my ($name, $directory, $suffix) = fileparse($file, $file_regex);
158 18 50 33     95  
159             next if (!$self->{include_editor_junk} && $self->_is_editor_junk($name));
160 18         56  
161             $directory = abs2rel($directory, $sp);
162              
163             # If we have a mixed-case package name, assume case has been preserved
164             # correctly. Otherwise, root through the file to locate the case-preserved
165 18         1302 # version of the package name.
166 18 50 33     109 my @pkg_dirs = ();
167 0         0 if ( $name eq lc($name) || $name eq uc($name) ) {
168 0 0       0 my $pkg_file = catfile($sp, $directory, "$name$suffix");
169 0         0 open PKGFILE, "<$pkg_file" or die "search_paths: Can't open $pkg_file: $!";
170 0         0 my $in_pod = 0;
171 0 0       0 while ( my $line = ) {
172 0 0       0 $in_pod = 1 if $line =~ m/^=\w/;
173 0 0 0     0 $in_pod = 0 if $line =~ /^=cut/;
174 0 0       0 next if ($in_pod || $line =~ /^=cut/); # skip pod text
175 0 0       0 next if $line =~ /^\s*#/; # and comments
176 0         0 if ( $line =~ m/^\s*package\s+(.*::)?($name)\s*;/i ) {
177 0         0 @pkg_dirs = split /::/, $1;
178 0         0 $name = $2;
179             last;
180             }
181 0         0 }
182             close PKGFILE;
183             }
184              
185 18 50       81 # then create the class name in a cross platform way
186 18         25 $directory =~ s/^[a-z]://i if($^O =~ /MSWin32|dos/); # remove volume
187 18 50       32 my @dirs = ();
188 18         58 if ($directory) {
189 18 50       46 ($directory) = ($directory =~ /(.*)/);
190             @dirs = grep(length($_), splitdir($directory))
191 18         40 unless $directory eq curdir();
192 0         0 for my $d (reverse @dirs) {
193 0 0       0 my $pkg_dir = pop @pkg_dirs;
194 0         0 last unless defined $pkg_dir;
195             $d =~ s/\Q$pkg_dir\E/$pkg_dir/i; # Correct case
196             }
197 0         0 } else {
198             $directory = "";
199 18         44 }
200             my $plugin = join '::', $searchpath, @dirs, $name;
201 18 50       66  
202             next unless $plugin =~ m!(?:[a-z\d]+)[a-z\d]!i;
203 18         56  
204 18 50       32 my $err = $self->handle_finding_plugin($plugin);
205             carp "Couldn't require $plugin : $err" if $err;
206 18         47
207             push @plugins, $plugin;
208             }
209              
210             # now add stuff that may have been in package
211             # NOTE we should probably use all the stuff we've been given already
212 6 50 33     35 # but then we can't unload it :(
213             push @plugins, $self->handle_innerpackages($searchpath) unless (exists $self->{inner} && !$self->{inner});
214             } # foreach $searchpath
215 66         212  
216             return @plugins;
217             }
218              
219 18     18   23 sub _is_editor_junk {
220 18         31 my $self = shift;
221             my $name = shift;
222              
223             # Emacs (and other Unix-y editors) leave temp files ending in a
224 18 50       40 # tilde as a backup.
225             return 1 if $name =~ /~$/;
226             # Emacs makes these files while a buffer is edited but not yet
227 18 50       38 # saved.
228             return 1 if $name =~ /^\.#/;
229 18 50       31 # Vim can leave these files behind if it crashes.
230             return 1 if $name =~ /\.sw[po]$/;
231 18         61  
232             return 0;
233             }
234              
235 21     21 0 25 sub handle_finding_plugin {
236 21         26 my $self = shift;
237             my $plugin = shift;
238 21 50 33     107  
239 0         0 return unless (defined $self->{'instantiate'} || $self->{'require'});
240             $self->_require($plugin);
241             }
242              
243 6     6 0 9 sub find_files {
244 6         10 my $self = shift;
245 6   33     44 my $search_path = shift;
246             my $file_regex = $self->{'file_regex'} || qr/\.pm$/;
247              
248              
249             # find all the .pm files in it
250             # this isn't perfect and won't find multiple plugins per file
251 6         20 #my $cwd = Cwd::getcwd;
252             my @files = ();
253 6         240 { # for the benefit of perl 5.6.1's Find, localize topic
  6         11  
254             local $_;
255             File::Find::find( { no_chdir => 1,
256             wanted => sub {
257 24 100   24   623 # Inlined from File::Find::Rule C< name => '*.pm' >
258 18         27 return unless $File::Find::name =~ /$file_regex/;
259 18         203 (my $path = $File::Find::name) =~ s#^\\./##;
260             push @files, $path;
261 6         453 }
262             }, $search_path );
263             }
264 6         48 #chdir $cwd;
265             return @files;
266              
267             }
268              
269 6     6 0 11 sub handle_innerpackages {
270 6         8 my $self = shift;
271 6         9 my $path = shift;
272             my @plugins;
273              
274 6         27  
275 3         225 foreach my $plugin (Devel::InnerPackage::list_packages($path)) {
276             my $err = $self->handle_finding_plugin($plugin);
277             #next if $err;
278 3         7 #next unless $INC{$plugin};
279             push @plugins, $plugin;
280 6         103 }
281             return @plugins;
282              
283             }
284              
285              
286 0     0     sub _require {
287 0           my $self = shift;
288 0           my $pack = shift;
289 0           local $@;
290 0           eval "CORE::require $pack";
291             return $@;
292             }
293              
294              
295             1;
296              
297             #line 350
298