Filename | /Users/timbo/perl5/perlbrew/perls/perl-5.18.2/lib/site_perl/5.18.2/Module/Pluggable.pm |
Statements | Executed 34 statements in 766µs |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
1 | 1 | 1 | 1.84ms | 11.7ms | BEGIN@5 | Module::Pluggable::
1 | 1 | 1 | 33µs | 38µs | import | Module::Pluggable::
1 | 1 | 1 | 17µs | 35µs | BEGIN@3 | Module::Pluggable::
1 | 1 | 1 | 15µs | 113µs | BEGIN@7 | Module::Pluggable::
1 | 1 | 1 | 11µs | 22µs | BEGIN@66 | Module::Pluggable::
1 | 1 | 1 | 10µs | 55µs | BEGIN@4 | Module::Pluggable::
1 | 1 | 1 | 9µs | 20µs | BEGIN@67 | Module::Pluggable::
1 | 1 | 1 | 8µs | 1.29s | __ANON__[:32] | Module::Pluggable::
0 | 0 | 0 | 0s | 0s | __ANON__[:42] | Module::Pluggable::
0 | 0 | 0 | 0s | 0s | __ANON__[:53] | Module::Pluggable::
0 | 0 | 0 | 0s | 0s | __ANON__[:63] | Module::Pluggable::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | package Module::Pluggable; | ||||
2 | |||||
3 | 2 | 32µs | 2 | 52µs | # spent 35µs (17+18) within Module::Pluggable::BEGIN@3 which was called:
# once (17µs+18µs) by Perl::Critic::PolicyFactory::import at line 3 # spent 35µs making 1 call to Module::Pluggable::BEGIN@3
# spent 18µs making 1 call to strict::import |
4 | 2 | 34µs | 2 | 99µs | # spent 55µs (10+44) within Module::Pluggable::BEGIN@4 which was called:
# once (10µs+44µs) by Perl::Critic::PolicyFactory::import at line 4 # spent 55µs making 1 call to Module::Pluggable::BEGIN@4
# spent 44µs making 1 call to vars::import |
5 | 2 | 119µs | 1 | 11.7ms | # spent 11.7ms (1.84+9.83) within Module::Pluggable::BEGIN@5 which was called:
# once (1.84ms+9.83ms) by Perl::Critic::PolicyFactory::import at line 5 # spent 11.7ms making 1 call to Module::Pluggable::BEGIN@5 |
6 | |||||
7 | 2 | 341µs | 2 | 116µs | # spent 113µs (15+98) within Module::Pluggable::BEGIN@7 which was called:
# once (15µs+98µs) by Perl::Critic::PolicyFactory::import at line 7 # spent 113µs making 1 call to Module::Pluggable::BEGIN@7
# spent 3µs making 1 call to if::import |
8 | |||||
9 | # ObQuote: | ||||
10 | # Bob Porter: Looks like you've been missing a lot of work lately. | ||||
11 | # Peter Gibbons: I wouldn't say I've been missing it, Bob! | ||||
12 | |||||
13 | |||||
14 | 1 | 700ns | $VERSION = '5.1'; | ||
15 | 1 | 100ns | $FORCE_SEARCH_ALL_PATHS = 0; | ||
16 | |||||
17 | # spent 38µs (33+6) within Module::Pluggable::import which was called:
# once (33µs+6µs) by Perl::Critic::PolicyFactory::import at line 59 of Perl/Critic/PolicyFactory.pm | ||||
18 | 1 | 400ns | my $class = shift; | ||
19 | 1 | 4µs | 1 | 2µs | my %opts = @_; # spent 2µs making 1 call to Readonly::Scalar::FETCH |
20 | |||||
21 | 1 | 1µs | my ($pkg, $file) = caller; | ||
22 | # the default name for the method is 'plugins' | ||||
23 | 1 | 600ns | my $sub = $opts{'sub_name'} || 'plugins'; | ||
24 | # get our package | ||||
25 | 1 | 300ns | my ($package) = $opts{'package'} || $pkg; | ||
26 | 1 | 400ns | $opts{filename} = $file; | ||
27 | 1 | 100ns | $opts{package} = $package; | ||
28 | 1 | 500ns | $opts{force_search_all_paths} = $FORCE_SEARCH_ALL_PATHS unless exists $opts{force_search_all_paths}; | ||
29 | |||||
30 | |||||
31 | 1 | 3µs | 1 | 4µs | my $finder = Module::Pluggable::Object->new(%opts); # spent 4µs making 1 call to Module::Pluggable::Object::new |
32 | 3 | 8µs | 1 | 1.29s | # spent 1.29s (8µs+1.29) within Module::Pluggable::__ANON__[/Users/timbo/perl5/perlbrew/perls/perl-5.18.2/lib/site_perl/5.18.2/Module/Pluggable.pm:32] which was called:
# once (8µs+1.29s) by Perl::Critic::PolicyFactory::import at line 61 of Perl/Critic/PolicyFactory.pm # spent 1.29s making 1 call to Module::Pluggable::Object::plugins |
33 | |||||
34 | my $searchsub = sub { | ||||
35 | my $self = shift; | ||||
36 | my ($action,@paths) = @_; | ||||
37 | |||||
38 | $finder->{'search_path'} = ["${package}::Plugin"] if ($action eq 'add' and not $finder->{'search_path'} ); | ||||
39 | push @{$finder->{'search_path'}}, @paths if ($action eq 'add'); | ||||
40 | $finder->{'search_path'} = \@paths if ($action eq 'new'); | ||||
41 | return $finder->{'search_path'}; | ||||
42 | 1 | 2µs | }; | ||
43 | |||||
44 | |||||
45 | my $onlysub = sub { | ||||
46 | my ($self, $only) = @_; | ||||
47 | |||||
48 | if (defined $only) { | ||||
49 | $finder->{'only'} = $only; | ||||
50 | }; | ||||
51 | |||||
52 | return $finder->{'only'}; | ||||
53 | 1 | 1µs | }; | ||
54 | |||||
55 | my $exceptsub = sub { | ||||
56 | my ($self, $except) = @_; | ||||
57 | |||||
58 | if (defined $except) { | ||||
59 | $finder->{'except'} = $except; | ||||
60 | }; | ||||
61 | |||||
62 | return $finder->{'except'}; | ||||
63 | 1 | 800ns | }; | ||
64 | |||||
65 | |||||
66 | 2 | 23µs | 2 | 32µs | # spent 22µs (11+11) within Module::Pluggable::BEGIN@66 which was called:
# once (11µs+11µs) by Perl::Critic::PolicyFactory::import at line 66 # spent 22µs making 1 call to Module::Pluggable::BEGIN@66
# spent 10µs making 1 call to strict::unimport |
67 | 2 | 180µs | 2 | 32µs | # spent 20µs (9+12) within Module::Pluggable::BEGIN@67 which was called:
# once (9µs+12µs) by Perl::Critic::PolicyFactory::import at line 67 # spent 20µs making 1 call to Module::Pluggable::BEGIN@67
# spent 12µs making 1 call to warnings::unimport |
68 | |||||
69 | 1 | 2µs | *{"$package\::$sub"} = $subroutine; | ||
70 | 1 | 2µs | *{"$package\::search_path"} = $searchsub; | ||
71 | 1 | 1µs | *{"$package\::only"} = $onlysub; | ||
72 | 1 | 6µs | *{"$package\::except"} = $exceptsub; | ||
73 | |||||
74 | } | ||||
75 | |||||
76 | 1 | 2µs | 1; | ||
77 | |||||
78 | =pod | ||||
79 | |||||
80 | =head1 NAME | ||||
81 | |||||
82 | Module::Pluggable - automatically give your module the ability to have plugins | ||||
83 | |||||
84 | =head1 SYNOPSIS | ||||
85 | |||||
86 | |||||
87 | Simple use Module::Pluggable - | ||||
88 | |||||
89 | package MyClass; | ||||
90 | use Module::Pluggable; | ||||
91 | |||||
92 | |||||
93 | and then later ... | ||||
94 | |||||
95 | use MyClass; | ||||
96 | my $mc = MyClass->new(); | ||||
97 | # returns the names of all plugins installed under MyClass::Plugin::* | ||||
98 | my @plugins = $mc->plugins(); | ||||
99 | |||||
100 | =head1 EXAMPLE | ||||
101 | |||||
102 | Why would you want to do this? Say you have something that wants to pass an | ||||
103 | object to a number of different plugins in turn. For example you may | ||||
104 | want to extract meta-data from every email you get sent and do something | ||||
105 | with it. Plugins make sense here because then you can keep adding new | ||||
106 | meta data parsers and all the logic and docs for each one will be | ||||
107 | self contained and new handlers are easy to add without changing the | ||||
108 | core code. For that, you might do something like ... | ||||
109 | |||||
110 | package Email::Examiner; | ||||
111 | |||||
112 | use strict; | ||||
113 | use Email::Simple; | ||||
114 | use Module::Pluggable require => 1; | ||||
115 | |||||
116 | sub handle_email { | ||||
117 | my $self = shift; | ||||
118 | my $email = shift; | ||||
119 | |||||
120 | foreach my $plugin ($self->plugins) { | ||||
121 | $plugin->examine($email); | ||||
122 | } | ||||
123 | |||||
124 | return 1; | ||||
125 | } | ||||
126 | |||||
- - | |||||
129 | .. and all the plugins will get a chance in turn to look at it. | ||||
130 | |||||
131 | This can be trivially extended so that plugins could save the email | ||||
132 | somewhere and then no other plugin should try and do that. | ||||
133 | Simply have it so that the C<examine> method returns C<1> if | ||||
134 | it has saved the email somewhere. You might also want to be paranoid | ||||
135 | and check to see if the plugin has an C<examine> method. | ||||
136 | |||||
137 | foreach my $plugin ($self->plugins) { | ||||
138 | next unless $plugin->can('examine'); | ||||
139 | last if $plugin->examine($email); | ||||
140 | } | ||||
141 | |||||
142 | |||||
143 | And so on. The sky's the limit. | ||||
144 | |||||
145 | |||||
146 | =head1 DESCRIPTION | ||||
147 | |||||
148 | Provides a simple but, hopefully, extensible way of having 'plugins' for | ||||
149 | your module. Obviously this isn't going to be the be all and end all of | ||||
150 | solutions but it works for me. | ||||
151 | |||||
152 | Essentially all it does is export a method into your namespace that | ||||
153 | looks through a search path for .pm files and turn those into class names. | ||||
154 | |||||
155 | Optionally it instantiates those classes for you. | ||||
156 | |||||
157 | =head1 ADVANCED USAGE | ||||
158 | |||||
159 | Alternatively, if you don't want to use 'plugins' as the method ... | ||||
160 | |||||
161 | package MyClass; | ||||
162 | use Module::Pluggable sub_name => 'foo'; | ||||
163 | |||||
164 | |||||
165 | and then later ... | ||||
166 | |||||
167 | my @plugins = $mc->foo(); | ||||
168 | |||||
169 | |||||
170 | Or if you want to look in another namespace | ||||
171 | |||||
172 | package MyClass; | ||||
173 | use Module::Pluggable search_path => ['Acme::MyClass::Plugin', 'MyClass::Extend']; | ||||
174 | |||||
175 | or directory | ||||
176 | |||||
177 | use Module::Pluggable search_dirs => ['mylibs/Foo']; | ||||
178 | |||||
179 | |||||
180 | Or if you want to instantiate each plugin rather than just return the name | ||||
181 | |||||
182 | package MyClass; | ||||
183 | use Module::Pluggable instantiate => 'new'; | ||||
184 | |||||
185 | and then | ||||
186 | |||||
187 | # whatever is passed to 'plugins' will be passed | ||||
188 | # to 'new' for each plugin | ||||
189 | my @plugins = $mc->plugins(@options); | ||||
190 | |||||
191 | |||||
192 | alternatively you can just require the module without instantiating it | ||||
193 | |||||
194 | package MyClass; | ||||
195 | use Module::Pluggable require => 1; | ||||
196 | |||||
197 | since requiring automatically searches inner packages, which may not be desirable, you can turn this off | ||||
198 | |||||
199 | |||||
200 | package MyClass; | ||||
201 | use Module::Pluggable require => 1, inner => 0; | ||||
202 | |||||
203 | |||||
204 | You can limit the plugins loaded using the except option, either as a string, | ||||
205 | array ref or regex | ||||
206 | |||||
207 | package MyClass; | ||||
208 | use Module::Pluggable except => 'MyClass::Plugin::Foo'; | ||||
209 | |||||
210 | or | ||||
211 | |||||
212 | package MyClass; | ||||
213 | use Module::Pluggable except => ['MyClass::Plugin::Foo', 'MyClass::Plugin::Bar']; | ||||
214 | |||||
215 | or | ||||
216 | |||||
217 | package MyClass; | ||||
218 | use Module::Pluggable except => qr/^MyClass::Plugin::(Foo|Bar)$/; | ||||
219 | |||||
220 | |||||
221 | and similarly for only which will only load plugins which match. | ||||
222 | |||||
223 | Remember you can use the module more than once | ||||
224 | |||||
225 | package MyClass; | ||||
226 | use Module::Pluggable search_path => 'MyClass::Filters' sub_name => 'filters'; | ||||
227 | use Module::Pluggable search_path => 'MyClass::Plugins' sub_name => 'plugins'; | ||||
228 | |||||
229 | and then later ... | ||||
230 | |||||
231 | my @filters = $self->filters; | ||||
232 | my @plugins = $self->plugins; | ||||
233 | |||||
234 | =head1 PLUGIN SEARCHING | ||||
235 | |||||
236 | Every time you call 'plugins' the whole search path is walked again. This allows | ||||
237 | for dynamically loading plugins even at run time. However this can get expensive | ||||
238 | and so if you don't expect to want to add new plugins at run time you could do | ||||
239 | |||||
240 | |||||
241 | package Foo; | ||||
242 | use strict; | ||||
243 | use Module::Pluggable sub_name => '_plugins'; | ||||
244 | |||||
245 | our @PLUGINS; | ||||
246 | sub plugins { @PLUGINS ||= shift->_plugins } | ||||
247 | 1; | ||||
248 | |||||
249 | =head1 INNER PACKAGES | ||||
250 | |||||
251 | If you have, for example, a file B<lib/Something/Plugin/Foo.pm> that | ||||
252 | contains package definitions for both C<Something::Plugin::Foo> and | ||||
253 | C<Something::Plugin::Bar> then as long as you either have either | ||||
254 | the B<require> or B<instantiate> option set then we'll also find | ||||
255 | C<Something::Plugin::Bar>. Nifty! | ||||
256 | |||||
257 | =head1 OPTIONS | ||||
258 | |||||
259 | You can pass a hash of options when importing this module. | ||||
260 | |||||
261 | The options can be ... | ||||
262 | |||||
263 | =head2 sub_name | ||||
264 | |||||
265 | The name of the subroutine to create in your namespace. | ||||
266 | |||||
267 | By default this is 'plugins' | ||||
268 | |||||
269 | =head2 search_path | ||||
270 | |||||
271 | An array ref of namespaces to look in. | ||||
272 | |||||
273 | =head2 search_dirs | ||||
274 | |||||
275 | An array ref of directories to look in before @INC. | ||||
276 | |||||
277 | =head2 instantiate | ||||
278 | |||||
279 | Call this method on the class. In general this will probably be 'new' | ||||
280 | but it can be whatever you want. Whatever arguments are passed to 'plugins' | ||||
281 | will be passed to the method. | ||||
282 | |||||
283 | The default is 'undef' i.e just return the class name. | ||||
284 | |||||
285 | =head2 require | ||||
286 | |||||
287 | Just require the class, don't instantiate (overrides 'instantiate'); | ||||
288 | |||||
289 | =head2 inner | ||||
290 | |||||
291 | If set to 0 will B<not> search inner packages. | ||||
292 | If set to 1 will override C<require>. | ||||
293 | |||||
294 | =head2 only | ||||
295 | |||||
296 | Takes a string, array ref or regex describing the names of the only plugins to | ||||
297 | return. Whilst this may seem perverse ... well, it is. But it also | ||||
298 | makes sense. Trust me. | ||||
299 | |||||
300 | =head2 except | ||||
301 | |||||
302 | Similar to C<only> it takes a description of plugins to exclude | ||||
303 | from returning. This is slightly less perverse. | ||||
304 | |||||
305 | =head2 package | ||||
306 | |||||
307 | This is for use by extension modules which build on C<Module::Pluggable>: | ||||
308 | passing a C<package> option allows you to place the plugin method in a | ||||
309 | different package other than your own. | ||||
310 | |||||
311 | =head2 file_regex | ||||
312 | |||||
313 | By default C<Module::Pluggable> only looks for I<.pm> files. | ||||
314 | |||||
315 | By supplying a new C<file_regex> then you can change this behaviour e.g | ||||
316 | |||||
317 | file_regex => qr/\.plugin$/ | ||||
318 | |||||
319 | =head2 include_editor_junk | ||||
320 | |||||
321 | By default C<Module::Pluggable> ignores files that look like they were | ||||
322 | left behind by editors. Currently this means files ending in F<~> (~), | ||||
323 | the extensions F<.swp> or F<.swo>, or files beginning with F<.#>. | ||||
324 | |||||
325 | Setting C<include_editor_junk> changes C<Module::Pluggable> so it does | ||||
326 | not ignore any files it finds. | ||||
327 | |||||
328 | =head2 follow_symlinks | ||||
329 | |||||
330 | Whether, when searching directories, to follow symlinks. | ||||
331 | |||||
332 | Defaults to 1 i.e do follow symlinks. | ||||
333 | |||||
334 | =head2 min_depth, max_depth | ||||
335 | |||||
336 | This will allow you to set what 'depth' of plugin will be allowed. | ||||
337 | |||||
338 | So, for example, C<MyClass::Plugin::Foo> will have a depth of 3 and | ||||
339 | C<MyClass::Plugin::Foo::Bar> will have a depth of 4 so to only get the former | ||||
340 | (i.e C<MyClass::Plugin::Foo>) do | ||||
341 | |||||
342 | package MyClass; | ||||
343 | use Module::Pluggable max_depth => 3; | ||||
344 | |||||
345 | and to only get the latter (i.e C<MyClass::Plugin::Foo::Bar>) | ||||
346 | |||||
347 | package MyClass; | ||||
348 | use Module::Pluggable min_depth => 4; | ||||
349 | |||||
350 | |||||
351 | =head1 TRIGGERS | ||||
352 | |||||
353 | Various triggers can also be passed in to the options. | ||||
354 | |||||
355 | If any of these triggers return 0 then the plugin will not be returned. | ||||
356 | |||||
357 | =head2 before_require <plugin> | ||||
358 | |||||
359 | Gets passed the plugin name. | ||||
360 | |||||
361 | If 0 is returned then this plugin will not be required either. | ||||
362 | |||||
363 | =head2 on_require_error <plugin> <err> | ||||
364 | |||||
365 | Gets called when there's an error on requiring the plugin. | ||||
366 | |||||
367 | Gets passed the plugin name and the error. | ||||
368 | |||||
369 | The default on_require_error handler is to C<carp> the error and return 0. | ||||
370 | |||||
371 | =head2 on_instantiate_error <plugin> <err> | ||||
372 | |||||
373 | Gets called when there's an error on instantiating the plugin. | ||||
374 | |||||
375 | Gets passed the plugin name and the error. | ||||
376 | |||||
377 | The default on_instantiate_error handler is to C<carp> the error and return 0. | ||||
378 | |||||
379 | =head2 after_require <plugin> | ||||
380 | |||||
381 | Gets passed the plugin name. | ||||
382 | |||||
383 | If 0 is returned then this plugin will be required but not returned as a plugin. | ||||
384 | |||||
385 | =head1 METHODs | ||||
386 | |||||
387 | =head2 search_path | ||||
388 | |||||
389 | The method C<search_path> is exported into you namespace as well. | ||||
390 | You can call that at any time to change or replace the | ||||
391 | search_path. | ||||
392 | |||||
393 | $self->search_path( add => "New::Path" ); # add | ||||
394 | $self->search_path( new => "New::Path" ); # replace | ||||
395 | |||||
396 | =head1 BEHAVIOUR UNDER TEST ENVIRONMENT | ||||
397 | |||||
398 | In order to make testing reliable we exclude anything not from blib if blib.pm is | ||||
399 | in %INC. | ||||
400 | |||||
401 | However if the module being tested used another module that itself used C<Module::Pluggable> | ||||
402 | then the second module would fail. This was fixed by checking to see if the caller | ||||
403 | had (^|/)blib/ in their filename. | ||||
404 | |||||
405 | There's an argument that this is the wrong behaviour and that modules should explicitly | ||||
406 | trigger this behaviour but that particular code has been around for 7 years now and I'm | ||||
407 | reluctant to change the default behaviour. | ||||
408 | |||||
409 | You can now (as of version 4.1) force Module::Pluggable to look outside blib in a test environment by doing either | ||||
410 | |||||
411 | require Module::Pluggable; | ||||
412 | $Module::Pluggable::FORCE_SEARCH_ALL_PATHS = 1; | ||||
413 | import Module::Pluggable; | ||||
414 | |||||
415 | or | ||||
416 | |||||
417 | use Module::Pluggable force_search_all_paths => 1; | ||||
418 | |||||
419 | =head1 @INC hooks and App::FatPacker | ||||
420 | |||||
421 | If a module's @INC has a hook and that hook is an object which has a C<files()> method then we will | ||||
422 | try and require those files too. See C<t/26inc_hook.t> for an example. | ||||
423 | |||||
424 | This has allowed L<App::FatPacker> (as of version 0.10.0) to provide support for Module::Pluggable. | ||||
425 | |||||
426 | This should also, theoretically, allow someone to modify PAR to do the same thing. | ||||
427 | |||||
428 | =head1 FUTURE PLANS | ||||
429 | |||||
430 | This does everything I need and I can't really think of any other | ||||
431 | features I want to add. Famous last words of course (not least | ||||
432 | because we're up to version 5.0 at the time of writing). | ||||
433 | |||||
434 | However suggestions (and patches) are always welcome. | ||||
435 | |||||
436 | =head1 DEVELOPMENT | ||||
437 | |||||
438 | The master repo for this module is at | ||||
439 | |||||
440 | https://github.com/simonwistow/Module-Pluggable | ||||
441 | |||||
442 | =head1 AUTHOR | ||||
443 | |||||
444 | Simon Wistow <simon@thegestalt.org> | ||||
445 | |||||
446 | =head1 COPYING | ||||
447 | |||||
448 | Copyright, 2006 Simon Wistow | ||||
449 | |||||
450 | Distributed under the same terms as Perl itself. | ||||
451 | |||||
452 | =head1 BUGS | ||||
453 | |||||
454 | None known. | ||||
455 | |||||
456 | =head1 SEE ALSO | ||||
457 | |||||
458 | L<File::Spec>, L<File::Find>, L<File::Basename>, L<Class::Factory::Util>, L<Module::Pluggable::Ordered> | ||||
459 | |||||
460 | =cut | ||||
461 | |||||
462 |