This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Add Module::Pluggable
[perl5.git] / lib / Module / Pluggable.pm
... / ...
CommitLineData
1package Module::Pluggable;
2
3use strict;
4use vars qw($VERSION);
5use 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.4';
13
14sub 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
711;
72
73=pod
74
75=head1 NAME
76
77Module::Pluggable - automatically give your module the ability to have plugins
78
79=head1 SYNOPSIS
80
81
82Simple use Module::Pluggable -
83
84 package MyClass;
85 use Module::Pluggable;
86
87
88and 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
97Why would you want to do this? Say you have something that wants to pass an
98object to a number of different plugins in turn. For example you may
99want to extract meta-data from every email you get sent and do something
100with it. Plugins make sense here because then you can keep adding new
101meta data parsers and all the logic and docs for each one will be
102self contained and new handlers are easy to add without changing the
103core 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
126This can be trivally extended so that plugins could save the email
127somewhere and then no other plugin should try and do that.
128Simply have it so that the C<examine> method returns C<1> if
129it has saved the email somewhere. You might also wnat to be paranoid
130and 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
138And so on. The sky's the limit.
139
140
141=head1 DESCRIPTION
142
143Provides a simple but, hopefully, extensible way of having 'plugins' for
144your module. Obviously this isn't going to be the be all and end all of
145solutions but it works for me.
146
147Essentially all it does is export a method into your namespace that
148looks through a search path for .pm files and turn those into class names.
149
150Optionally it instantiates those classes for you.
151
152=head1 ADVANCED USAGE
153
154
155Alternatively, if you don't want to use 'plugins' as the method ...
156
157 package MyClass;
158 use Module::Pluggable sub_name => 'foo';
159
160
161and then later ...
162
163 my @plugins = $mc->foo();
164
165
166Or if you want to look in another namespace
167
168 package MyClass;
169 use Module::Pluggable search_path => ['Acme::MyClass::Plugin', 'MyClass::Extend'];
170
171or directory
172
173 use Module::Pluggable search_dirs => ['mylibs/Foo'];
174
175
176Or if you want to instantiate each plugin rather than just return the name
177
178 package MyClass;
179 use Module::Pluggable instantiate => 'new';
180
181and 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
188alternatively you can just require the module without instantiating it
189
190 package MyClass;
191 use Module::Pluggable require => 1;
192
193since 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
200You can limit the plugins loaded using the except option, either as a string,
201array ref or regex
202
203 package MyClass;
204 use Module::Pluggable except => 'MyClass::Plugin::Foo';
205
206or
207
208 package MyClass;
209 use Module::Pluggable except => ['MyClass::Plugin::Foo', 'MyClass::Plugin::Bar'];
210
211or
212
213 package MyClass;
214 use Module::Pluggable except => qr/^MyClass::Plugin::(Foo|Bar)$/;
215
216
217and similarly for only which will only load plugins which match.
218
219Remember 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
225and then later ...
226
227 my @filters = $self->filters;
228 my @plugins = $self->plugins;
229
230=head1 INNER PACKAGES
231
232If you have, for example, a file B<lib/Something/Plugin/Foo.pm> that
233contains package definitions for both C<Something::Plugin::Foo> and
234C<Something::Plugin::Bar> then as long as you either have either
235the B<require> or B<instantiate> option set then we'll also find
236C<Something::Plugin::Bar>. Nifty!
237
238=head1 OPTIONS
239
240You can pass a hash of options when importing this module.
241
242The options can be ...
243
244=head2 sub_name
245
246The name of the subroutine to create in your namespace.
247
248By default this is 'plugins'
249
250=head2 search_path
251
252An array ref of namespaces to look in.
253
254=head2 search_dirs
255
256An array ref of directorys to look in before @INC.
257
258=head2 instantiate
259
260Call this method on the class. In general this will probably be 'new'
261but it can be whatever you want. Whatever arguments are passed to 'plugins'
262will be passed to the method.
263
264The default is 'undef' i.e just return the class name.
265
266=head2 require
267
268Just require the class, don't instantiate (overrides 'instantiate');
269
270=head2 inner
271
272If set to 0 will B<not> search inner packages.
273If set to 1 will override C<require>.
274
275=head2 only
276
277Takes a string, array ref or regex describing the names of the only plugins to
278return. Whilst this may seem perverse ... well, it is. But it also
279makes sense. Trust me.
280
281=head2 except
282
283Similar to C<only> it takes a description of plugins to exclude
284from returning. This is slightly less perverse.
285
286=head2 package
287
288This is for use by extension modules which build on C<Module::Pluggable>:
289passing a C<package> option allows you to place the plugin method in a
290different package other than your own.
291
292=head2 file_regex
293
294By default C<Module::Pluggable> only looks for I<.pm> files.
295
296By 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
306The method C<search_path> is exported into you namespace as well.
307You can call that at any time to change or replace the
308search_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
317This does everything I need and I can't really think of any other
318features I want to add. Famous last words of course
319
320Recently tried fixed to find inner packages and to make it
321'just work' with PAR but there are still some issues.
322
323
324However suggestions (and patches) are welcome.
325
326=head1 AUTHOR
327
328Simon Wistow <simon@thegestalt.org>
329
330=head1 COPYING
331
332Copyright, 2006 Simon Wistow
333
334Distributed under the same terms as Perl itself.
335
336=head1 BUGS
337
338None known.
339
340=head1 SEE ALSO
341
342L<File::Spec>, L<File::Find>, L<File::Basename>, L<Class::Factory::Util>, L<Module::Pluggable::Ordered>
343
344=cut
345
346