[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 package Module::Pluggable; 2 3 use strict; 4 use vars qw($VERSION); 5 use Module::Pluggable::Object; 6 7 # ObQuote: 8 # Bob Porter: Looks like you've been missing a lot of work lately. 9 # Peter Gibbons: I wouldn't say I've been missing it, Bob! 10 11 12 $VERSION = '3.6'; 13 14 sub import { 15 my $class = shift; 16 my %opts = @_; 17 18 my ($pkg, $file) = caller; 19 # the default name for the method is 'plugins' 20 my $sub = $opts{'sub_name'} || 'plugins'; 21 # get our package 22 my ($package) = $opts{'package'} || $pkg; 23 $opts{filename} = $file; 24 $opts{package} = $package; 25 26 27 my $finder = Module::Pluggable::Object->new(%opts); 28 my $subroutine = sub { my $self = shift; return $finder->plugins(@_) }; 29 30 my $searchsub = sub { 31 my $self = shift; 32 my ($action,@paths) = @_; 33 34 $finder->{'search_path'} = ["$package}::Plugin"] if ($action eq 'add' and not $finder->{'search_path'} ); 35 push @{$finder->{'search_path'}}, @paths if ($action eq 'add'); 36 $finder->{'search_path'} = \@paths if ($action eq 'new'); 37 return $finder->{'search_path'}; 38 }; 39 40 41 my $onlysub = sub { 42 my ($self, $only) = @_; 43 44 if (defined $only) { 45 $finder->{'only'} = $only; 46 }; 47 48 return $finder->{'only'}; 49 }; 50 51 my $exceptsub = sub { 52 my ($self, $except) = @_; 53 54 if (defined $except) { 55 $finder->{'except'} = $except; 56 }; 57 58 return $finder->{'except'}; 59 }; 60 61 62 no strict 'refs'; 63 no warnings 'redefine'; 64 *{"$package\::$sub"} = $subroutine; 65 *{"$package\::search_path"} = $searchsub; 66 *{"$package\::only"} = $onlysub; 67 *{"$package\::except"} = $exceptsub; 68 69 } 70 71 1; 72 73 =pod 74 75 =head1 NAME 76 77 Module::Pluggable - automatically give your module the ability to have plugins 78 79 =head1 SYNOPSIS 80 81 82 Simple use Module::Pluggable - 83 84 package MyClass; 85 use Module::Pluggable; 86 87 88 and then later ... 89 90 use MyClass; 91 my $mc = MyClass->new(); 92 # returns the names of all plugins installed under MyClass::Plugin::* 93 my @plugins = $mc->plugins(); 94 95 =head1 EXAMPLE 96 97 Why would you want to do this? Say you have something that wants to pass an 98 object to a number of different plugins in turn. For example you may 99 want to extract meta-data from every email you get sent and do something 100 with it. Plugins make sense here because then you can keep adding new 101 meta data parsers and all the logic and docs for each one will be 102 self contained and new handlers are easy to add without changing the 103 core code. For that, you might do something like ... 104 105 package Email::Examiner; 106 107 use strict; 108 use Email::Simple; 109 use Module::Pluggable require => 1; 110 111 sub handle_email { 112 my $self = shift; 113 my $email = shift; 114 115 foreach my $plugin ($self->plugins) { 116 $plugin->examine($email); 117 } 118 119 return 1; 120 } 121 122 123 124 .. and all the plugins will get a chance in turn to look at it. 125 126 This can be trivally extended so that plugins could save the email 127 somewhere and then no other plugin should try and do that. 128 Simply have it so that the C<examine> method returns C<1> if 129 it has saved the email somewhere. You might also wnat to be paranoid 130 and check to see if the plugin has an C<examine> method. 131 132 foreach my $plugin ($self->plugins) { 133 next unless $plugin->can('examine'); 134 last if $plugin->examine($email); 135 } 136 137 138 And so on. The sky's the limit. 139 140 141 =head1 DESCRIPTION 142 143 Provides a simple but, hopefully, extensible way of having 'plugins' for 144 your module. Obviously this isn't going to be the be all and end all of 145 solutions but it works for me. 146 147 Essentially all it does is export a method into your namespace that 148 looks through a search path for .pm files and turn those into class names. 149 150 Optionally it instantiates those classes for you. 151 152 =head1 ADVANCED USAGE 153 154 155 Alternatively, if you don't want to use 'plugins' as the method ... 156 157 package MyClass; 158 use Module::Pluggable sub_name => 'foo'; 159 160 161 and then later ... 162 163 my @plugins = $mc->foo(); 164 165 166 Or if you want to look in another namespace 167 168 package MyClass; 169 use Module::Pluggable search_path => ['Acme::MyClass::Plugin', 'MyClass::Extend']; 170 171 or directory 172 173 use Module::Pluggable search_dirs => ['mylibs/Foo']; 174 175 176 Or if you want to instantiate each plugin rather than just return the name 177 178 package MyClass; 179 use Module::Pluggable instantiate => 'new'; 180 181 and then 182 183 # whatever is passed to 'plugins' will be passed 184 # to 'new' for each plugin 185 my @plugins = $mc->plugins(@options); 186 187 188 alternatively you can just require the module without instantiating it 189 190 package MyClass; 191 use Module::Pluggable require => 1; 192 193 since requiring automatically searches inner packages, which may not be desirable, you can turn this off 194 195 196 package MyClass; 197 use Module::Pluggable require => 1, inner => 0; 198 199 200 You can limit the plugins loaded using the except option, either as a string, 201 array ref or regex 202 203 package MyClass; 204 use Module::Pluggable except => 'MyClass::Plugin::Foo'; 205 206 or 207 208 package MyClass; 209 use Module::Pluggable except => ['MyClass::Plugin::Foo', 'MyClass::Plugin::Bar']; 210 211 or 212 213 package MyClass; 214 use Module::Pluggable except => qr/^MyClass::Plugin::(Foo|Bar)$/; 215 216 217 and similarly for only which will only load plugins which match. 218 219 Remember you can use the module more than once 220 221 package MyClass; 222 use Module::Pluggable search_path => 'MyClass::Filters' sub_name => 'filters'; 223 use Module::Pluggable search_path => 'MyClass::Plugins' sub_name => 'plugins'; 224 225 and then later ... 226 227 my @filters = $self->filters; 228 my @plugins = $self->plugins; 229 230 =head1 INNER PACKAGES 231 232 If you have, for example, a file B<lib/Something/Plugin/Foo.pm> that 233 contains package definitions for both C<Something::Plugin::Foo> and 234 C<Something::Plugin::Bar> then as long as you either have either 235 the B<require> or B<instantiate> option set then we'll also find 236 C<Something::Plugin::Bar>. Nifty! 237 238 =head1 OPTIONS 239 240 You can pass a hash of options when importing this module. 241 242 The options can be ... 243 244 =head2 sub_name 245 246 The name of the subroutine to create in your namespace. 247 248 By default this is 'plugins' 249 250 =head2 search_path 251 252 An array ref of namespaces to look in. 253 254 =head2 search_dirs 255 256 An array ref of directorys to look in before @INC. 257 258 =head2 instantiate 259 260 Call this method on the class. In general this will probably be 'new' 261 but it can be whatever you want. Whatever arguments are passed to 'plugins' 262 will be passed to the method. 263 264 The default is 'undef' i.e just return the class name. 265 266 =head2 require 267 268 Just require the class, don't instantiate (overrides 'instantiate'); 269 270 =head2 inner 271 272 If set to 0 will B<not> search inner packages. 273 If set to 1 will override C<require>. 274 275 =head2 only 276 277 Takes a string, array ref or regex describing the names of the only plugins to 278 return. Whilst this may seem perverse ... well, it is. But it also 279 makes sense. Trust me. 280 281 =head2 except 282 283 Similar to C<only> it takes a description of plugins to exclude 284 from returning. This is slightly less perverse. 285 286 =head2 package 287 288 This is for use by extension modules which build on C<Module::Pluggable>: 289 passing a C<package> option allows you to place the plugin method in a 290 different package other than your own. 291 292 =head2 file_regex 293 294 By default C<Module::Pluggable> only looks for I<.pm> files. 295 296 By supplying a new C<file_regex> then you can change this behaviour e.g 297 298 file_regex => qr/\.plugin$/ 299 300 301 302 =head1 METHODs 303 304 =head2 search_path 305 306 The method C<search_path> is exported into you namespace as well. 307 You can call that at any time to change or replace the 308 search_path. 309 310 $self->search_path( add => "New::Path" ); # add 311 $self->search_path( new => "New::Path" ); # replace 312 313 314 315 =head1 FUTURE PLANS 316 317 This does everything I need and I can't really think of any other 318 features I want to add. Famous last words of course 319 320 Recently tried fixed to find inner packages and to make it 321 'just work' with PAR but there are still some issues. 322 323 324 However suggestions (and patches) are welcome. 325 326 =head1 AUTHOR 327 328 Simon Wistow <simon@thegestalt.org> 329 330 =head1 COPYING 331 332 Copyright, 2006 Simon Wistow 333 334 Distributed under the same terms as Perl itself. 335 336 =head1 BUGS 337 338 None known. 339 340 =head1 SEE ALSO 341 342 L<File::Spec>, L<File::Find>, L<File::Basename>, L<Class::Factory::Util>, L<Module::Pluggable::Ordered> 343 344 =cut 345 346
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Tue Mar 17 22:47:18 2015 | Cross-referenced by PHPXref 0.7.1 |