1 package Module::Pluggable::Object;
6 use File::Spec::Functions qw(splitdir catdir curdir catfile abs2rel);
7 use Carp qw(croak carp);
8 use Devel::InnerPackage;
10 use vars qw($VERSION);
19 return bless \%opts, $class;
23 ### Eugggh, this code smells
24 ### This is what happens when you keep adding patches
32 $self->{'require'} = 1 if $self->{'inner'};
34 my $filename = $self->{'filename'};
35 my $pkg = $self->{'package'};
37 # automatically turn a scalar search path or namespace into a arrayref
38 for (qw(search_path search_dirs)) {
39 $self->{$_} = [ $self->{$_} ] if exists $self->{$_} && !ref($self->{$_});
45 # default search path is '<Module>::<Name>::Plugin'
46 $self->{'search_path'} = ["${pkg}::Plugin"] unless $self->{'search_path'};
52 # check to see if we're running under test
53 my @SEARCHDIR = exists $INC{"blib.pm"} && defined $filename && $filename =~ m!(^|/)blib/! ? grep {/blib/} @INC : @INC;
55 # add any search_dir params
56 unshift @SEARCHDIR, @{$self->{'search_dirs'}} if defined $self->{'search_dirs'};
59 my @plugins = $self->search_directories(@SEARCHDIR);
61 # push @plugins, map { print STDERR "$_\n"; $_->require } list_packages($_) for (@{$self->{'search_path'}});
63 # return blank unless we've found anything
64 return () unless @plugins;
73 if (defined $self->{'only'}) {
74 if (ref($self->{'only'}) eq 'ARRAY') {
75 %only = map { $_ => 1 } @{$self->{'only'}};
76 } elsif (ref($self->{'only'}) eq 'Regexp') {
77 $only = $self->{'only'}
78 } elsif (ref($self->{'only'}) eq '') {
79 $only{$self->{'only'}} = 1;
84 if (defined $self->{'except'}) {
85 if (ref($self->{'except'}) eq 'ARRAY') {
86 %except = map { $_ => 1 } @{$self->{'except'}};
87 } elsif (ref($self->{'except'}) eq 'Regexp') {
88 $except = $self->{'except'}
89 } elsif (ref($self->{'except'}) eq '') {
90 $except{$self->{'except'}} = 1;
96 # probably not necessary but hey ho
99 next if (keys %only && !$only{$_} );
100 next unless (!defined $only || m!$only! );
102 next if (keys %except && $except{$_} );
103 next if (defined $except && m!$except! );
107 # are we instantiating or requring?
108 if (defined $self->{'instantiate'}) {
109 my $method = $self->{'instantiate'};
110 return map { ($_->can($method)) ? $_->$method(@_) : () } keys %plugins;
112 # no? just return the names
113 return keys %plugins;
119 sub search_directories {
124 # go through our @INC
125 foreach my $dir (@SEARCHDIR) {
126 push @plugins, $self->search_paths($dir);
138 my $file_regex = $self->{'file_regex'} || qr/\.pm$/;
141 # and each directory in our search path
142 foreach my $searchpath (@{$self->{'search_path'}}) {
143 # create the search directory in a cross platform goodness way
144 my $sp = catdir($dir, (split /::/, $searchpath));
146 # if it doesn't exist or it's not a dir then skip it
147 next unless ( -e $sp && -d _ ); # Use the cached stat the second time
149 my @files = $self->find_files($sp);
151 # foreach one we've found
152 foreach my $file (@files) {
153 # untaint the file; accept .pm only
154 next unless ($file) = ($file =~ /(.*$file_regex)$/);
155 # parse the file to get the name
156 my ($name, $directory, $suffix) = fileparse($file, $file_regex);
158 next if (!$self->{include_editor_junk} && $self->_is_editor_junk($name));
160 $directory = abs2rel($directory, $sp);
162 # If we have a mixed-case package name, assume case has been preserved
163 # correctly. Otherwise, root through the file to locate the case-preserved
164 # version of the package name.
166 if ( $name eq lc($name) || $name eq uc($name) ) {
167 my $pkg_file = catfile($sp, $directory, "$name$suffix");
168 open PKGFILE, "<$pkg_file" or die "search_paths: Can't open $pkg_file: $!";
170 while ( my $line = <PKGFILE> ) {
171 $in_pod = 1 if $line =~ m/^=\w/;
172 $in_pod = 0 if $line =~ /^=cut/;
173 next if ($in_pod || $line =~ /^=cut/); # skip pod text
174 next if $line =~ /^\s*#/; # and comments
175 if ( $line =~ m/^\s*package\s+(.*::)?($name)\s*;/i ) {
176 @pkg_dirs = split /::/, $1;
184 # then create the class name in a cross platform way
185 $directory =~ s/^[a-z]://i if($^O =~ /MSWin32|dos/); # remove volume
188 ($directory) = ($directory =~ /(.*)/);
189 @dirs = grep(length($_), splitdir($directory))
190 unless $directory eq curdir();
191 for my $d (reverse @dirs) {
192 my $pkg_dir = pop @pkg_dirs;
193 last unless defined $pkg_dir;
194 $d =~ s/\Q$pkg_dir\E/$pkg_dir/i; # Correct case
199 my $plugin = join '::', $searchpath, @dirs, $name;
201 next unless $plugin =~ m!(?:[a-z\d]+)[a-z\d]!i;
203 my $err = $self->handle_finding_plugin($plugin);
204 carp "Couldn't require $plugin : $err" if $err;
206 push @plugins, $plugin;
209 # now add stuff that may have been in package
210 # NOTE we should probably use all the stuff we've been given already
211 # but then we can't unload it :(
212 push @plugins, $self->handle_innerpackages($searchpath) unless (exists $self->{inner} && !$self->{inner});
213 } # foreach $searchpath
218 sub _is_editor_junk {
222 # Emacs (and other Unix-y editors) leave temp files ending in a
224 return 1 if $name =~ /~$/;
225 # Emacs makes these files while a buffer is edited but not yet
227 return 1 if $name =~ /^\.#/;
228 # Vim can leave these files behind if it crashes.
229 return 1 if $name =~ /\.sw[po]$/;
234 sub handle_finding_plugin {
238 return unless (defined $self->{'instantiate'} || $self->{'require'});
239 $self->_require($plugin);
244 my $search_path = shift;
245 my $file_regex = $self->{'file_regex'} || qr/\.pm$/;
248 # find all the .pm files in it
249 # this isn't perfect and won't find multiple plugins per file
250 #my $cwd = Cwd::getcwd;
252 { # for the benefit of perl 5.6.1's Find, localize topic
254 File::Find::find( { no_chdir => 1,
256 # Inlined from File::Find::Rule C< name => '*.pm' >
257 return unless $File::Find::name =~ /$file_regex/;
258 (my $path = $File::Find::name) =~ s#^\\./##;
268 sub handle_innerpackages {
274 foreach my $plugin (Devel::InnerPackage::list_packages($path)) {
275 my $err = $self->handle_finding_plugin($plugin);
277 #next unless $INC{$plugin};
278 push @plugins, $plugin;
289 eval "CORE::require $pack";
300 Module::Pluggable::Object - automatically give your module the ability to have plugins
305 Simple use Module::Pluggable -
308 use Module::Pluggable::Object;
310 my $finder = Module::Pluggable::Object->new(%opts);
311 print "My plugins are: ".join(", ", $finder->plugins)."\n";
315 Provides a simple but, hopefully, extensible way of having 'plugins' for
316 your module. Obviously this isn't going to be the be all and end all of
317 solutions but it works for me.
319 Essentially all it does is export a method into your namespace that
320 looks through a search path for .pm files and turn those into class names.
322 Optionally it instantiates those classes for you.
324 This object is wrapped by C<Module::Pluggable>. If you want to do something
325 odd or add non-general special features you're probably best to wrap this
326 and produce your own subclass.
330 See the C<Module::Pluggable> docs.
334 Simon Wistow <simon@thegestalt.org>
338 Copyright, 2006 Simon Wistow
340 Distributed under the same terms as Perl itself.