diff options
author | Steve Hay <steve.m.hay@googlemail.com> | 2012-08-15 14:04:18 +0100 |
---|---|---|
committer | Steve Hay <steve.m.hay@googlemail.com> | 2012-08-15 14:04:18 +0100 |
commit | df67a34a189349ae842b21368751de0ba50f5cf3 (patch) | |
tree | b8ed12b016c7468f9cfce75d37a4aa9e412d3bb9 /cpan | |
parent | c95f3a5940d5ad03a2f91205377a2695ef63ab58 (diff) | |
download | perl-df67a34a189349ae842b21368751de0ba50f5cf3.tar.gz |
Upgrade to Module-Pluggable 4.2
The core build process cannot use Build.PL since Module::Build and/or its
prerequisites may not have been built yet, so EXCLUDE that and retain our
(already CUSTOMIZED) Makefile.PL instead for now.
Diffstat (limited to 'cpan')
-rw-r--r-- | cpan/Module-Pluggable/lib/Devel/InnerPackage.pm | 1 | ||||
-rw-r--r-- | cpan/Module-Pluggable/lib/Module/Pluggable.pm | 108 | ||||
-rw-r--r-- | cpan/Module-Pluggable/lib/Module/Pluggable/Object.pm | 161 | ||||
-rw-r--r-- | cpan/Module-Pluggable/t/22trigger.t | 54 | ||||
-rw-r--r-- | cpan/Module-Pluggable/t/23depth.t | 38 | ||||
-rw-r--r-- | cpan/Module-Pluggable/t/24local_inc_object.t | 19 | ||||
-rw-r--r-- | cpan/Module-Pluggable/t/24local_inc_package.t | 17 | ||||
-rw-r--r-- | cpan/Module-Pluggable/t/lib/Text/Abbrev.pm | 10 | ||||
-rw-r--r-- | cpan/Module-Pluggable/t/lib/TriggerTest/Plugin/After.pm | 3 | ||||
-rw-r--r-- | cpan/Module-Pluggable/t/lib/TriggerTest/Plugin/CallbackAllow.pm | 6 | ||||
-rw-r--r-- | cpan/Module-Pluggable/t/lib/TriggerTest/Plugin/CallbackDeny.pm | 6 | ||||
-rw-r--r-- | cpan/Module-Pluggable/t/lib/TriggerTest/Plugin/Deny.pm | 3 | ||||
-rw-r--r-- | cpan/Module-Pluggable/t/lib/TriggerTest/Plugin/Error.pm | 2 |
13 files changed, 357 insertions, 71 deletions
diff --git a/cpan/Module-Pluggable/lib/Devel/InnerPackage.pm b/cpan/Module-Pluggable/lib/Devel/InnerPackage.pm index 69f8dcaa44..ee2a5f410d 100644 --- a/cpan/Module-Pluggable/lib/Devel/InnerPackage.pm +++ b/cpan/Module-Pluggable/lib/Devel/InnerPackage.pm @@ -11,7 +11,6 @@ $VERSION = '0.4'; =head1 NAME - Devel::InnerPackage - find all the inner packages of a package =head1 SYNOPSIS diff --git a/cpan/Module-Pluggable/lib/Module/Pluggable.pm b/cpan/Module-Pluggable/lib/Module/Pluggable.pm index 55cf7269e7..d11156d022 100644 --- a/cpan/Module-Pluggable/lib/Module/Pluggable.pm +++ b/cpan/Module-Pluggable/lib/Module/Pluggable.pm @@ -1,7 +1,7 @@ package Module::Pluggable; use strict; -use vars qw($VERSION); +use vars qw($VERSION $FORCE_SEARCH_ALL_PATHS); use Module::Pluggable::Object; # ObQuote: @@ -9,7 +9,8 @@ use Module::Pluggable::Object; # Peter Gibbons: I wouldn't say I've been missing it, Bob! -$VERSION = '4.0'; +$VERSION = '4.2'; +$FORCE_SEARCH_ALL_PATHS = 0; sub import { my $class = shift; @@ -22,6 +23,7 @@ sub import { my ($package) = $opts{'package'} || $pkg; $opts{filename} = $file; $opts{package} = $package; + $opts{force_search_all_paths} = $FORCE_SEARCH_ALL_PATHS unless exists $opts{force_search_all_paths}; my $finder = Module::Pluggable::Object->new(%opts); @@ -152,9 +154,8 @@ Optionally it instantiates those classes for you. =head1 ADVANCED USAGE - Alternatively, if you don't want to use 'plugins' as the method ... - + package MyClass; use Module::Pluggable sub_name => 'foo'; @@ -227,6 +228,21 @@ and then later ... my @filters = $self->filters; my @plugins = $self->plugins; + +=head1 PLUGIN SEARCHING + +Every time you call 'plugins' the whole search path is walked again. This allows +for dynamically loading plugins even at run time. However this can get expensive +and so if you don't expect to want to add new plugins at run time you could do + + + package Foo; + use strict; + use Module::Pluggable sub_name => '_plugins'; + + our @PLUGINS; + sub plugins { @PLUGINS ||= shift->_plugins } + 1; =head1 INNER PACKAGES @@ -307,6 +323,62 @@ the extensions F<.swp> or F<.swo>, or files beginning with F<.#>. Setting C<include_editor_junk> changes C<Module::Pluggable> so it does not ignore any files it finds. +=head2 follow_symlinks + +Whether, when searching directories, to follow symlinks. + +Defaults to 1 i.e do follow symlinks. + +=head2 min_depth, max_depth + +This will allow you to set what 'depth' of plugin will be allowed. + +So, for example, C<MyClass::Plugin::Foo> will have a depth of 3 and +C<MyClass::Plugin::Foo::Bar> will have a depth of 4 so to only get the former +(i.e C<MyClass::Plugin::Foo>) do + + package MyClass; + use Module::Pluggable max_depth => 3; + +and to only get the latter (i.e C<MyClass::Plugin::Foo::Bar>) + + package MyClass; + use Module::Pluggable min_depth => 4; + + +=head1 TRIGGERS + +Various triggers can also be passed in to the options. + +If any of these triggers return 0 then the plugin will not be returned. + +=head2 before_require <plugin> + +Gets passed the plugin name. + +If 0 is returned then this plugin will not be required either. + +=head2 on_require_error <plugin> <err> + +Gets called when there's an error on requiring the plugin. + +Gets passed the plugin name and the error. + +The default on_require_error handler is to C<carp> the error and return 0. + +=head2 on_instantiate_error <plugin> <err> + +Gets called when there's an error on instantiating the plugin. + +Gets passed the plugin name and the error. + +The default on_instantiate_error handler is to C<carp> the error and return 0. + +=head2 after_require <plugin> + +Gets passed the plugin name. + +If 0 is returned then this plugin will be required but not returned as a plugin. =head1 METHODs @@ -319,7 +391,29 @@ search_path. $self->search_path( add => "New::Path" ); # add $self->search_path( new => "New::Path" ); # replace +=head1 BEHAVIOUR UNDER TEST ENVIRONMENT +In order to make testing reliable we exclude anything not from blib if blib.pm is +in %INC. + +However if the module being tested used another module that itself used C<Module::Pluggable> +then the second module would fail. This was fixed by checking to see if the caller +had (^|/)blib/ in their filename. + +There's an argument that this is the wrong behaviour and that modules should explicitly +trigger this behaviour but that particular code has been around for 7 years now and I'm +reluctant to change the default behaviour. + +You can now (as of version 4.1) force Module::Pluggable to look outside blib in a test environment by doing either + + require Module::Pluggable; + $Module::Pluggable::FORCE_SEARCH_ALL_PATHS = 1; + import Module::Pluggable; + +or + + use Module::Pluggable force_search_all_paths => 1; + =head1 FUTURE PLANS @@ -332,6 +426,12 @@ Recently tried fixed to find inner packages and to make it However suggestions (and patches) are welcome. +=head1 DEVELOPMENT + +The master repo for this module is at + +https://github.com/simonwistow/Module-Pluggable + =head1 AUTHOR Simon Wistow <simon@thegestalt.org> diff --git a/cpan/Module-Pluggable/lib/Module/Pluggable/Object.pm b/cpan/Module-Pluggable/lib/Module/Pluggable/Object.pm index e0ee993075..3077b166fd 100644 --- a/cpan/Module-Pluggable/lib/Module/Pluggable/Object.pm +++ b/cpan/Module-Pluggable/lib/Module/Pluggable/Object.pm @@ -4,11 +4,11 @@ use strict; use File::Find (); use File::Basename; use File::Spec::Functions qw(splitdir catdir curdir catfile abs2rel); -use Carp qw(croak carp); +use Carp qw(croak carp confess); use Devel::InnerPackage; use vars qw($VERSION); -$VERSION = '3.9'; +$VERSION = '4.2'; sub new { @@ -25,64 +25,73 @@ sub new { sub plugins { - my $self = shift; - - # override 'require' - $self->{'require'} = 1 if $self->{'inner'}; - - my $filename = $self->{'filename'}; - my $pkg = $self->{'package'}; - - # Get the exception params instantiated - $self->_setup_exceptions; - - # automatically turn a scalar search path or namespace into a arrayref - for (qw(search_path search_dirs)) { - $self->{$_} = [ $self->{$_} ] if exists $self->{$_} && !ref($self->{$_}); - } - - # default search path is '<Module>::<Name>::Plugin' - $self->{'search_path'} = ["${pkg}::Plugin"] unless $self->{'search_path'}; + my $self = shift; + my @args = @_; + # override 'require' + $self->{'require'} = 1 if $self->{'inner'}; - #my %opts = %$self; + my $filename = $self->{'filename'}; + my $pkg = $self->{'package'}; + # Get the exception params instantiated + $self->_setup_exceptions; - # check to see if we're running under test - my @SEARCHDIR = exists $INC{"blib.pm"} && defined $filename && $filename =~ m!(^|/)blib/! ? grep {/blib/} @INC : @INC; + # automatically turn a scalar search path or namespace into a arrayref + for (qw(search_path search_dirs)) { + $self->{$_} = [ $self->{$_} ] if exists $self->{$_} && !ref($self->{$_}); + } - # add any search_dir params - unshift @SEARCHDIR, @{$self->{'search_dirs'}} if defined $self->{'search_dirs'}; + # default search path is '<Module>::<Name>::Plugin' + $self->{'search_path'} ||= ["${pkg}::Plugin"]; + # default error handler + $self->{'on_require_error'} ||= sub { my ($plugin, $err) = @_; carp "Couldn't require $plugin : $err"; return 0 }; + $self->{'on_instantiate_error'} ||= sub { my ($plugin, $err) = @_; carp "Couldn't instantiate $plugin: $err"; return 0 }; - my @plugins = $self->search_directories(@SEARCHDIR); - push(@plugins, $self->handle_innerpackages($_)) for @{$self->{'search_path'}}; + # default whether to follow symlinks + $self->{'follow_symlinks'} = 1 unless exists $self->{'follow_symlinks'}; - # push @plugins, map { print STDERR "$_\n"; $_->require } list_packages($_) for (@{$self->{'search_path'}}); - - # return blank unless we've found anything - return () unless @plugins; + # check to see if we're running under test + my @SEARCHDIR = exists $INC{"blib.pm"} && defined $filename && $filename =~ m!(^|/)blib/! && !$self->{'force_search_all_paths'} ? grep {/blib/} @INC : @INC; + # add any search_dir params + unshift @SEARCHDIR, @{$self->{'search_dirs'}} if defined $self->{'search_dirs'}; + # set our @INC up to include and prefer our search_dirs if necessary + my @tmp = @INC; + unshift @tmp, @{$self->{'search_dirs'} || []}; + local @INC = @tmp if defined $self->{'search_dirs'}; - # remove duplicates - # probably not necessary but hey ho - my %plugins; - for(@plugins) { - next unless $self->_is_legit($_); - $plugins{$_} = 1; - } + my @plugins = $self->search_directories(@SEARCHDIR); + push(@plugins, $self->handle_innerpackages($_)) for @{$self->{'search_path'}}; + + # return blank unless we've found anything + return () unless @plugins; + + # remove duplicates + # probably not necessary but hey ho + my %plugins; + for(@plugins) { + next unless $self->_is_legit($_); + $plugins{$_} = 1; + } - # are we instantiating or requring? - if (defined $self->{'instantiate'}) { - my $method = $self->{'instantiate'}; - return map { ($_->can($method)) ? $_->$method(@_) : () } keys %plugins; - } else { - # no? just return the names - return keys %plugins; + # are we instantiating or requring? + if (defined $self->{'instantiate'}) { + my $method = $self->{'instantiate'}; + my @objs = (); + foreach my $package (keys %plugins) { + next unless $package->can($method); + my $obj = eval { $package->new(@_) }; + $self->{'on_instantiate_error'}->($package, $@) if $@; + push @objs, $obj if $obj; } - - + return @objs; + } else { + # no? just return the names + return keys %plugins; + } } sub _setup_exceptions { @@ -127,12 +136,16 @@ sub _is_legit { my %except = %{$self->{_exceptions}->{except_hash}||{}}; my $only = $self->{_exceptions}->{only}; my $except = $self->{_exceptions}->{except}; + my $depth = () = split '::', $plugin, -1; return 0 if (keys %only && !$only{$plugin} ); return 0 unless (!defined $only || $plugin =~ m!$only! ); return 0 if (keys %except && $except{$plugin} ); return 0 if (defined $except && $plugin =~ m!$except! ); + + return 0 if defined $self->{max_depth} && $depth>$self->{max_depth}; + return 0 if defined $self->{min_depth} && $depth<$self->{min_depth}; return 1; } @@ -193,7 +206,7 @@ sub search_paths { next if ($in_pod || $line =~ /^=cut/); # skip pod text next if $line =~ /^\s*#/; # and comments if ( $line =~ m/^\s*package\s+(.*::)?($name)\s*;/i ) { - @pkg_dirs = split /::/, $1; + @pkg_dirs = split /::/, $1 if defined $1;; $name = $2; last; } @@ -220,10 +233,7 @@ sub search_paths { next unless $plugin =~ m!(?:[a-z\d]+)[a-z\d]!i; - my $err = $self->handle_finding_plugin($plugin); - carp "Couldn't require $plugin : $err" if $err; - - push @plugins, $plugin; + $self->handle_finding_plugin($plugin, \@plugins) } # now add stuff that may have been in package @@ -252,12 +262,33 @@ sub _is_editor_junk { } sub handle_finding_plugin { - my $self = shift; - my $plugin = shift; - - return unless (defined $self->{'instantiate'} || $self->{'require'}); + my $self = shift; + my $plugin = shift; + my $plugins = shift; + my $no_req = shift || 0; + return unless $self->_is_legit($plugin); - $self->_require($plugin); + unless (defined $self->{'instantiate'} || $self->{'require'}) { + push @$plugins, $plugin; + return; + } + + $self->{before_require}->($plugin) || return if defined $self->{before_require}; + unless ($no_req) { + my $tmp = $@; + my $res = eval { $self->_require($plugin) }; + my $err = $@; + $@ = $tmp; + if ($err) { + if (defined $self->{on_require_error}) { + $self->{on_require_error}->($plugin, $err) || return; + } else { + return; + } + } + } + $self->{after_require}->($plugin) || return if defined $self->{after_require}; + push @$plugins, $plugin; } sub find_files { @@ -273,7 +304,8 @@ sub find_files { { # for the benefit of perl 5.6.1's Find, localize topic local $_; File::Find::find( { no_chdir => 1, - wanted => sub { + follow => $self->{'follow_symlinks'}, + wanted => sub { # Inlined from File::Find::Rule C< name => '*.pm' > return unless $File::Find::name =~ /$file_regex/; (my $path = $File::Find::name) =~ s#^\\./##; @@ -294,10 +326,7 @@ sub handle_innerpackages { my @plugins; foreach my $plugin (Devel::InnerPackage::list_packages($path)) { - my $err = $self->handle_finding_plugin($plugin); - #next if $err; - #next unless $INC{$plugin}; - push @plugins, $plugin; + $self->handle_finding_plugin($plugin, \@plugins, 1); } return @plugins; @@ -305,11 +334,11 @@ sub handle_innerpackages { sub _require { - my $self = shift; - my $pack = shift; - local $@; + my $self = shift; + my $pack = shift; eval "CORE::require $pack"; - return $@; + die ($@) if $@; + return 1; } diff --git a/cpan/Module-Pluggable/t/22trigger.t b/cpan/Module-Pluggable/t/22trigger.t new file mode 100644 index 0000000000..819e9ef20f --- /dev/null +++ b/cpan/Module-Pluggable/t/22trigger.t @@ -0,0 +1,54 @@ +#!perl -w + +use strict; +use FindBin; +use lib (($FindBin::Bin."/lib")=~/^(.*)$/); +use Test::More tests => 7; + +my $foo; +my @plugins; +my @errors; +ok($foo = TriggerTest->new(), "Created new TriggerTest"); +ok(@plugins = $foo->plugins, "Ran plugins"); +ok(@errors = $foo->errors, "Got errors"); +is_deeply([sort @plugins], ['TriggerTest::Plugin::After', 'TriggerTest::Plugin::CallbackAllow'], "Got the correct plugins"); +is_deeply([@errors], ['TriggerTest::Plugin::Error'], "Got the correct errors"); +ok(_is_loaded('TriggerTest::Plugin::CallbackDeny'), "CallbackDeny has been required"); +ok(!_is_loaded('TriggerTest::Plugin::Deny'), "Deny has not been required"); + + +# Stolen from Module::Loaded by Chris Williams (bingOs) +sub _is_loaded { + my $pm = shift; + my $file = __PACKAGE__->_pm_to_file( $pm ) or return; + return $INC{$file} if exists $INC{$file}; + return; +} + +sub _pm_to_file { + my $pkg = shift; + my $pm = shift or return; + my $file = join '/', split '::', $pm; + $file .= '.pm'; + return $file; +} + +package TriggerTest; + +our @ERRORS; +use strict; +use Module::Pluggable require => 1, + on_require_error => sub { my $p = shift; push @ERRORS, $p; return 0 }, + before_require => sub { my $p = shift; return !($p eq "TriggerTest::Plugin::Deny") }, + after_require => sub { my $p = shift; return !($p->can('exclude') && $p->exclude) }; + +sub new { + my $class = shift; + return bless {}, $class; +} + +sub errors { + @ERRORS; +} +1; + diff --git a/cpan/Module-Pluggable/t/23depth.t b/cpan/Module-Pluggable/t/23depth.t new file mode 100644 index 0000000000..51ccfca463 --- /dev/null +++ b/cpan/Module-Pluggable/t/23depth.t @@ -0,0 +1,38 @@ +#!perl -w + +use strict; +use FindBin; +use lib (($FindBin::Bin."/lib")=~/^(.*)$/); +use Test::More tests => 2; + + +my $min = MinTest->new(); +my $max = MaxTest->new(); +is_deeply([qw(MyOtherTest::Plugin::Bar MyOtherTest::Plugin::Foo MyOtherTest::Plugin::Quux)], [$max->plugins], "min depth"); +is_deeply([qw(MyOtherTest::Plugin::Quux::Foo)], [$min->plugins], "max depth"); + + +package MinTest; +use File::Spec::Functions qw(catdir); +use strict; +use File::Spec::Functions qw(catdir); +use Module::Pluggable search_path => "MyOtherTest::Plugin", min_depth => 4; + + +sub new { + my $class = shift; + return bless {}, $class; +} + +package MaxTest; +use File::Spec::Functions qw(catdir); +use strict; +use File::Spec::Functions qw(catdir); +use Module::Pluggable search_path => "MyOtherTest::Plugin", max_depth => 3; + + +sub new { + my $class = shift; + return bless {}, $class; +} +1;
\ No newline at end of file diff --git a/cpan/Module-Pluggable/t/24local_inc_object.t b/cpan/Module-Pluggable/t/24local_inc_object.t new file mode 100644 index 0000000000..bedacbdac9 --- /dev/null +++ b/cpan/Module-Pluggable/t/24local_inc_object.t @@ -0,0 +1,19 @@ +#!perl -w + +use strict; +use FindBin; +use Test::More tests => 2; + +my $inc = IncTest->new(); +my ($ta) = grep { ref($_) eq 'Text::Abbrev'} eval { local ($^W) = 0; $inc->plugins }; +ok($ta); +is($ta->MPCHECK, "HELLO"); + +package IncTest; +use Module::Pluggable search_path => "Text", search_dirs => "t/lib", instantiate => 'new', on_instantiate_error => sub {}; + +sub new { + my $class = shift; + return bless {}, $class; +} +1; diff --git a/cpan/Module-Pluggable/t/24local_inc_package.t b/cpan/Module-Pluggable/t/24local_inc_package.t new file mode 100644 index 0000000000..ef0f330f1d --- /dev/null +++ b/cpan/Module-Pluggable/t/24local_inc_package.t @@ -0,0 +1,17 @@ +#!perl -w + +use strict; +use FindBin; +use Test::More tests => 1; + +IncTest->new()->plugins; +is(Text::Abbrev->MPCHECK, "HELLO"); + +package IncTest; +use Module::Pluggable search_path => "Text", search_dirs => "t/lib", require => 1; + +sub new { + my $class = shift; + return bless {}, $class; +} +1;
\ No newline at end of file diff --git a/cpan/Module-Pluggable/t/lib/Text/Abbrev.pm b/cpan/Module-Pluggable/t/lib/Text/Abbrev.pm new file mode 100644 index 0000000000..fb176ba0ef --- /dev/null +++ b/cpan/Module-Pluggable/t/lib/Text/Abbrev.pm @@ -0,0 +1,10 @@ +package Text::Abbrev; +use strict; + +sub new { + return bless {}, shift; +} + +sub MPCHECK { "HELLO" } + +1;
\ No newline at end of file diff --git a/cpan/Module-Pluggable/t/lib/TriggerTest/Plugin/After.pm b/cpan/Module-Pluggable/t/lib/TriggerTest/Plugin/After.pm new file mode 100644 index 0000000000..b5f6901557 --- /dev/null +++ b/cpan/Module-Pluggable/t/lib/TriggerTest/Plugin/After.pm @@ -0,0 +1,3 @@ +package TriggerTest::Plugin::After; + +1;
\ No newline at end of file diff --git a/cpan/Module-Pluggable/t/lib/TriggerTest/Plugin/CallbackAllow.pm b/cpan/Module-Pluggable/t/lib/TriggerTest/Plugin/CallbackAllow.pm new file mode 100644 index 0000000000..589b154ca4 --- /dev/null +++ b/cpan/Module-Pluggable/t/lib/TriggerTest/Plugin/CallbackAllow.pm @@ -0,0 +1,6 @@ +package TriggerTest::Plugin::CallbackAllow; + +sub exclude { + return 0; +} +1;
\ No newline at end of file diff --git a/cpan/Module-Pluggable/t/lib/TriggerTest/Plugin/CallbackDeny.pm b/cpan/Module-Pluggable/t/lib/TriggerTest/Plugin/CallbackDeny.pm new file mode 100644 index 0000000000..e63227f59c --- /dev/null +++ b/cpan/Module-Pluggable/t/lib/TriggerTest/Plugin/CallbackDeny.pm @@ -0,0 +1,6 @@ +package TriggerTest::Plugin::CallbackDeny; + +sub exclude { + return 1; +} +1;
\ No newline at end of file diff --git a/cpan/Module-Pluggable/t/lib/TriggerTest/Plugin/Deny.pm b/cpan/Module-Pluggable/t/lib/TriggerTest/Plugin/Deny.pm new file mode 100644 index 0000000000..311e2a07a0 --- /dev/null +++ b/cpan/Module-Pluggable/t/lib/TriggerTest/Plugin/Deny.pm @@ -0,0 +1,3 @@ +package TriggerTest::Plugin::Deny; + +1;
\ No newline at end of file diff --git a/cpan/Module-Pluggable/t/lib/TriggerTest/Plugin/Error.pm b/cpan/Module-Pluggable/t/lib/TriggerTest/Plugin/Error.pm new file mode 100644 index 0000000000..620465a942 --- /dev/null +++ b/cpan/Module-Pluggable/t/lib/TriggerTest/Plugin/Error.pm @@ -0,0 +1,2 @@ +package TriggerTest::Plugin::Error; + |