Module-Pluggable-6.4/0000755000175100017510000000000015231231755014122 5ustar runnerrunnerModule-Pluggable-6.4/t/0000755000175100017510000000000015231231755014365 5ustar runnerrunnerModule-Pluggable-6.4/t/03diffname.t0000755000175100017510000000070715231231640016466 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 3; my $foo; ok($foo = MyTest->new()); my @plugins; my @expected = qw(MyTest::Plugin::Bar MyTest::Plugin::Foo MyTest::Plugin::Quux::Foo); ok(@plugins = sort $foo->foo); is_deeply(\@plugins, \@expected); package MyTest; use strict; use Module::Pluggable ( sub_name => 'foo'); sub new { my $class = shift; return bless {}, $class; } 1; Module-Pluggable-6.4/t/14package.t0000644000175100017510000000111415231231640016300 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 5; my $foo; ok($foo = MyTest->new()); my @plugins; my @expected = qw(MyTest::Plugin::Bar MyTest::Plugin::Foo MyTest::Plugin::Quux::Foo); ok(@plugins = sort $foo->plugins); is_deeply(\@plugins, \@expected); @plugins = (); ok(@plugins = sort MyTest->plugins); is_deeply(\@plugins, \@expected); package MyTest; use strict; sub new { return bless {}, $_[0] } package MyOtherTest; use strict; use Module::Pluggable ( package => "MyTest" ); sub new { return bless {}, $_[0] } 1; Module-Pluggable-6.4/t/29unreadable.t0000755000175100017510000000350515231231640017026 0ustar runnerrunner#!perl -w use strict; use Test::More; BEGIN { if ($> == 0) { plan skip_all => "Running as root"; } else { plan tests => 6; } } use FindBin; #use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use File::Temp qw/tempdir/; use File::Path qw(make_path); # The problem with checking for files that are unreadable # is that we can't check unreadable files into git # So we're going to create it on the fly # First create a tmp directory and then a directory underneath my $dir = tempdir(CLEANUP => 1); my $path = "${dir}/lib/Unreadable"; my $file = "${path}/Foo.pm"; make_path($path); # ... now create a file open(my $fh, ">", $file) || die "Couldn't create temporary file $file: $!"; print $fh "package Unreadable::Foo;\n1;\n"; close($fh); # ... and set the file permissions on that to unreadable chmod(0200, $file); # .. and include the new path push @INC, "${dir}/lib"; # This should die when it can't read the file my @a = eval { MyTest->plugins }; ok(defined $@, "Got an error"); is_deeply([@a], []); # This should not die but also shouldn't be able to require the plugin my @b = eval { MyTest2->plugins }; ok(!$@ , "Didn't get an error $@"); is_deeply([@b], []); # Now set it readable chmod(0600, $file); # This should not die and should be able to require the plugin my @c = eval { MyTest->plugins }; ok(!$@ , "Didn't get an error $@"); is_deeply([@c], ["Unreadable::Foo"]); package MyTest; use File::Spec::Functions qw(catdir); use strict; use Module::Pluggable search_path => "Unreadable", require => 1, on_require_error => sub { die $_[1] }; package MyTest2; use File::Spec::Functions qw(catdir); use strict; use Module::Pluggable search_path => "Unreadable", require => 1, on_require_error => sub { 0 }; 1; Module-Pluggable-6.4/t/13exceptarray.t0000644000175100017510000000214615231231640017241 0ustar runnerrunner#!perl -wT use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 10; { my $foo; ok($foo = MyTest->new()); my @plugins; my @expected = qw(MyTest::Plugin::Bar MyTest::Plugin::Quux::Foo); ok(@plugins = sort $foo->plugins); is_deeply(\@plugins, \@expected); @plugins = (); ok(@plugins = sort MyTest->plugins); is_deeply(\@plugins, \@expected); } { my $foo; ok($foo = MyTestSub->new()); my @plugins; my @expected = qw(MyTest::Plugin::Bar MyTest::Plugin::Quux::Foo); ok(@plugins = sort $foo->plugins); is_deeply(\@plugins, \@expected); @plugins = (); ok(@plugins = sort MyTestSub->plugins); is_deeply(\@plugins, \@expected); } package MyTest; use strict; use Module::Pluggable except => [ "MyTest::Plugin::Foo" ]; sub new { my $class = shift; return bless {}, $class; } package MyTestSub; use strict; use Module::Pluggable search_path => "MyTest::Plugin"; sub new { my $class = shift; my $self = bless {}, $class; $self->except(["MyTest::Plugin::Foo"]); return $self; } 1; Module-Pluggable-6.4/t/21editor_junk.t0000644000175100017510000000204215231231640017221 0ustar runnerrunner#!perl -w use Test::More; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Module::Pluggable::Object; use File::Spec::Functions qw(catfile); my ($dodgy_file) = (catfile($FindBin::Bin,"lib", "EditorJunk", "Plugin", "#Bar.pm#")=~/^(.*)$/); unless (-f $dodgy_file) { plan skip_all => "Can't handle plugin names with octothorpes\n"; } else { plan tests => 4; } my $foo; ok($foo = EditorJunk->new()); my @plugins; my @expected = qw(EditorJunk::Plugin::Bar EditorJunk::Plugin::Foo); ok(@plugins = sort $foo->plugins); is_deeply(\@plugins, \@expected, "is deeply"); my $mpo = Module::Pluggable::Object->new( package => 'EditorJunk', filename => __FILE__, include_editor_junk => 1, ); @expected = ('EditorJunk::Plugin::.#Bar', 'EditorJunk::Plugin::Bar', 'EditorJunk::Plugin::Foo'); @plugins = sort $mpo->plugins(); is_deeply(\@plugins, \@expected, "is deeply"); package EditorJunk; use strict; use Module::Pluggable; sub new { my $class = shift; return bless {}, $class; } 1; Module-Pluggable-6.4/t/26inc_hook.t0000755000175100017510000000121715231231640016510 0ustar runnerrunner#!perl -w use strict; use Test::More tests => 2; my @plugins = Foo->plugins; is($plugins[0], 'IncHook::Test', "found module"); is($plugins[0]->found_it, 'IncHook::Test', "ran module"); package IncHook; use strict; sub new { return bless {}, shift } sub IncHook::INC { my ($self, $filename) = @_; return unless $filename eq 'IncHook/Test.pm'; my @sub = ('package IncHook::Test; sub found_it { return __PACKAGE__ }; 1;'); return sub { defined($_ = shift @sub) }; } sub files { qw(IncHook/Test.pm) } package Foo; use strict; use Module::Pluggable require => 1, search_path => 'IncHook'; BEGIN { unshift @INC, IncHook->new }; 1;Module-Pluggable-6.4/t/12onlyrequire.t0000644000175100017510000000066415231231640017272 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 2; my @packages = eval { Zot->_dist_types }; is($@, '', "No warnings"); is(scalar(@packages), 0, "Correctly only got 1 package"); package Zot; use strict; use Module::Pluggable ( sub_name => '_dist_types', search_path => __PACKAGE__, only => qr/Zot::\w+$/, require => 1, ); 1; Module-Pluggable-6.4/t/16different_extension.t0000755000175100017510000000112715231231640020760 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 5; my $foo; ok($foo = ExtTest->new()); my @plugins; my @expected = qw(ExtTest::Plugin::Bar ExtTest::Plugin::Foo ExtTest::Plugin::Quux::Foo); ok(@plugins = sort $foo->plugins); is_deeply(\@plugins, \@expected, "is deeply"); @plugins = (); ok(@plugins = sort ExtTest->plugins); is_deeply(\@plugins, \@expected, "is deeply class"); package ExtTest; use strict; use Module::Pluggable file_regex => qr/\.plugin$/; sub new { my $class = shift; return bless {}, $class; } 1; Module-Pluggable-6.4/t/10innerpack_super.t0000644000175100017510000000071415231231640020076 0ustar runnerrunner#!perl -wT use Test::More tests => 3; use strict; use_ok('Devel::InnerPackage'); Bar->whee; is_deeply([Devel::InnerPackage::list_packages("Bar")],[], "Don't pick up ::SUPER pseudo stash"); is_deeply([Devel::InnerPackage::list_packages("Foo")],['Foo::Bar'], "Still pick up other inner package"); package Foo; sub whee { 1; } package Foo::Bar; sub whee {} package Bar; use parent -norequire => 'Foo'; sub whee { shift->SUPER::whee; 2; } 1; Module-Pluggable-6.4/t/09require.t0000755000175100017510000000061515231231640016375 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 2; my $t = MyTest->new(); ok($t->plugins()); ok(keys %{MyTest::Plugin::Foo::}); package MyTest; use File::Spec::Functions qw(catdir); use strict; use Module::Pluggable (require => 1); use base qw(Module::Pluggable); sub new { my $class = shift; return bless {}, $class; } 1; Module-Pluggable-6.4/t/13exceptregex.t0000644000175100017510000000214415231231640017233 0ustar runnerrunner#!perl -wT use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 10; { my $foo; ok($foo = MyTest->new()); my @plugins; my @expected = qw(MyTest::Plugin::Bar MyTest::Plugin::Quux::Foo); ok(@plugins = sort $foo->plugins); is_deeply(\@plugins, \@expected); @plugins = (); ok(@plugins = sort MyTest->plugins); is_deeply(\@plugins, \@expected); } { my $foo; ok($foo = MyTestSub->new()); my @plugins; my @expected = qw(MyTest::Plugin::Bar MyTest::Plugin::Quux::Foo); ok(@plugins = sort $foo->plugins); is_deeply(\@plugins, \@expected); @plugins = (); ok(@plugins = sort MyTestSub->plugins); is_deeply(\@plugins, \@expected); } package MyTest; use strict; use Module::Pluggable except => qr/MyTest::Plugin::Foo/; sub new { my $class = shift; return bless {}, $class; } package MyTestSub; use strict; use Module::Pluggable search_path => "MyTest::Plugin"; sub new { my $class = shift; my $self = bless {}, $class; $self->except(qr/MyTest::Plugin::Foo/); return $self; } 1; Module-Pluggable-6.4/t/20dodgy_files.t0000755000175100017510000000321015231231640017174 0ustar runnerrunner#!perl -w BEGIN { if ($^O eq 'VMS' || $^O eq 'VOS') { print "1..0 # Skip: can't handle misspelled plugin names\n"; exit; } } use strict; use FindBin; use Test::More; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use File::Spec::Functions qw(catfile); my ($dodgy_file) = (catfile($FindBin::Bin, "lib", "OddTest", "Plugin", "-Dodgy.pm")=~/^(.*)$/); unless (-f $dodgy_file) { plan skip_all => "Can't handle misspelled plugin names\n"; } else { plan tests => 5; } my $foo; ok($foo = OddTest->new()); my @plugins; my @expected = ('OddTest::Plugin::-Dodgy', 'OddTest::Plugin::Foo'); ok(@plugins = sort $foo->plugins); is_deeply(\@plugins, \@expected, "is deeply"); my @odd_plugins; my @odd_expected = qw(OddTest::Plugin::Foo); ok(@odd_plugins = sort $foo->odd_plugins); is_deeply(\@odd_plugins, \@odd_expected, "is deeply"); package OddTest::Pluggable; use Data::Dumper; use base qw(Module::Pluggable::Object); sub find_files { my $self = shift; my @files = $self->SUPER::find_files(@_); return grep { !/(^|\/)-/ } $self->SUPER::find_files(@_) ; } package OddTest; use strict; use Module::Pluggable; sub new { my $class = shift; return bless {}, $class; } sub odd_plugins { my $self = shift; my %opts; my ($pkg, $file) = caller; # the default name for the method is 'plugins' my $sub = $opts{'sub_name'} || 'plugins'; # get our package my ($package) = $opts{'package'} || "OddTest"; $opts{filename} = $file; $opts{package} = $package; my $op = OddTest::Pluggable->new( package => ref($self) ); return $op->plugins(@_); } 1; Module-Pluggable-6.4/t/05postpath.t0000755000175100017510000000074215231231640016560 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 3; my $foo; ok($foo = MyTest->new()); my @plugins; my @expected = qw(MyTest::Extend::Plugin::Bar); ok(@plugins = sort $foo->plugins); is_deeply(\@plugins, \@expected); package MyTest; use File::Spec::Functions qw(catdir); use strict; use Module::Pluggable (search_path => ["MyTest::Extend::Plugin"]); sub new { my $class = shift; return bless {}, $class; } 1; Module-Pluggable-6.4/t/04acmedir.t0000644000175100017510000000077015231231640016317 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 3; my $foo; ok($foo = MyTest->new()); my @plugins; my @expected = qw(Acme::MyTest::Plugin::Foo); ok(@plugins = sort $foo->plugins); is_deeply(\@plugins, \@expected); package MyTest; use File::Spec::Functions qw(catdir); use strict; use Module::Pluggable search_path => ["Acme::MyTest::Plugin"], search_dirs => [ "t/acme" ]; sub new { my $class = shift; return bless {}, $class; } 1; Module-Pluggable-6.4/t/12onlyarray.t0000644000175100017510000000205215231231640016725 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 10; { my $foo; ok($foo = MyTest->new()); my @plugins; my @expected = qw(MyTest::Plugin::Foo); ok(@plugins = sort $foo->plugins); is_deeply(\@plugins, \@expected); @plugins = (); ok(@plugins = sort MyTest->plugins); is_deeply(\@plugins, \@expected); } { my $foo; ok($foo = MyTestSub->new()); my @plugins; my @expected = qw(MyTest::Plugin::Foo); ok(@plugins = sort $foo->plugins); is_deeply(\@plugins, \@expected); @plugins = (); ok(@plugins = sort MyTestSub->plugins); is_deeply(\@plugins, \@expected); } package MyTest; use strict; use Module::Pluggable only => [ "MyTest::Plugin::Foo" ]; sub new { my $class = shift; return bless {}, $class; } package MyTestSub; use strict; use Module::Pluggable search_path => "MyTest::Plugin"; sub new { my $class = shift; my $self = bless {}, $class; $self->only(["MyTest::Plugin::Foo"]); return $self; } 1; Module-Pluggable-6.4/t/02works.t0000755000175100017510000000106515231231640016057 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 5; my $foo; ok($foo = MyTest->new()); my @plugins; my @expected = qw(MyTest::Plugin::Bar MyTest::Plugin::Foo MyTest::Plugin::Quux::Foo); ok(@plugins = sort $foo->plugins); is_deeply(\@plugins, \@expected, "is deeply"); @plugins = (); ok(@plugins = sort MyTest->plugins); is_deeply(\@plugins, \@expected, "is deeply class"); package MyTest; use strict; use Module::Pluggable; sub new { my $class = shift; return bless {}, $class; } 1; Module-Pluggable-6.4/t/18skipped_package.t0000644000175100017510000000042015231231640020022 0ustar runnerrunner#!perl -w use Test::More tests => 1; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Devel::InnerPackage qw(list_packages); use No::Middle; my @p = list_packages("No::Middle"); is_deeply([ sort @p ], [ qw(No::Middle::Package::A No::Middle::Package::B) ]); Module-Pluggable-6.4/t/24local_inc_object.t0000644000175100017510000000212115231231640020156 0ustar runnerrunner#!perl -w use strict; use FindBin; use Test::More tests => 5; my $inc = IncTest->new(); my ($ta) = grep { ref($_) eq 'LocalIncTest::Plugin::Abbrev'} eval { local ($^W) = 0; $inc->plugins }; ok($ta); is($ta->MPCHECK, "HELLO"); ok($inc->before() > 0,'before_instantiate fired'); my %after = $inc->after(); ok(keys %after > 0, 'after_instantiate fired'); my $norefs = scalar grep { ref($_) } values %after; my $total = scalar values %after; ok($total == $norefs, 'after_instantiate has all refs'); package IncTest; our @BEFORE; our %AFTER; use Module::Pluggable search_path => "LocalIncTest::Plugin", search_dirs => "t/lib", instantiate => 'module_pluggable', before_instantiate => sub { push @BEFORE, $_[0]; return 1 }, after_instantiate => sub { $AFTER{$_[0]} = $_[1]; return $_[1] }, on_require_error => sub { }, on_instantiate_error => sub { }; sub new { my $class = shift; return bless {}, $class; } sub before { @BEFORE } sub after { %AFTER } 1; Module-Pluggable-6.4/t/10innerpack_onefile.t0000644000175100017510000000107715231231640020364 0ustar runnerrunner#!perl -wT use strict; use Test::More tests => 2; use Data::Dumper; my $mc = MyClass->new(); my $mc2 = MyClass2->new(); is_deeply([$mc->plugins], [qw(MyClass::Plugin::MyPlugin)], "Got inner plugin"); is_deeply([$mc2->plugins], [], "Didn't get plugin"); package MyClass::Plugin::MyPlugin; sub pretty { print "I am pretty" }; package MyClass; use Module::Pluggable inner => 1; sub new { return bless {}, $_[0] } package MyClass2; use Module::Pluggable search_path => "MyClass::Plugin", inner => 0; sub new { return bless {}, $_[0] } 1; Module-Pluggable-6.4/t/12only.t0000644000175100017510000000204315231231640015666 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 10; { my $foo; ok($foo = MyTest->new()); my @plugins; my @expected = qw(MyTest::Plugin::Foo); ok(@plugins = sort $foo->plugins); is_deeply(\@plugins, \@expected); @plugins = (); ok(@plugins = sort MyTest->plugins); is_deeply(\@plugins, \@expected); } { my $foo; ok($foo = MyTestSub->new()); my @plugins; my @expected = qw(MyTest::Plugin::Foo); ok(@plugins = sort $foo->plugins); is_deeply(\@plugins, \@expected); @plugins = (); ok(@plugins = sort MyTestSub->plugins); is_deeply(\@plugins, \@expected); } package MyTest; use strict; use Module::Pluggable only => "MyTest::Plugin::Foo"; sub new { my $class = shift; return bless {}, $class; } package MyTestSub; use strict; use Module::Pluggable search_path => "MyTest::Plugin"; sub new { my $class = shift; my $self = bless {}, $class; $self->only("MyTest::Plugin::Foo"); return $self; } 1; Module-Pluggable-6.4/t/02alsoworks.t0000644000175100017510000000115615231231640016734 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 5; my $foo; ok($foo = MyOtherTest->new()); my @plugins; my @expected = qw(MyOtherTest::Plugin::Bar MyOtherTest::Plugin::Foo MyOtherTest::Plugin::Quux MyOtherTest::Plugin::Quux::Foo); ok(@plugins = sort $foo->plugins); is_deeply(\@plugins, \@expected, "is deeply"); @plugins = (); ok(@plugins = sort MyOtherTest->plugins); is_deeply(\@plugins, \@expected, "is deeply class"); package MyOtherTest; use strict; use Module::Pluggable; sub new { my $class = shift; return bless {}, $class; } 1; Module-Pluggable-6.4/t/04acmepath_single.t0000644000175100017510000000073115231231640020033 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 3; my $foo; ok($foo = MyTest->new()); my @plugins; my @expected = qw(Acme::MyTest::Plugin::Foo); ok(@plugins = sort $foo->plugins); is_deeply(\@plugins, \@expected); package MyTest; use File::Spec::Functions qw(catdir); use strict; use Module::Pluggable search_path => "Acme::MyTest::Plugin"; sub new { my $class = shift; return bless {}, $class; } 1; Module-Pluggable-6.4/t/lib/0000755000175100017510000000000015231231755015133 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/MyOtherTest/0000755000175100017510000000000015231231755017362 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/MyOtherTest/Plugin/0000755000175100017510000000000015231231755020620 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/MyOtherTest/Plugin/Bar.pm0000644000175100017510000000006315231231640021652 0ustar runnerrunnerpackage MyOtherTest::Plugin::Bar; use strict; 1; Module-Pluggable-6.4/t/lib/MyOtherTest/Plugin/Foo.pm0000644000175100017510000000006315231231640021671 0ustar runnerrunnerpackage MyOtherTest::Plugin::Foo; use strict; 1; Module-Pluggable-6.4/t/lib/MyOtherTest/Plugin/Quux/0000755000175100017510000000000015231231755021562 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/MyOtherTest/Plugin/Quux/Foo.pm0000644000175100017510000000007115231231640022632 0ustar runnerrunnerpackage MyOtherTest::Plugin::Quux::Foo; use strict; 1; Module-Pluggable-6.4/t/lib/MyOtherTest/Plugin/Quux.pm0000644000175100017510000000006415231231640022111 0ustar runnerrunnerpackage MyOtherTest::Plugin::Quux; use strict; 1; Module-Pluggable-6.4/t/lib/ExtTest/0000755000175100017510000000000015231231755016533 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/ExtTest/Plugin/0000755000175100017510000000000015231231755017771 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/ExtTest/Plugin/Foo.plugin0000755000175100017510000000006215231231640021726 0ustar runnerrunnerpackage MyTest::Plugin::Foo; use strict; 1; Module-Pluggable-6.4/t/lib/ExtTest/Plugin/Quux/0000755000175100017510000000000015231231755020733 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/ExtTest/Plugin/Quux/Foo.plugin0000755000175100017510000000007015231231640022667 0ustar runnerrunnerpackage MyTest::Plugin::Quux::Foo; use strict; 1; Module-Pluggable-6.4/t/lib/ExtTest/Plugin/Bar.plugin0000755000175100017510000000006215231231640021707 0ustar runnerrunnerpackage MyTest::Plugin::Bar; use strict; 1; Module-Pluggable-6.4/t/lib/No/0000755000175100017510000000000015231231755015507 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/No/Middle.pm0000644000175100017510000000017715231231640017241 0ustar runnerrunnerpackage No::Middle; sub foo {} package No::Middle::Package::A; sub foo {} package No::Middle::Package::B; sub foo {} 1; Module-Pluggable-6.4/t/lib/InnerTest/0000755000175100017510000000000015231231755017046 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/InnerTest/Plugin/0000755000175100017510000000000015231231755020304 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/InnerTest/Plugin/Foo.pm0000644000175100017510000000031515231231640021355 0ustar runnerrunnerpackage InnerTest::Plugin::Foo; use strict; our $FOO = 1; package InnerTest::Plugin::Bar; use strict; sub bar {} package InnerTest::Plugin::Quux; use strict; use base qw(InnerTest::Plugin::Bar); 1; Module-Pluggable-6.4/t/lib/Zot/0000755000175100017510000000000015231231755015707 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/Zot/.Zork.pm0000644000175100017510000000000015231231640017227 0ustar runnerrunnerModule-Pluggable-6.4/t/lib/LocalIncTest/0000755000175100017510000000000015231231755017457 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/LocalIncTest/Plugin/0000755000175100017510000000000015231231755020715 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/LocalIncTest/Plugin/Abbrev.pm0000644000175100017510000000020415231231640022441 0ustar runnerrunnerpackage LocalIncTest::Plugin::Abbrev; use strict; sub module_pluggable { return bless {}, shift; } sub MPCHECK { "HELLO" } 1;Module-Pluggable-6.4/t/lib/EditorJunk/0000755000175100017510000000000015231231755017211 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/EditorJunk/Plugin/0000755000175100017510000000000015231231755020447 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/EditorJunk/Plugin/Bar.pm0000644000175100017510000000005615231231640021503 0ustar runnerrunnerpackage EditorJunk::Bar; use strict; 1; Module-Pluggable-6.4/t/lib/EditorJunk/Plugin/Foo.pm0000644000175100017510000000005615231231640021522 0ustar runnerrunnerpackage EditorJunk::Foo; use strict; 1; Module-Pluggable-6.4/t/lib/EditorJunk/Plugin/Bar.pm.swp0000644000175100017510000000005615231231640022313 0ustar runnerrunnerpackage EditorJunk::Bar; use strict; 1; Module-Pluggable-6.4/t/lib/EditorJunk/Plugin/Bar.pm.swo0000644000175100017510000000005615231231640022312 0ustar runnerrunnerpackage EditorJunk::Bar; use strict; 1; Module-Pluggable-6.4/t/lib/EditorJunk/Plugin/Bar.pm~0000644000175100017510000000005615231231640021701 0ustar runnerrunnerpackage EditorJunk::Bar; use strict; 1; Module-Pluggable-6.4/t/lib/Acme/0000755000175100017510000000000015231231755016000 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/Acme/Foo-Bar.pm0000644000175100017510000000006115231231640017551 0ustar runnerrunnerpackage Acme::FooBar; our $quux = "hello"; 1; Module-Pluggable-6.4/t/lib/Acme/MyTest/0000755000175100017510000000000015231231755017225 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/Acme/MyTest/Plugin/0000755000175100017510000000000015231231755020463 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/Acme/MyTest/Plugin/Foo.pm0000755000175100017510000000007015231231640021535 0ustar runnerrunnerpackage Acme::MyTest::Plugin::Foo; use strict; 1; Module-Pluggable-6.4/t/lib/M/0000755000175100017510000000000015231231755015327 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/M/X.pm0000644000175100017510000000012315231231640016061 0ustar runnerrunnerpackage M::X; use strict; sub new { return bless {}, $_[0]; } sub frobnitz {} 1; Module-Pluggable-6.4/t/lib/TriggerTest/0000755000175100017510000000000015231231755017376 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/TriggerTest/Plugin/0000755000175100017510000000000015231231755020634 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/TriggerTest/Plugin/CallbackAllow.pm0000644000175100017510000000011515231231640023653 0ustar runnerrunnerpackage TriggerTest::Plugin::CallbackAllow; sub exclude { return 0; } 1;Module-Pluggable-6.4/t/lib/TriggerTest/Plugin/CallbackDeny.pm0000644000175100017510000000011415231231640023473 0ustar runnerrunnerpackage TriggerTest::Plugin::CallbackDeny; sub exclude { return 1; } 1;Module-Pluggable-6.4/t/lib/TriggerTest/Plugin/Deny.pm0000644000175100017510000000004615231231640022062 0ustar runnerrunnerpackage TriggerTest::Plugin::Deny; 1;Module-Pluggable-6.4/t/lib/TriggerTest/Plugin/Error.pm0000644000175100017510000000004515231231640022253 0ustar runnerrunnerpackage TriggerTest::Plugin::Error; Module-Pluggable-6.4/t/lib/TriggerTest/Plugin/After.pm0000644000175100017510000000004715231231640022225 0ustar runnerrunnerpackage TriggerTest::Plugin::After; 1;Module-Pluggable-6.4/t/lib/OddTest/0000755000175100017510000000000015231231755016501 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/OddTest/Plugin/0000755000175100017510000000000015231231755017737 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/OddTest/Plugin/Foo.pm0000644000175100017510000000005715231231640021013 0ustar runnerrunnerpackage OddFiles/Plugin/Foo.pm sub new {} 1; Module-Pluggable-6.4/t/lib/MyTest/0000755000175100017510000000000015231231755016360 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/MyTest/Plugin/0000755000175100017510000000000015231231755017616 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/MyTest/Plugin/Bar.pm0000755000175100017510000000006215231231640020652 0ustar runnerrunnerpackage MyTest::Plugin::Bar; use strict; 1; Module-Pluggable-6.4/t/lib/MyTest/Plugin/Foo.pm0000755000175100017510000000014515231231640020673 0ustar runnerrunnerpackage MyTest::Plugin::Foo; use strict; sub new { return bless {}, $_[0]; } sub frobnitz {} 1; Module-Pluggable-6.4/t/lib/MyTest/Plugin/Quux/0000755000175100017510000000000015231231755020560 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/MyTest/Plugin/Quux/Foo.pm0000755000175100017510000000007015231231640021632 0ustar runnerrunnerpackage MyTest::Plugin::Quux::Foo; use strict; 1; Module-Pluggable-6.4/t/lib/MyTest/Extend/0000755000175100017510000000000015231231755017607 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/MyTest/Extend/Plugin/0000755000175100017510000000000015231231755021045 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/MyTest/Extend/Plugin/Bar.pm0000755000175100017510000000027215231231640022104 0ustar runnerrunnerpackage MyTest::Extend::Plugin::Bar; use strict; sub new { my $class = shift; my %self = @_; return bless \%self, $class; } sub nork { return $_[0]->{'nork'}; } 1; Module-Pluggable-6.4/t/lib/TA/0000755000175100017510000000000015231231755015437 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/TA/C/0000755000175100017510000000000015231231755015621 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/TA/C/A/0000755000175100017510000000000015231231755016001 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/TA/C/A/I.pm0000644000175100017510000000016515231231640016522 0ustar runnerrunnerpackage TA::C::A::I; sub foo { } package TA::C::A::I::A; sub foo { } package TA::C::A::I::A::B; sub foo { } 1; Module-Pluggable-6.4/t/lib/Apple/0000755000175100017510000000000015231231755016174 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/Apple/Double/0000755000175100017510000000000015231231755017406 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/Apple/Double/Plugin/0000755000175100017510000000000015231231755020644 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/Apple/Double/Plugin/.AppleDouble/0000755000175100017510000000000015231231755023116 5ustar runnerrunnerModule-Pluggable-6.4/t/lib/Apple/Double/Plugin/.AppleDouble/File.pm0000644000175100017510000000002415231231640024320 0ustar runnerrunnerthis isn't a module Module-Pluggable-6.4/t/lib/Apple/Double/Plugin/File.pm0000644000175100017510000000004015231231640022044 0ustar runnerrunnerpackage Apple::Double::File; 1; Module-Pluggable-6.4/t/01use.t0000755000175100017510000000022515231231640015502 0ustar runnerrunner#!perl -w use strict; use Test::More tests => 3; use_ok('Module::Pluggable'); use_ok('Module::Pluggable::Object'); use_ok('Devel::InnerPackage'); Module-Pluggable-6.4/t/acme/0000755000175100017510000000000015231231755015272 5ustar runnerrunnerModule-Pluggable-6.4/t/acme/Acme/0000755000175100017510000000000015231231755016137 5ustar runnerrunnerModule-Pluggable-6.4/t/acme/Acme/MyTest/0000755000175100017510000000000015231231755017364 5ustar runnerrunnerModule-Pluggable-6.4/t/acme/Acme/MyTest/Plugin/0000755000175100017510000000000015231231755020622 5ustar runnerrunnerModule-Pluggable-6.4/t/acme/Acme/MyTest/Plugin/Foo.pm0000644000175100017510000000007015231231640021671 0ustar runnerrunnerpackage Acme::MyTest::Plugin::Foo; use strict; 1; Module-Pluggable-6.4/t/11usetwice.t0000644000175100017510000000135515231231640016541 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 3; my $foo; ok($foo = MyTest->new()); my @plugins; my @expected = qw(MyTest::Extend::Plugin::Bar MyTest::Plugin::Bar MyTest::Plugin::Foo MyTest::Plugin::Quux::Foo); push @plugins, $foo->plugins; push @plugins, $foo->foo; @plugins = sort @plugins; is_deeply(\@plugins, \@expected); @plugins = (); push @plugins, MyTest->plugins; push @plugins, MyTest->foo; @plugins = sort @plugins; is_deeply(\@plugins, \@expected); package MyTest; use strict; use Module::Pluggable; use Module::Pluggable ( search_path => [ "MyTest::Extend::Plugin" ] , sub_name => 'foo' ); sub new { my $class = shift; return bless {}, $class; } 1; Module-Pluggable-6.4/t/27app_fatpacker.t0000755000175100017510000000362615231231640017526 0ustar runnerrunner#!perl use strict; use warnings; use Test::More; BEGIN { my $need_version = "0.10.0"; if (eval "use App::FatPacker $need_version ; 1;") { plan tests => 2; } else { plan skip_all => "App::FatPacker >= $need_version not available"; } } use Cwd 'cwd'; use File::Temp; use File::Copy; use File::Find; use File::Path 'mkpath'; # use legacy interface for backwards compatibility use File::Spec::Functions qw(catdir catfile splitdir); # prepare directory for App::FatPacker my $testdir = File::Temp->newdir; my $fatlib = catdir($testdir->dirname, 'fatlib'); # copy our Module::Pluggable to $tempdir/fatlib mkpath $fatlib; copy_dir('lib', $fatlib, 1); # Copy the test application and its plugins to $tempdir/lib copy_dir(catdir('t', 'fp'), $testdir->dirname, 2); # fatpack it. fatpacker requires files be in the current directory my $cwd = cwd; my $packed = eval { chdir $testdir or die "unable to chdir to $testdir\n"; my $fp = App::FatPacker->new; $fp->fatpack_file('app.pl'); }; my $err = $@; chdir $cwd; BAIL_OUT("error fatpacking test application: $err") if $@; # write packed application to a file outside of the test dir to # make sure there's no way it can see its modules my $packed_file = File::Temp->new; $packed_file->print($packed); $packed_file->close; # run it (and it's included tests ) require_ok $packed_file; sub copy_dir { my ($from, $to, $shift) = @_; find( sub { my @p = splitdir($File::Find::dir); splice(@p, 0, $shift); my $ddir = catdir($to, @p); if (-d $_) { $ddir = catdir($ddir, $_); mkpath $ddir unless -d $ddir; } else { unless (copy($_, $ddir)) { my $file = catfile( $File::Find::dir, $_ ); die "error copying $file to $ddir: $!\n"; } } }, $from ); } Module-Pluggable-6.4/t/30search_dirs_strict.t0000644000175100017510000000175415231231640020573 0ustar runnerrunner#!perl -w use strict; use FindBin; use Test::More tests => 3; SKIP: { my $inc = IncTest->new(); $inc->plugins; ok($inc->required() > 0,'before_instantiate fired'); # In other words, we only required LocalIncTest::Plugin::Abbrev, not all the # other random stuff from a standard @INC. is_deeply( [ $inc->required ], [ 'LocalIncTest::Plugin::Abbrev' ], 'we only required the expected library', ); my ($ta) = $inc->required; is($ta->MPCHECK, 'HELLO', 'we got our version of LocalIncTest::Plugin::Abbrev'); }; package IncTest; our %REQUIRED; use Module::Pluggable search_path => "LocalIncTest::Plugin", search_dirs => "t/lib", search_dirs_strict => 1, require => 1, after_require => sub { $REQUIRED{$_[0]} = 1 }, on_require_error => sub { }; sub new { my $class = shift; return bless {}, $class; } sub required { my @req = sort keys %REQUIRED; return @req; } 1; Module-Pluggable-6.4/t/06multipath.t0000755000175100017510000000107315231231640016724 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 3; my $foo; ok($foo = MyTest->new()); my @plugins; my @expected = qw(Acme::MyTest::Plugin::Foo MyTest::Extend::Plugin::Bar); ok(@plugins = sort $foo->plugins); is_deeply(\@plugins, \@expected); package MyTest; use File::Spec::Functions qw(catdir); use strict; use File::Spec::Functions qw(catdir); use Module::Pluggable (search_path => ["MyTest::Extend::Plugin", "Acme::MyTest::Plugin"]); sub new { my $class = shift; return bless {}, $class; } 1; Module-Pluggable-6.4/t/02works_taint.t0000755000175100017510000000137615231231640017263 0ustar runnerrunner#!perl -wT # NOTE: Module::Pluggable is going into core # and CORE tests can't modify @INC under taint # so this is a work around to make sure it # still works under taint checking. use strict; use Test::More tests => 5; my $foo; ok($foo = MyTest->new()); my @plugins; my @expected = qw(Module::Pluggable::Object); ok(@plugins = sort $foo->plugins); ok(grep {/Module::Pluggable::Object/} @plugins, "Contains Module::Pluggable::Object"); @plugins = (); ok(@plugins = sort MyTest->plugins); ok(grep {/Module::Pluggable::Object/} @plugins, "Contains Module::Pluggable::Object under class method"); package MyTest; use strict; use Module::Pluggable search_path => 'Module::Pluggable'; sub new { my $class = shift; return bless {}, $class; } 1; Module-Pluggable-6.4/t/04acmedir_single.t0000644000175100017510000000076315231231640017662 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 3; my $foo; ok($foo = MyTest->new()); my @plugins; my @expected = qw(Acme::MyTest::Plugin::Foo); ok(@plugins = sort $foo->plugins); is_deeply(\@plugins, \@expected); package MyTest; use File::Spec::Functions qw(catdir); use strict; use Module::Pluggable search_path => "Acme::MyTest::Plugin", search_dirs => "t/acme" ; sub new { my $class = shift; return bless {}, $class; } 1; Module-Pluggable-6.4/t/10innerpack.t0000644000175100017510000000112415231231640016654 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 4; my $t = InnerTest->new(); my %plugins = map { $_ => 1 } $t->plugins; ok(keys %plugins, "Got some plugins"); ok($plugins{'InnerTest::Plugin::Foo'}, "Got Foo"); ok($plugins{'InnerTest::Plugin::Bar'}, "Got Bar - the inner package"); ok($plugins{'InnerTest::Plugin::Quux'}, "Got Quux - the other inner package"); package InnerTest; use strict; use Module::Pluggable require => 1; use base qw(Module::Pluggable); sub new { my $class = shift; return bless {}, $class; } 1; Module-Pluggable-6.4/t/15topicsafe.t0000644000175100017510000000041415231231640016665 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More 'no_plan'; use Module::Pluggable search_path => 'Acme::MyTest'; my $topic = "topic"; for ($topic) { main->plugins; } is($topic, 'topic', "we've got the right topic"); Module-Pluggable-6.4/t/17devel_inner_package.t0000644000175100017510000000044715231231640020665 0ustar runnerrunner#!perl -w use Test::More tests => 3; use Devel::InnerPackage qw(list_packages); use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); my @packages; use_ok("TA::C::A::I"); ok(@packages = list_packages("TA::C::A::I")); is_deeply([sort @packages], [qw(TA::C::A::I::A TA::C::A::I::A::B)]); Module-Pluggable-6.4/t/19can_ok_clobber.t0000644000175100017510000000152615231231640017643 0ustar runnerrunner#!/usr/bin/perl use strict; use warnings; use Data::Dumper; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests=>5; #use_ok( 'MyTest' ); #diag "Module::Pluggable::VERSION $Module::Pluggable::VERSION"; my @plugins = sort MyTest->plugins; my @plugins_after; use_ok( 'MyTest::Plugin::Foo' ); ok( my $foo = MyTest::Plugin::Foo->new() ); @plugins_after = MyTest->plugins; is_deeply( \@plugins_after, \@plugins, "plugins haven't been clobbered", ) or diag Dumper(\@plugins_after,\@plugins); can_ok ($foo, 'frobnitz'); @plugins_after = sort MyTest->plugins; is_deeply( \@plugins_after, \@plugins, "plugins haven't been clobbered", ) or diag Dumper(\@plugins_after,\@plugins); package MyTest; use strict; use Module::Pluggable; sub new { my $class = shift; return bless {}, $class; } 1; Module-Pluggable-6.4/t/13except.t0000644000175100017510000000213715231231640016202 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 10; { my $foo; ok($foo = MyTest->new()); my @plugins; my @expected = qw(MyTest::Plugin::Bar MyTest::Plugin::Quux::Foo); ok(@plugins = sort $foo->plugins); is_deeply(\@plugins, \@expected); @plugins = (); ok(@plugins = sort MyTest->plugins); is_deeply(\@plugins, \@expected); } { my $foo; ok($foo = MyTestSub->new()); my @plugins; my @expected = qw(MyTest::Plugin::Bar MyTest::Plugin::Quux::Foo); ok(@plugins = sort $foo->plugins); is_deeply(\@plugins, \@expected); @plugins = (); ok(@plugins = sort MyTestSub->plugins); is_deeply(\@plugins, \@expected); } package MyTest; use strict; use Module::Pluggable except => "MyTest::Plugin::Foo"; sub new { my $class = shift; return bless {}, $class; } package MyTestSub; use strict; use Module::Pluggable search_path => "MyTest::Plugin"; sub new { my $class = shift; my $self = bless {}, $class; $self->except("MyTest::Plugin::Foo"); return $self; } 1; Module-Pluggable-6.4/t/10innerpack_inner.t0000644000175100017510000000100315231231640020043 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 3; my $t = InnerTest->new(); my %plugins = map { $_ => 1 } $t->plugins; ok(keys %plugins, "Got some plugins"); ok($plugins{'InnerTest::Plugin::Foo'}, "Got Foo"); ok($plugins{'InnerTest::Plugin::Bar'}, "Got Bar - the inner package"); package InnerTest; use strict; use Module::Pluggable inner => 1; use base qw(Module::Pluggable); sub new { my $class = shift; return bless {}, $class; } 1; Module-Pluggable-6.4/t/fp/0000755000175100017510000000000015231231755014772 5ustar runnerrunnerModule-Pluggable-6.4/t/fp/lib/0000755000175100017510000000000015231231755015540 5ustar runnerrunnerModule-Pluggable-6.4/t/fp/lib/App/0000755000175100017510000000000015231231755016260 5ustar runnerrunnerModule-Pluggable-6.4/t/fp/lib/App/TestMPFP/0000755000175100017510000000000015231231755017662 5ustar runnerrunnerModule-Pluggable-6.4/t/fp/lib/App/TestMPFP/Plugin/0000755000175100017510000000000015231231755021120 5ustar runnerrunnerModule-Pluggable-6.4/t/fp/lib/App/TestMPFP/Plugin/A.pm0000644000175100017510000000004515231231640021626 0ustar runnerrunnerpackage App::TestMPFP::Plugin::A; 1; Module-Pluggable-6.4/t/fp/app.pl0000644000175100017510000000037015231231640016100 0ustar runnerrunner#!/usr/bin/env perl use strict; use warnings; use Test::More; my @plugins = App::TestMPFP->plugins(); is_deeply([@plugins], ['App::TestMPFP::Plugin::A'], 'found expected plugins'); package App::TestMPFP; use Module::Pluggable require => 1; 1; Module-Pluggable-6.4/t/22trigger.t0000644000175100017510000000300115231231640016344 0ustar runnerrunner#!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; Module-Pluggable-6.4/t/08nothing.t0000755000175100017510000000072115231231640016364 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 2; my $foo; ok($foo = MyTest->new()); my @expected = (); my @plugins = sort $foo->plugins; is_deeply(\@plugins, \@expected); package MyTest; use File::Spec::Functions qw(catdir); use strict; use Module::Pluggable (search_path => ["No::Such::Modules"]); use base qw(Module::Pluggable); sub new { my $class = shift; return bless {}, $class; } 1; Module-Pluggable-6.4/t/07instantiate.t0000755000175100017510000000146315231231640017244 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 6; my $foo; ok($foo = MyTest->new()); my @plugins; ok(@plugins = sort $foo->booga(nork => 'fark')); is(ref $plugins[0],'MyTest::Extend::Plugin::Bar'); is($plugins[0]->nork,'fark'); @plugins = (); eval { @plugins = $foo->wooga( nork => 'fark') }; is($@, ''); is(scalar(@plugins),0); package MyTest; use File::Spec::Functions qw(catdir); use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Module::Pluggable (search_path => ["MyTest::Extend::Plugin"], sub_name => 'booga', instantiate => 'new'); use Module::Pluggable (search_path => ["MyTest::Extend::Plugin"], sub_name => 'wooga', instantiate => 'nosomuchmethod'); sub new { my $class = shift; return bless {}, $class; } 1; Module-Pluggable-6.4/t/10innerpack_noinner.t0000644000175100017510000000103115231231640020401 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 3; my $t = InnerTest->new(); my %plugins = map { $_ => 1 } $t->plugins; ok(keys %plugins, "Got some plugins"); ok($plugins{'InnerTest::Plugin::Foo'}, "Got Foo"); ok(!$plugins{'InnerTest::Plugin::Bar'}, "Didn't get Bar - the inner package"); package InnerTest; use strict; use Module::Pluggable require => 1, inner => 0; use base qw(Module::Pluggable); sub new { my $class = shift; return bless {}, $class; } 1; Module-Pluggable-6.4/t/12onlyregex.t0000644000175100017510000000205215231231640016721 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 10; { my $foo; ok($foo = MyTest->new()); my @plugins; my @expected = qw(MyTest::Plugin::Foo); ok(@plugins = sort $foo->plugins); is_deeply(\@plugins, \@expected); @plugins = (); ok(@plugins = sort MyTest->plugins); is_deeply(\@plugins, \@expected); } { my $foo; ok($foo = MyTestSub->new()); my @plugins; my @expected = qw(MyTest::Plugin::Foo); ok(@plugins = sort $foo->plugins); is_deeply(\@plugins, \@expected); @plugins = (); ok(@plugins = sort MyTestSub->plugins); is_deeply(\@plugins, \@expected); } package MyTest; use strict; use Module::Pluggable only => qr/MyTest::Plugin::Foo$/; sub new { my $class = shift; return bless {}, $class; } package MyTestSub; use strict; use Module::Pluggable search_path => "MyTest::Plugin"; sub new { my $class = shift; my $self = bless {}, $class; $self->only(qr/MyTest::Plugin::Foo$/); return $self; } 1; Module-Pluggable-6.4/t/28appledouble.t0000755000175100017510000000064515231231640017221 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 3; my @expected = qw(Apple::Double::Plugin::File); ok(my $foo = Apple::Double->new()); ok(my @plugins = sort $foo->plugins); is_deeply(\@plugins, \@expected, "is deeply"); package Apple::Double; use strict; use warnings; use Module::Pluggable; sub new { my $class = shift; return bless {}, $class; } 1; Module-Pluggable-6.4/t/10innerpack_override.t0000644000175100017510000000102115231231640020547 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 3; my $t = InnerTest->new(); my %plugins = map { $_ => 1 } $t->plugins; ok(keys %plugins, "Got some plugins"); ok($plugins{'InnerTest::Plugin::Foo'}, "Got Foo"); ok($plugins{'InnerTest::Plugin::Bar'}, "Got Bar - the inner package"); package InnerTest; use strict; use Module::Pluggable require => 0, inner => 1; use base qw(Module::Pluggable); sub new { my $class = shift; return bless {}, $class; } 1; Module-Pluggable-6.4/t/24local_inc_package.t0000644000175100017510000000050615231231640020310 0ustar runnerrunner#!perl -w use strict; use FindBin; use Test::More tests => 1; IncTest->new()->plugins; is(LocalIncTest::Plugin::Abbrev->MPCHECK, "HELLO"); package IncTest; use Module::Pluggable search_path => "LocalIncTest::Plugin", search_dirs => "t/lib", require => 1; sub new { my $class = shift; return bless {}, $class; } 1; Module-Pluggable-6.4/t/23depth.t0000644000175100017510000000155715231231640016024 0ustar runnerrunner#!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([sort qw(MyOtherTest::Plugin::Bar MyOtherTest::Plugin::Foo MyOtherTest::Plugin::Quux)], [sort $max->plugins], "min depth"); is_deeply([qw(MyOtherTest::Plugin::Quux::Foo)], [sort $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;Module-Pluggable-6.4/t/31per_path_opts.t0000644000175100017510000000670015231231640017561 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib "$FindBin::Bin/lib"; use Test::More tests => 6; use Module::Pluggable::Object; # Basic per-path opts: two paths, no overrides { my $finder = Module::Pluggable::Object->new( search_path => { 'MyTest::Plugin' => {}, 'MyTest::Extend::Plugin' => {}, }, search_dirs => ["$FindBin::Bin/lib"], ); my @plugins = $finder->plugins; is_deeply( \@plugins, [qw(MyTest::Extend::Plugin::Bar MyTest::Plugin::Bar MyTest::Plugin::Foo MyTest::Plugin::Quux::Foo)], 'both paths searched, results merged and sorted', ); } # Per-path max_depth: restrict one path, not the other { my $finder = Module::Pluggable::Object->new( search_path => { 'MyTest::Plugin' => { max_depth => 3 }, 'MyTest::Extend::Plugin' => {}, }, search_dirs => ["$FindBin::Bin/lib"], ); my @plugins = $finder->plugins; is_deeply( \@plugins, [qw(MyTest::Extend::Plugin::Bar MyTest::Plugin::Bar MyTest::Plugin::Foo)], 'max_depth => 3 on MyTest::Plugin excludes Quux::Foo', ); } # Deduplication: same plugin reachable via two paths { my $finder = Module::Pluggable::Object->new( search_path => { 'MyTest::Plugin' => {}, 'MyTest::Plugin' => {}, # duplicate key, Perl keeps last — same path twice via hash trick won't work }, search_dirs => ["$FindBin::Bin/lib"], ); my @plugins = $finder->plugins; my %seen; my @dupes = grep { $seen{$_}++ } @plugins; is_deeply(\@dupes, [], 'no duplicates when same plugin appears via multiple paths'); } # Different options per path are isolated (max_depth on one does not bleed into the other) { my $finder = Module::Pluggable::Object->new( search_path => { 'MyTest::Plugin' => { max_depth => 3 }, 'MyTest::Extend::Plugin' => { max_depth => 3 }, }, search_dirs => ["$FindBin::Bin/lib"], ); my @plugins = $finder->plugins; is_deeply( \@plugins, [qw(MyTest::Plugin::Bar MyTest::Plugin::Foo)], 'max_depth => 3 applied independently to each path', ); } # Mixed arrayref form: order is preserved (path-discovery order), not re-sorted # alphabetically across paths, and per-path opts still apply. { my $finder = Module::Pluggable::Object->new( search_path => [ { 'MyTest::Plugin' => { max_depth => 3 } }, { 'MyTest::Extend::Plugin' => {} }, ], search_dirs => ["$FindBin::Bin/lib"], ); my @plugins = $finder->plugins; is_deeply( \@plugins, [qw(MyTest::Plugin::Bar MyTest::Plugin::Foo MyTest::Extend::Plugin::Bar)], 'mixed arrayref: MyTest::Plugin found (depth-limited) before MyTest::Extend::Plugin', ); } # Reversing the arrayref order reverses the result order, confirming it tracks # the spec rather than falling back to alphabetical sort. { my $finder = Module::Pluggable::Object->new( search_path => [ { 'MyTest::Extend::Plugin' => {} }, { 'MyTest::Plugin' => { max_depth => 3 } }, ], search_dirs => ["$FindBin::Bin/lib"], ); my @plugins = $finder->plugins; is_deeply( \@plugins, [qw(MyTest::Extend::Plugin::Bar MyTest::Plugin::Bar MyTest::Plugin::Foo)], 'mixed arrayref: reversing spec order reverses result order', ); } Module-Pluggable-6.4/t/04acmepath.t0000755000175100017510000000073515231231640016501 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 3; my $foo; ok($foo = MyTest->new()); my @plugins; my @expected = qw(Acme::MyTest::Plugin::Foo); ok(@plugins = sort $foo->plugins); is_deeply(\@plugins, \@expected); package MyTest; use File::Spec::Functions qw(catdir); use strict; use Module::Pluggable (search_path => ["Acme::MyTest::Plugin"]); sub new { my $class = shift; return bless {}, $class; } 1; Module-Pluggable-6.4/t/25single_letter_package.t0000755000175100017510000000077415231231640021240 0ustar runnerrunner#!perl -w use strict; use FindBin; use lib (($FindBin::Bin."/lib")=~/^(.*)$/); use Test::More tests => 5; my $foo; ok($foo = M->new()); my @plugins; my @expected = qw(M::X); ok(@plugins = sort $foo->plugins); is_deeply(\@plugins, \@expected, "is deeply"); @plugins = (); ok(@plugins = sort M->plugins); is_deeply(\@plugins, \@expected, "is deeply class"); package M; use strict; use Module::Pluggable search_path => "M"; sub new { my $class = shift; return bless {}, $class; } 1; Module-Pluggable-6.4/Makefile.PL0000644000175100017510000001027115231231640016066 0ustar runnerrunneruse 5.006; use strict; use warnings; use ExtUtils::MakeMaker; use File::Spec::Functions qw(catfile); ExtUtils::MakeMaker->VERSION(6.98) if -f '.gitignore'; # VOS and VMS can't handle dodgy plugin names # and VOS can't even unpack them so we create them on the # fly and only run the tests if they're present my %dodgy_files = ( catfile('OddTest', 'Plugin', '-Dodgy.pm') => 'OddTest::Plugin::-Dodgy', catfile('EditorJunk', 'Plugin', '#Bar.pm#') => 'EditorJunk::Bar', catfile('EditorJunk', 'Plugin', '.#Bar.pm') => 'EditorJunk::Bar', ); my @files; unless (grep { lc($^O) eq $_ } qw(vms vos)) { foreach my $test (keys %dodgy_files) { my ($file) = (catfile("t", "lib", $test)=~/^(.*)$/); if (open(FH, ">$file")) { my $name = $dodgy_files{$test}; print FH "package $name;\nsub new {}\n1;"; close(FH); push @files, $file; } } } my %WriteMakefileArgs = ( NAME => 'Module::Pluggable', VERSION_FROM => 'lib/Module/Pluggable.pm', ABSTRACT_FROM => 'lib/Module/Pluggable.pm', AUTHOR => 'Simon Wistow ', LICENSE => 'perl_5', MIN_PERL_VERSION => '5.006', INSTALLDIRS => ($] >= 5.008009 && $] <= 5.011000 ? 'perl' : 'site'), CONFIGURE_REQUIRES => { 'ExtUtils::MakeMaker' => '0', }, PREREQ_PM => { 'if' => '0', 'File::Basename' => '0', 'File::Spec' => '3.00', 'Exporter' => '5.57', # use Exporter 'import' 'File::Find' => '0', 'File::Spec::Functions' => '0', 'strict' => '0', 'Scalar::Util' => '0', }, TEST_REQUIRES => { 'Test::More' => '0', 'Cwd' => '0', 'Data::Dumper' => '0', 'File::Copy' => '0', 'File::Find' => '0', 'File::Path' => '0', 'File::Spec::Functions' => '0', 'File::Temp' => '0', 'base' => '0', 'parent' => '0', 'FindBin' => '0', 'strict' => '0', 'warnings' => '0', }, META_MERGE => { 'meta-spec' => { version => 2 }, dynamic_config => 0, resources => { repository => { url => 'https://github.com/simonwistow/Module-Pluggable.git', web => 'https://github.com/simonwistow/Module-Pluggable', type => 'git', }, bugtracker => { mailto => 'bug-Module-Pluggable@rt.cpan.org', web => 'https://rt.cpan.org/Public/Dist/Display.html?Name=Module-Pluggable', }, }, prereqs => { runtime => { recommends => { 'Module::Runtime' => '0.012', }, }, test => { suggests => { 'App::FatPacker' => '0.010000', }, }, }, }, ); # degrade gracefully for older EUMM/older perls if (!eval { ExtUtils::MakeMaker->VERSION('6.6303') }) { $WriteMakefileArgs{BUILD_REQUIRES} = $WriteMakefileArgs{TEST_REQUIRES}; delete $WriteMakefileArgs{TEST_REQUIRES}; } if (!eval { ExtUtils::MakeMaker->VERSION('6.5501') }) { @{$WriteMakefileArgs{PREREQ_PM}}{ keys %{$WriteMakefileArgs{BUILD_REQUIRES}} } = @{$WriteMakefileArgs{BUILD_REQUIRES}}{ keys %{$WriteMakefileArgs{BUILD_REQUIRES}} }; delete $WriteMakefileArgs{BUILD_REQUIRES}; } # On `make install`, delete any copy of this module left behind in a core # Perl library directory by an old release that used INSTALLDIRS => 'perl'. # Such a copy would otherwise shadow the new 'site' install (#39). Only # needed when we're actually installing to 'site' this time around. my $installing_to_site = $WriteMakefileArgs{INSTALLDIRS} eq 'site'; sub MY::postamble { return '' unless $installing_to_site; return <<'MAKE_FRAG'; install :: $(NOECHO) $(PERL) installscripts/remove_stale_core_copies.pl MAKE_FRAG } WriteMakefile(%WriteMakefileArgs); Module-Pluggable-6.4/META.json0000644000175100017510000000430215231231755015542 0ustar runnerrunner{ "abstract" : "automatically give your module the ability to have plugins", "author" : [ "Simon Wistow " ], "dynamic_config" : 0, "generated_by" : "ExtUtils::MakeMaker version 7.78, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Module-Pluggable", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "recommends" : { "Module::Runtime" : "0.012" }, "requires" : { "Exporter" : "5.57", "File::Basename" : "0", "File::Find" : "0", "File::Spec" : "3.00", "File::Spec::Functions" : "0", "Scalar::Util" : "0", "if" : "0", "perl" : "5.006", "strict" : "0" } }, "test" : { "requires" : { "Cwd" : "0", "Data::Dumper" : "0", "File::Copy" : "0", "File::Find" : "0", "File::Path" : "0", "File::Spec::Functions" : "0", "File::Temp" : "0", "FindBin" : "0", "Test::More" : "0", "base" : "0", "parent" : "0", "strict" : "0", "warnings" : "0" }, "suggests" : { "App::FatPacker" : "0.010000" } } }, "release_status" : "stable", "resources" : { "bugtracker" : { "mailto" : "bug-Module-Pluggable@rt.cpan.org", "web" : "https://rt.cpan.org/Public/Dist/Display.html?Name=Module-Pluggable" }, "repository" : { "type" : "git", "url" : "https://github.com/simonwistow/Module-Pluggable.git", "web" : "https://github.com/simonwistow/Module-Pluggable" } }, "version" : "6.4", "x_serialization_backend" : "JSON::PP version 4.16" } Module-Pluggable-6.4/installscripts/0000755000175100017510000000000015231231755017200 5ustar runnerrunnerModule-Pluggable-6.4/installscripts/remove_stale_core_copies.pl0000644000175100017510000000151315231231640024565 0ustar runnerrunner#!perl # Run by the 'install' target in the generated Makefile (see MY::postamble # in Makefile.PL). Deletes any copy of this module left behind in a core # Perl library directory by an old release that used INSTALLDIRS => 'perl'. # Those directories come before the site directories in @INC, so a stale # copy there silently shadows the newly installed 'site' copy (#39). use strict; use warnings; use Config; my @rel_paths = ( 'Module/Pluggable.pm', 'Module/Pluggable/Object.pm', ); my @core_dirs = grep { defined && length && -d $_ } ($Config{installprivlib}, $Config{installarchlib}); for my $dir (@core_dirs) { for my $rel (@rel_paths) { my $path = "$dir/$rel"; next unless -e $path; print "Removing stale core copy: $path\n"; unlink $path or warn "Couldn't remove $path: $!\n"; } } Module-Pluggable-6.4/Changes0000755000175100017510000002363715231231640015424 0ustar runnerrunner2026-07-25 - 6.4 Add a CI/CD pipeline for the first time in 26 years Add the ability to make search search "strict" and only walk the given search_dirs (thanks @rjbs) Add much more documentation to Module::Pluggable::Object (thanks Dave Rolsky) Make it so that you can give each search_path its own custom options Finally fix spurious test failure because of already installed modules I hope (thanks @SZREZIC, Andreas Koenig and @ZEFRAM) Remove optional_features from Makefile.PL META_MERGE which caused installation confusion sometimes Add the ability to have different sorting algorithms (thanks @CHOHAG) Fix the issues with OSX extended-attributes on files (thanks pgoyette99) Clean up old stale core-lib (thanks @dolmen and @haarg) 2025-01-10 - 6.3 Fix make_path() in t/29unreadable.t. (thanks to Michal Josef Špaček @michal-josef-spacek) Fix t/29unreadable.t tests failing as root. (thanks to Michal Josef Špaček @michal-josef-spacek) 2024-10-23 - 6.2 Simon, did you forget to remove a dependency you used only for testing? Yes, yes you did. 2024-10-22 - 6.1 Don't use vars, use our (thanks to Graham Knop @haarg) Ignore the .AppleDouble (thanks to Dave Cantrell @DrHyde) Improve vim swapfile patterns (thanks to Elan Ruusamäe @glensc) Fix behaviour of File::Find and symlinks on certain versions of Win32 (thanks @thockar) Skip unreadable files (thanks Sven Schober @sschober) 2015-11-20 - 6.0 Fix typo in examples (thanks to Zoffix Znet @zoffixznet) Fix spelling of "FatPacker" (thanks to David Wheeler @theory) Add before_instantiate and after_instantiate hooks (thanks to Dean Hamstead @djzort) Use explicit test plan instead of done_testing (thanks to Matthew Horsfall @wolfsage) 2015-08-05 - 5.2 Use Module::Runtime if it's installed (thanks to Olivier Mengué) Switch to using ExtUtils::MakeMaker (thanks to Karen Etheridge and Tim Orling) 2014-01-05 - 5.1 Add in missing files left out of the MANIFEST due to stupidity (thanks to Petr Pisar) 2013-12-20 - 5.0 Gain support for @INC hooks and hence for App::FatPacker (thanks to Diab Jerius) 2013-10-22 - 4.9 Fix the fact that we can't handle single letter package names (thanks sbaynes) 2013-05-27 - 4.8 Fix some typos (David Steinbrunner) Fix error in testing when Text::BibTex is installed (thanks to Andreas Koenig) 2013-02-25 - 4.7 Fix more hash ordering bugs in tests by forcing sort of returned plugins (Yves Orton) 2013-01-23 - 4.6 Add warning about future removal from core 2012-11-05 - 4.5 Fix docs Fix problem with PAUSE upload 2012-11-05 - 4.4 Fix hash ordering bug in tests (Yves Orton) Fix install dir (Tatsuhiko Miyagawa) 2012-08-15 - 4.3 Fix calling the correct method when instantiating (Doh!) Hopefully stop smoker failures 2012-08-14 - 4.2 Fix a problem with installation directory (Jerry D. Hedden) 2012-07-20 - 4.1 Allow triggers on events which gives a powerful way to modify behaviour (Tim Brody, Dan Dascalescu, Paul Evans, Jens Rehsack) Put documentation in about behaviour under blib and test, allow searching outside blib under test (suggestion from Stephen Baynes) Made following symlinks the default behaviour, added ability to turn that off (Aran Deltac) Fix installation path (Matthias Dietrich, Todd Rinaldo) Allow min_depth and max_depth (as per suggestion from Jens Rehsack) Set our @INC up to include and prefer our search_dirs if necessary (as per suggestion from Ian Goodacre) Switch to Module::Build 2012-01-03 - 4.0 Don't use defined on an array (it's deprecated) 2009-03-02 - 3.9 Allow inner packages when in a one file situation (suggestion from Christopher Brown) Fix bug with requiring skipped packages (from Jos Boumans) 2008-03-16 - 3.8 Set INSTALLDIRS correctly in Makefile.PL A couple of other fixups to play nicely in Core 2008-03-12 - 3.7 Ignore editor cruft by default (Dave Rolsky and Matt Trout) Doc patches (Matt Trout) Prevent prototype mismatch warnings under Error.pm (Christopher H. Laco) Don't pick up the ::SUPER pseudo stash in 5.8 (Alex Vandiver) Make things work under VOS (Nicholas Clark and Paul Green) Fix warning under Devel::Cover (Brian Cassidy) Make tests run under Taint again Get rid of Build.PL 2007-04-07 - 3.6 Include blead perl patch from Craig Berry that gives better Module::Pluggable::Object::search_paths portability as prompted by VMS test failures. 2007-01-29 - 3.5 Patch from Audrey Tang to prevent clobbering of $@ 2006-11-27 - 3.4 Make sure we don't fail taint checking when other Module::Pluggable::* modules are installed. 2006-11-24 - 3.3 Few more patches from Jos Boumans to get ready for CORE 2006-11-15 - 3.2 Remove Class::Inspector dependency and inline code Prepare for assimilation into CORE 2006-07-11 - 3.1 Force Test::More version to be latest which stops it trampling on $_ Use Class::Inspector tests to check to see Package is loaded thus preventing incompatability problems introduced by last patch. 2006-06-07 - 3.01 Fix from Brian Cassidy in Devel::InnerPackage 2006-06-06 - 3.0 Big refactor to split stuff up into more manageable pieces 2006-04-05 - 2.98 Allow the ability to provide the file matching regex 2006-02-06 - 2.97 Patch from Ricardo Signes to fix bug where File::Find is not topic-safe in 5.6.1 2005-09-01 - 2.96 Patch from Alex Vandiver to sort an edge case where the package stash to contain "::" 2005-07-30 - 2.95 Patch from Alex Vandiver to sort ::ISA::CACHE issues. More patches from Christopher H. Laco to sort out only and except and to fix calling search_path( add => ... ) before plugins() 2005-07-09 - 2.9 More Tainting fixes Patches from Christopher H. Laco and Joe McMahon to do more taint fixing Suggestion from Christopher H. Laco to do a can check before instatiating 2005-03-18 - 2.8 Minor fixes Patch from Marcus Thiesen to fix so ISA cache magic Patch from Anthony D. Urso to get M::P to work under taint 2005-02-14 - 2.7 Allow redefinition of search_path A patch from Barbie to allow you to do $self->search_path( add => 'Some::Path' ); $self->search_path( new => 'Some::New::Path' ); 2005-02-02 - 2.6 Problems under tests Richard Clamp diagnosed a problem as being due to the fact that we exclude anything not from blib if there was blib.pm is %INC. Of course if the module being tested used another module that used Module::Pluggable then the second module would fail. Fixed it by checking to see if the caller had (^|/)blib/ in their filename. 2004-12-20 - 2.5 'Inspiration' from Module::Pluggable::Fast Noticed Sebastian Riedel's curious Module::Pluggable::Fast which appears to break API backwards compatability in order to inline an explicit call to 'new'. It has no tests. A quick benchmark showed that it was about 10% faster because of cruft that had accumulated over time. So a few quick changes and now Module::Pluggable is only 3% slower. Which is nice. Also added a patch from Barbie to fix things under Windows. 2004-12-15 - 2.4 Bug fix There seemed to be some irregularities in how 5.8.1 worked with the list_packages method. Fixed thanks to Schwern, Michael Cummings and Jos Boumans. Added some more documentation. Added ability to specify except and only as regexes. 2004-10-27 - 2.3 Niceties Allow you to explicitly stop looking for inner packages. Made it nicer to have single element search_dirs and search_paths. 2004-10-08 - 2.2 Dieting Thanks to suggestion and patches from Adam Kennedy Module::Pluggable has dumped some weight and got rid of the none Core dependecies of File::Find::Rule and UNIVERSAL::require 2004-08-25 - 2.1 Small buglette Require inner packages when appropriate. This helps towards making things work with PAR. Thanks to Brian Cassidy. Never released. 2004-08-19 - 2.0 Working inner packages As long as you have require or instantiate set then we'll also find inner packages. Why I didn't fix this way back in 1.3 I don't know. 2004-07-18 - 1.9 Add 'package' option This lets you install a method in another package's namespace. Thanks to Simon Cozens. 2004-07-08 - 1.8 Fix those two options They worked before but now they're more robust. 2004-07-07 - 1.7 Add support for limiting plugins Added 'only' and ''except' options. 2004-06-03 - 1.6 Add a traditional Makefile.PL Even though I think Module::Build is much better. 2004-05-25 - 1.5 Build.PL stupidity Must add prereqs. Doh. 2004-05-25 - 1.4 Multiple instances Made it so you could use it twice in the same package. Removed the inner package stuff inorder to fix it properly. 2004-05-06 - 1.3 Ability to search in inner packages Simon Cozens donated some code to allow us to search inner packages. 2004-05-06 - 1.2 Fix minor buglet Apply a patch from Tom Insam to fix requiring without instantiating and make require failures more verbose. 2003-12-15 - 1.11 Update MANIFEST. So that make dist works properly. 2003-12-15 - 1.1 Make it work with multi level plugins Apparently Foo::Plugin::Bar::Quux doesn't work. Thanks to Darren Chamberlain for spotting this. 2003-12-15 - 1.00 Add some more features Added the ability to require without instantiating (for Tom Insam) Prevented the names from being explicitly sorted (again, for Tom Insam) Added in the ability to provide other search directorys. 2003-11-27 - 0.95 Some Test::More issues Explicitly number the tests which fixes some test more failures 2003-10-21 - 0.9 We can rebuild you, we have the technology Having used this in the wild some changes to make it better. 2003-10-20 - 0.8 Fix. Namespace issues. 2003-10-17 - 0.7 And take your Build.PL with you Pesky MANIFEST.SKIP 2003-10-15 - 0.6 Initial release Be free my pretty. EMANCIPIA! Module-Pluggable-6.4/lib/0000755000175100017510000000000015231231755014670 5ustar runnerrunnerModule-Pluggable-6.4/lib/Devel/0000755000175100017510000000000015231231755015727 5ustar runnerrunnerModule-Pluggable-6.4/lib/Devel/InnerPackage.pm0000755000175100017510000000462215231231640020614 0ustar runnerrunnerpackage Devel::InnerPackage; use strict; use Exporter 5.57 'import'; use if $] > 5.017, 'deprecate'; our $VERSION = '0.4'; our @EXPORT_OK = qw(list_packages); =pod =head1 NAME Devel::InnerPackage - find all the inner packages of a package =head1 SYNOPSIS use Foo::Bar; use Devel::InnerPackage qw(list_packages); my @inner_packages = list_packages('Foo::Bar'); =head1 DESCRIPTION Given a file like this package Foo::Bar; sub foo {} package Foo::Bar::Quux; sub quux {} package Foo::Bar::Quirka; sub quirka {} 1; then list_packages('Foo::Bar'); will return Foo::Bar::Quux Foo::Bar::Quirka =head1 METHODS =head2 list_packages Return a list of all inner packages of that package. =cut sub list_packages { my $pack = shift; $pack .= "::" unless $pack =~ m!::$!; no strict 'refs'; my @packs; my @stuff = grep !/^(main|)::$/, keys %{$pack}; for my $cand (grep /::$/, @stuff) { $cand =~ s!::$!!; my @children = list_packages($pack.$cand); push @packs, "$pack$cand" unless $cand =~ /^::/ || !__PACKAGE__->_loaded($pack.$cand); # or @children; push @packs, @children; } return grep {$_ !~ /::(::ISA::CACHE|SUPER)/} @packs; } ### XXX this is an inlining of the Class-Inspector->loaded() ### method, but inlined to remove the dependency. sub _loaded { my ($class, $name) = @_; no strict 'refs'; # Handle by far the two most common cases # This is very fast and handles 99% of cases. return 1 if defined ${"${name}::VERSION"}; return 1 if @{"${name}::ISA"}; # Are there any symbol table entries other than other namespaces foreach ( keys %{"${name}::"} ) { next if substr($_, -2, 2) eq '::'; return 1 if defined &{"${name}::$_"}; } # No functions, and it doesn't have a version, and isn't anything. # As an absolute last resort, check for an entry in %INC my $filename = join( '/', split /(?:'|::)/, $name ) . '.pm'; return 1 if defined $INC{$filename}; ''; } =head1 AUTHOR Simon Wistow =head1 COPYING Copyright, 2005 Simon Wistow Distributed under the same terms as Perl itself. =head1 BUGS None known. =cut 1; Module-Pluggable-6.4/lib/Module/0000755000175100017510000000000015231231755016115 5ustar runnerrunnerModule-Pluggable-6.4/lib/Module/Pluggable.pm0000755000175100017510000003540415231231640020357 0ustar runnerrunnerpackage Module::Pluggable; use strict; use Module::Pluggable::Object; use if $] > 5.017, 'deprecate'; # ObQuote: # Bob Porter: Looks like you've been missing a lot of work lately. # Peter Gibbons: I wouldn't say I've been missing it, Bob! our $VERSION = '6.4'; our $FORCE_SEARCH_ALL_PATHS = 0; sub import { my $class = shift; my %opts = @_; my ($pkg, $file) = caller; # the default name for the method is 'plugins' my $sub = $opts{'sub_name'} || 'plugins'; # get our package 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); my $subroutine = sub { my $self = shift; return $finder->plugins(@_) }; my $search_path_sub = sub { my $self = shift; my ($action,@paths) = @_; $finder->{'search_path'} = ["${package}::Plugin"] if ($action eq 'add' and not $finder->{'search_path'} ); push @{$finder->{'search_path'}}, @paths if ($action eq 'add'); $finder->{'search_path'} = \@paths if ($action eq 'new'); return $finder->{'search_path'}; }; my $search_dirs_sub = sub { my $self = shift; my ($action,@paths) = @_; $finder->{'search_dirs'} = ["${package}::Plugin"] if ($action eq 'add' and not $finder->{'search_dirs'} ); push @{$finder->{'search_dirs'}}, @paths if ($action eq 'add'); $finder->{'search_dirs'} = \@paths if ($action eq 'new'); return $finder->{'search_dirs'}; }; my $onlysub = sub { my ($self, $only) = @_; if (defined $only) { $finder->{'only'} = $only; }; return $finder->{'only'}; }; my $exceptsub = sub { my ($self, $except) = @_; if (defined $except) { $finder->{'except'} = $except; }; return $finder->{'except'}; }; no strict 'refs'; no warnings qw(redefine prototype); *{"$package\::$sub"} = $subroutine; *{"$package\::search_path"} = $search_path_sub; *{"$package\::search_dirs"} = $search_dirs_sub; *{"$package\::only"} = $onlysub; *{"$package\::except"} = $exceptsub; } 1; =pod =head1 NAME Module::Pluggable - automatically give your module the ability to have plugins =head1 SYNOPSIS Simple use Module::Pluggable - package MyClass; use Module::Pluggable; and then later ... use MyClass; my $mc = MyClass->new(); # returns the names of all plugins installed under MyClass::Plugin::* my @plugins = $mc->plugins(); =head1 EXAMPLE Why would you want to do this? Say you have something that wants to pass an object to a number of different plugins in turn. For example you may want to extract meta-data from every email you get sent and do something with it. Plugins make sense here because then you can keep adding new meta data parsers and all the logic and docs for each one will be self contained and new handlers are easy to add without changing the core code. For that, you might do something like ... package Email::Examiner; use strict; use Email::Simple; use Module::Pluggable require => 1; sub handle_email { my $self = shift; my $email = shift; foreach my $plugin ($self->plugins) { $plugin->examine($email); } return 1; } .. and all the plugins will get a chance in turn to look at it. This can be trivially extended so that plugins could save the email somewhere and then no other plugin should try and do that. Simply have it so that the C method returns C<1> if it has saved the email somewhere. You might also want to be paranoid and check to see if the plugin has an C method. foreach my $plugin ($self->plugins) { next unless $plugin->can('examine'); last if $plugin->examine($email); } And so on. The sky's the limit. =head1 DESCRIPTION Provides a simple but, hopefully, extensible way of having 'plugins' for your module. Obviously this isn't going to be the be all and end all of solutions but it works for me. Essentially all it does is export a method into your namespace that looks through a search path for .pm files and turn those into class names. 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'; and then later ... my @plugins = $mc->foo(); Or if you want to look in another namespace package MyClass; use Module::Pluggable search_path => ['Acme::MyClass::Plugin', 'MyClass::Extend']; or directory use Module::Pluggable search_dirs => ['mylibs/Foo']; If you I want to search those C, provide a true value for C. Or if you want to instantiate each plugin rather than just return the name package MyClass; use Module::Pluggable instantiate => 'new'; and then # whatever is passed to 'plugins' will be passed # to 'new' for each plugin my @plugins = $mc->plugins(@options); alternatively you can just require the module without instantiating it package MyClass; use Module::Pluggable require => 1; since requiring automatically searches inner packages, which may not be desirable, you can turn this off package MyClass; use Module::Pluggable require => 1, inner => 0; You can limit the plugins loaded using the except option, either as a string, array ref or regex package MyClass; use Module::Pluggable except => 'MyClass::Plugin::Foo'; or package MyClass; use Module::Pluggable except => ['MyClass::Plugin::Foo', 'MyClass::Plugin::Bar']; or package MyClass; use Module::Pluggable except => qr/^MyClass::Plugin::(Foo|Bar)$/; and similarly for only which will only load plugins which match. Remember you can use the module more than once package MyClass; use Module::Pluggable search_path => 'MyClass::Filters', sub_name => 'filters'; use Module::Pluggable search_path => 'MyClass::Plugins', sub_name => 'plugins'; 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 If you have, for example, a file B that contains package definitions for both C and C then as long as you either have either the B or B option set then we'll also find C. Nifty! =head1 OPTIONS You can pass a hash of options when importing this module. The options can be ... =head2 sub_name The name of the subroutine to create in your namespace. By default this is 'plugins' =head2 search_path An array ref of namespaces to look in. search_path => ['MyApp::Plugin', 'MyApp::Extension'] Alternatively, you can pass a hash ref where each key is a namespace prefix and each value is a hash ref of option overrides for that path. The per-path options are merged on top of the global options, so you only need to specify what differs. search_path => { 'MyApp::Plugin' => { max_depth => 2 }, 'MyApp::Extension' => { max_depth => 3, instantiate => 'new' }, } If you want plugins to be in a particular order, use an array ref of mixed plain strings and single-key hash refs instead. Order is preserved; only the hash ref entries carry per-path option overrides. search_path => [ 'MyApp::Plugin', { 'MyApp::Extension' => { max_depth => 3, instantiate => 'new' } }, ] Results from all paths are combined and returned in a single list. Class names are deduplicated across paths; if any path uses C its objects are included as-is alongside any class names from other paths. =head2 search_dirs An array ref of directories to look in before @INC. =head2 instantiate Call this method on the class. In general this will probably be 'new' but it can be whatever you want. Whatever arguments are passed to 'plugins' will be passed to the method. The default is 'undef' i.e just return the class name. =head2 require Just require the class, don't instantiate (overrides 'instantiate'); =head2 inner If set to 0 will B search inner packages. If set to 1 will override C. =head2 only Takes a string, array ref or regex describing the names of the only plugins to return. Whilst this may seem perverse ... well, it is. But it also makes sense. Trust me. =head2 except Similar to C it takes a description of plugins to exclude from returning. This is slightly less perverse. =head2 package This is for use by extension modules which build on C: passing a C option allows you to place the plugin method in a different package other than your own. =head2 file_regex By default C only looks for I<.pm> files. By supplying a new C then you can change this behaviour e.g file_regex => qr/\.plugin$/ =head2 include_editor_junk By default C ignores files that look like they were left behind by editors. Currently this means files ending in F<~> (~), the extensions F<.swp> or F<.swo>, or files beginning with F<.#>. Setting C changes C 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 will have a depth of 3 and C will have a depth of 4 so to only get the former (i.e C) do package MyClass; use Module::Pluggable max_depth => 3; and to only get the latter (i.e C) package MyClass; use Module::Pluggable min_depth => 4; =head2 sort_results Controls the order plugin names (or objects) are returned in. One of: =over 4 =item * C<'alpha'> (the default) - sorted alphabetically by package name. =item * C<'path'> - returned in search-path discovery order: all plugins under the first C entry, then the second, and so on. =item * C<0> - no sort is applied at all. Fastest option, for callers that will sort or otherwise order the results themselves. =back package MyClass; use Module::Pluggable sort_results => 'path'; =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 Gets passed the plugin name. If 0 is returned then this plugin will not be required either. =head2 on_require_error 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 the error and return 0. =head2 after_require Gets passed the plugin name. If 0 is returned then this plugin will be required but not instantiated or returned as a plugin. =head2 before_instantiate Gets passed the plugin name. If 0 is returned, then instantiation will not occur. =head2 on_instantiate_error 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 the error and return 0. =head2 after_instantiate Gets passed the plugin name and the object that was instentiated. i.e. the return value of Cnew()> The return value must be and will be returned AS the plugin. If the return value is not true, then nothing will be returned as a plugin. =head1 METHODs =head2 search_path The method C is exported into you namespace as well. You can call that at any time to change or replace the 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 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 @INC hooks and App::FatPacker If a module's @INC has a hook and that hook is an object which has a C method then we will try and require those files too. See C for an example. This has allowed L (as of version 0.10.0) to provide support for Module::Pluggable. This should also, theoretically, allow someone to modify PAR to do the same thing. =head1 Module::Require recommended Up until version 5.2 L used a string C to require plugins. This has now been changed to optionally use L and it's C method when available and fall back to using a path based C when not. It's recommended, but not required, that you install Module::Runtime. =head1 FUTURE PLANS This does everything I need and I can't really think of any other features I want to add. Famous last words of course (not least because we're up to version 5.0 at the time of writing). However suggestions (and patches) are always welcome. =head1 DEVELOPMENT The master repo for this module is at https://github.com/simonwistow/Module-Pluggable =head1 AUTHOR Simon Wistow =head1 COPYING Copyright, 2006 Simon Wistow Distributed under the same terms as Perl itself. =head1 BUGS None known. =head1 SEE ALSO L, L, L, L, L =cut Module-Pluggable-6.4/lib/Module/Pluggable/0000755000175100017510000000000015231231755020017 5ustar runnerrunnerModule-Pluggable-6.4/lib/Module/Pluggable/Object.pm0000755000175100017510000006544115231231640021571 0ustar runnerrunnerpackage Module::Pluggable::Object; use strict; use File::Find (); use File::Basename; use File::Spec::Functions qw(splitdir catdir curdir catfile abs2rel); use Carp qw(croak carp confess); use Devel::InnerPackage; use Scalar::Util qw( blessed ); use if $] > 5.017, 'deprecate'; our $VERSION = '5.3'; BEGIN { eval { require Module::Runtime }; unless ($@) { Module::Runtime->import('require_module'); } else { *require_module = sub { my $module = shift; my $path = $module . ".pm"; $path =~ s{::}{/}g; require $path; }; } } =head1 NAME Module::Pluggable::Object - the underlying plugin-finding object used by Module::Pluggable =head1 SYNOPSIS package MyClass; use Module::Pluggable::Object; my $finder = Module::Pluggable::Object->new( package => __PACKAGE__, search_path => ['MyClass::Plugin'], require => 1, ); my @plugins = $finder->plugins; print "Found: ", join(", ", @plugins), "\n"; =head1 DESCRIPTION C is the engine underneath L. It walks a set of search paths (namespaces and directories) looking for C<.pm> files, maps their paths to package names, and optionally Cs or instantiates them. Most of the time you should use L directly, which wraps this class and exports a C method into your namespace. Use this class directly when you need more control: =over 4 =item * You want to create multiple independent finders in the same package. =item * You want to subclass or decorate the finder object. =item * You want to store the finder and call C on it explicitly rather than through an exported method. =item * You are building your own higher-level abstraction on top of the plugin-discovery machinery. =back =head1 CONSTRUCTOR =head2 new(%opts) my $finder = Module::Pluggable::Object->new(%opts); Creates and returns a new finder object. All options are passed as a flat hash; they are stored on the object and consulted by L. See L below for the full list. =cut sub new { my $class = shift; my %opts = @_; return bless \%opts, $class; } =head1 METHODS =head2 plugins my @plugins = $finder->plugins; # list of class names my @objects = $finder->plugins(@args); # instances when 'instantiate' is set The main entry point. Walks C across C (and C<@INC>) and returns either a sorted list of fully-qualified package names or, if C is set, a list of objects created by calling the named method on each plugin class. Arguments passed to C are forwarded to the C method on each plugin class. The full search is repeated on every call, which allows plugins installed after startup to be discovered at run time. If you do not need dynamic discovery and want to pay the I/O cost only once, memoize the result yourself: our @PLUGINS; sub plugins { @PLUGINS ||= shift->_finder->plugins } =cut ### Eugggh, this code smells ### This is what happens when you keep adding patches ### *sigh* # Normalises the three supported forms of 'search_path' into an ordered # list of [$namespace, \%per_path_opts] pairs: # "Foo::Plugin" -> plain scalar # ["Foo::Plugin", "Bar::Plugin"] -> plain arrayref # { "Foo::Plugin" => { max_depth => 2 }, ... } -> hashref, sorted for determinism # ["Foo::Plugin", { "Bar::Plugin" => { ... } }] -> mixed arrayref, preserves order sub _normalise_search_path { my ($sp) = @_; return () unless defined $sp; return ([$sp, {}]) unless ref $sp; if (ref($sp) eq 'HASH') { return map { [$_, $sp->{$_} || {}] } sort keys %$sp; } if (ref($sp) eq 'ARRAY') { my @out; for my $item (@$sp) { if (!ref($item)) { push @out, [$item, {}]; } elsif (ref($item) eq 'HASH') { push @out, map { [$_, $item->{$_} || {}] } sort keys %$item; } } return @out; } return (); } sub plugins { my $self = shift; my @args = @_; my $pkg = $self->{'package'}; # Normalise search_path, then apply the default my @path_specs = _normalise_search_path($self->{'search_path'}); @path_specs = ([$pkg . '::Plugin', {}]) unless @path_specs; # If any path carries per-path option overrides, dispatch each path through # its own sub-finder and merge results in spec order, deduplicating class names. if (grep { %{$_->[1]} } @path_specs) { my @results; for my $spec (@path_specs) { my ($path, $per_path) = @$spec; my $sub = ref($self)->new(%$self, %$per_path, search_path => [$path]); push @results, $sub->plugins(@args); } my %seen; return grep { ref($_) ? 1 : !$seen{$_}++ } @results; } # No per-path overrides: restore as a simple arrayref and continue as normal $self->{'search_path'} = [map { $_->[0] } @path_specs]; # override 'require' $self->{'require'} = 1 if $self->{'inner'}; my $filename = $self->{'filename'}; # Get the exception params instantiated $self->_setup_exceptions; # automatically turn a scalar search_dirs into an arrayref $self->{'search_dirs'} = [ $self->{'search_dirs'} ] if exists $self->{'search_dirs'} && !ref($self->{'search_dirs'}); # 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 }; # before and after instantiation hooks $self->{'before_instantiate'} ||= sub { 1 }; $self->{'after_instantiate'} ||= sub { return $_[1] }; # default whether to follow symlinks # because the default behavior is changed in the Perl-CORE module File::Find VERSION >= '1.39', # in lower versions of File::Find, 'follow_symlinks' is (independent from the callers setting of # 'follow_symlinks') hardcoded set to 0 on Windows so we force File::Find to fall back to the old # behavior, if not otherwise told $self->{'follow_symlinks'} = 0 if ($File::Find::VERSION >= '1.39' && $^O eq 'MSWin32' && ! exists $self->{'follow_symlinks'} ); $self->{'follow_symlinks'} = 1 unless exists $self->{'follow_symlinks'}; # 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; if ($self->{'search_dirs_strict'}) { die "search_dirs_strict: makes no sense without setting search_dirs" unless $self->{'search_dirs'}; @SEARCHDIR = (); } # 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'}; my @plugins = $self->search_directories(@SEARCHDIR); push(@plugins, $self->handle_inc_hooks($_, @SEARCHDIR)) for @{$self->{'search_path'}}; push(@plugins, $self->handle_innerpackages($_)) for @{$self->{'search_path'}}; # return blank unless we've found anything return () unless @plugins; # remove duplicates, keeping track of discovery order for 'sort_results' my (@dedupe, %plugins); for(@plugins) { next unless $self->_is_legit($_); next if $plugins{$_}++; push @dedupe, $_; } my $sort_results = exists $self->{'sort_results'} ? $self->{'sort_results'} : 'alpha'; my @ordered = $sort_results eq 'path' ? @dedupe : !$sort_results ? keys %plugins : sort keys %plugins; # are we instantiating or requiring? if (defined $self->{'instantiate'}) { my $method = $self->{'instantiate'}; my @objs = (); foreach my $package (@ordered) { next unless $package->can($method); $self->{'before_instantiate'}->($package) or next; my $obj = eval { $package->$method(@_) }; # We dont actually care what ->$method() returns $self->{'on_instantiate_error'}->($package, $@) if $@; if ($obj) { $obj = $self->{'after_instantiate'}->($package,$obj) or next; # Again, we dont actually care if we get a blessed reference or not push @objs, $obj; } } return @objs; } else { # no? just return the names my @objs = @ordered; return @objs; } } sub _setup_exceptions { my $self = shift; my %only; my %except; my $only; my $except; if (defined $self->{'only'}) { if (ref($self->{'only'}) eq 'ARRAY') { %only = map { $_ => 1 } @{$self->{'only'}}; } elsif (ref($self->{'only'}) eq 'Regexp') { $only = $self->{'only'} } elsif (ref($self->{'only'}) eq '') { $only{$self->{'only'}} = 1; } } if (defined $self->{'except'}) { if (ref($self->{'except'}) eq 'ARRAY') { %except = map { $_ => 1 } @{$self->{'except'}}; } elsif (ref($self->{'except'}) eq 'Regexp') { $except = $self->{'except'} } elsif (ref($self->{'except'}) eq '') { $except{$self->{'except'}} = 1; } } $self->{_exceptions}->{only_hash} = \%only; $self->{_exceptions}->{only} = $only; $self->{_exceptions}->{except_hash} = \%except; $self->{_exceptions}->{except} = $except; } sub _is_legit { my $self = shift; my $plugin = shift; my %only = %{$self->{_exceptions}->{only_hash}||{}}; 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 0 if $plugin =~ /(^|::).AppleDouble/; return 1; } =head2 search_directories(@dirs) my @plugins = $finder->search_directories(@INC); Iterates over C<@dirs> and delegates to L for each one. Normally called internally by L, but exposed so subclasses can override the top-level directory loop. =cut sub search_directories { my $self = shift; my @SEARCHDIR = @_; my @plugins; # go through our @INC foreach my $dir (@SEARCHDIR) { push @plugins, $self->search_paths($dir); } return @plugins; } =head2 search_paths($dir) my @plugins = $finder->search_paths($dir); For a single base directory, iterates over C namespaces, locates C<.pm> files under the corresponding subdirectory, converts file paths to package names, and calls L for each candidate. Handles case-insensitive filesystems by reading the C declaration out of the file when the file name is all-lower or all-upper case. =cut sub search_paths { my $self = shift; my $dir = shift; my @plugins; my $file_regex = $self->{'file_regex'} || qr/\.pm$/; # and each directory in our search path foreach my $searchpath (@{$self->{'search_path'}}) { # create the search directory in a cross platform goodness way my $sp = catdir($dir, (split /::/, $searchpath)); # if it doesn't exist or it's not a dir then skip it next unless ( -e $sp && -d _ ); # Use the cached stat the second time my @files = $self->find_files($sp); # foreach one we've found foreach my $file (@files) { # untaint the file; accept .pm only next unless ($file) = ($file =~ /(.*$file_regex)$/); # parse the file to get the name my ($name, $directory, $suffix) = fileparse($file, $file_regex); next if (!$self->{include_editor_junk} && $self->_is_editor_junk($name)); $directory = abs2rel($directory, $sp); # If we have a mixed-case package name, assume case has been preserved # correctly. Otherwise, root through the file to locate the case-preserved # version of the package name. my @pkg_dirs = (); if ( $name eq lc($name) || $name eq uc($name) ) { my $pkg_file = catfile($sp, $directory, "$name$suffix"); open PKGFILE, "<$pkg_file" or die "search_paths: Can't open $pkg_file: $!"; my $in_pod = 0; while ( my $line = ) { $in_pod = 1 if $line =~ m/^=\w/; $in_pod = 0 if $line =~ /^=cut/; 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 if defined $1;; $name = $2; last; } } close PKGFILE; } # then create the class name in a cross platform way $directory =~ s/^[a-z]://i if($^O =~ /MSWin32|dos/); # remove volume my @dirs = (); if ($directory) { ($directory) = ($directory =~ /(.*)/); @dirs = grep(length($_), splitdir($directory)) unless $directory eq curdir(); for my $d (reverse @dirs) { my $pkg_dir = pop @pkg_dirs; last unless defined $pkg_dir; $d =~ s/\Q$pkg_dir\E/$pkg_dir/i; # Correct case } } else { $directory = ""; } my $plugin = join '::', $searchpath, @dirs, $name; next unless $plugin =~ m!(?:[a-z\d]+)[a-z\d]*!i; $self->handle_finding_plugin($plugin, \@plugins) } # now add stuff that may have been in package # NOTE we should probably use all the stuff we've been given already # but then we can't unload it :( push @plugins, $self->handle_innerpackages($searchpath); } # foreach $searchpath return @plugins; } sub _is_editor_junk { my $self = shift; my $name = shift; # Emacs (and other Unix-y editors) leave temp files ending in a # tilde as a backup. return 1 if $name =~ /~$/; # Emacs makes these files while a buffer is edited but not yet # saved. return 1 if $name =~ /^\.#/; # Vim can leave these files behind if it crashes. return 1 if $name =~ /^[._].*\.s[a-w][a-z]$/; return 0; } =head2 handle_finding_plugin($plugin, \@plugins, $no_require) $finder->handle_finding_plugin('MyApp::Plugin::Foo', \@plugins); Called for each candidate package name discovered during the search. Applies the C/C/C/C filters, invokes C and C triggers, optionally Cs the module, and pushes it onto C<\@plugins> if everything succeeds. Pass a true value for C<$no_require> to skip the C step (used when the module is known to already be loaded, e.g. for inner packages). =cut sub handle_finding_plugin { my $self = shift; my $plugin = shift; my $plugins = shift; my $no_req = shift || 0; return unless $self->_is_legit($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 { require_module($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; } =head2 find_files($search_path) my @files = $finder->find_files($dir); Recursively finds all files matching C (default C) under C<$dir> using L. Respects the C option. =cut sub find_files { my $self = shift; my $search_path = shift; my $file_regex = $self->{'file_regex'} || qr/\.pm$/; # find all the .pm files in it # this isn't perfect and won't find multiple plugins per file #my $cwd = Cwd::getcwd; my @files = (); { # for the benefit of perl 5.6.1's Find, localize topic local $_; File::Find::find( { no_chdir => 1, 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#^\\./##; push @files, $path; } }, $search_path ); } #chdir $cwd; return @files; } =head2 handle_inc_hooks($search_path, @dirs) my @plugins = $finder->handle_inc_hooks($path, @SEARCHDIR); Handles the case where an entry in C<@INC> is a blessed object with a C method (as used by L and L). Calls C on each such object, filters by C<$search_path>, and delegates to L. =cut sub handle_inc_hooks { my $self = shift; my $path = shift; my @SEARCHDIR = @_; my @plugins; for my $dir ( @SEARCHDIR ) { next unless blessed( $dir ) && $dir->can( 'files' ); foreach my $plugin ( $dir->files ) { $plugin =~ s/\.pm$//; $plugin =~ s{/}{::}g; next unless $plugin =~ m!^${path}::!; $self->handle_finding_plugin( $plugin, \@plugins ); } } return @plugins; } =head2 handle_innerpackages($search_path) my @plugins = $finder->handle_innerpackages('MyApp::Plugin'); Uses L to find any package declared inside an already- loaded file whose name falls under C<$search_path>. This is how C is discovered when it is defined inside F. Set C<< inner => 0 >> to disable this behaviour. =cut sub handle_innerpackages { my $self = shift; return () if (exists $self->{inner} && !$self->{inner}); my $path = shift; my @plugins; foreach my $plugin (Devel::InnerPackage::list_packages($path)) { $self->handle_finding_plugin($plugin, \@plugins, 1); } return @plugins; } 1; =head1 OPTIONS All options are passed to C as a flat hash and may be set or overridden directly on the object before calling C. =head2 package The package name used to build the default C. When using L this is set automatically to the calling package. When using C directly you should set it explicitly. =head2 search_path An array ref (or a single string, which is promoted to an array ref) of namespace prefixes to search. Defaults to C<["${package}::Plugin"]>. search_path => ['MyApp::Plugin', 'MyApp::Extension'] Alternatively, you can pass a hash ref where each key is a namespace prefix and each value is a hash ref of option overrides for that path. The per-path options are merged on top of the global options, so you only need to specify what differs. search_path => { 'MyApp::Plugin' => { max_depth => 2 }, 'MyApp::Extension' => { max_depth => 3, instantiate => 'new' }, } As another alternative, if you want plugins to be in a particular order, use an array ref of mixed plain strings and single-key hash refs. Order is preserved; only the hash ref entries carry per-path option overrides. search_path => [ 'MyApp::Plugin', { 'MyApp::Extension' => { max_depth => 3, instantiate => 'new' } }, ] Results from all paths are combined and returned in a single list. Class names are deduplicated across paths; if any path uses C its objects are included as-is alongside any class names from other paths. =head2 search_dirs An array ref (or a single string) of filesystem directories to search I C<@INC>. Use this when plugins live outside the normal include path. search_dirs => ['/opt/myapp/plugins'] =head2 search_dirs_strict When true, only the directories in C are searched; C<@INC> is ignored entirely. Requires C to also be set. search_dirs_strict => 1 =head2 instantiate Name of the method to call on each plugin class to create an instance. Typically C<'new'>. When set, C returns a list of objects instead of class names. Arguments passed to C are forwarded to this method. instantiate => 'new' =head2 require When true, each plugin module is Cd but not instantiated. Overrides C. Also enables inner-package discovery (see L). require => 1 =head2 inner Controls whether inner packages (secondary C declarations inside a single F<.pm> file) are discovered. =over 4 =item * Defaults to true when C or C is set. =item * Set to C<0> to disable even when C is set. =back inner => 0 =head2 only Restricts the returned plugins to those matching this value. May be: =over 4 =item * A string - only the exact named plugin is returned. =item * An array ref of strings - only those exact plugins are returned. =item * A compiled regex - only plugins whose name matches are returned. =back only => qr/^MyApp::Plugin::Safe/ =head2 except Excludes matching plugins from the results. Accepts the same forms as C. except => ['MyApp::Plugin::Broken', 'MyApp::Plugin::Deprecated'] =head2 min_depth / max_depth Restrict discovery to plugins at a certain namespace depth (number of C<::>-separated components). For example, C has depth 3 and C has depth 4. max_depth => 3 # only MyApp::Plugin::Foo, not MyApp::Plugin::Foo::Bar min_depth => 4 # only MyApp::Plugin::Foo::Bar and deeper =head2 file_regex A compiled regex used to identify plugin files. Defaults to C. file_regex => qr/\.plugin$/ =head2 follow_symlinks Whether to follow symbolic links when walking the filesystem. Defaults to C<1> (follow symlinks) on all platforms except Windows with L E 1.39, where it defaults to C<0> to preserve the historical behaviour. follow_symlinks => 0 =head2 include_editor_junk By default, files that look like editor artefacts are silently skipped: =over 4 =item * Files ending in C<~> (Emacs / generic backup files). =item * Files beginning with C<.#> (Emacs lock files). =item * Files matching C<^[._].*\.s[a-w][a-z]$> (Vim swap files). =back Set C to a true value to disable this filtering and include every file that matches C. include_editor_junk => 1 =head2 force_search_all_paths By default, when the module is loaded from inside a C tree (i.e. during C), only C<@INC> entries that themselves contain C are searched. Set this option to search all of C<@INC> regardless. force_search_all_paths => 1 =head2 sort_results Controls the order plugin names (or objects) are returned in. One of: =over 4 =item * C<'alpha'> (the default) - sorted alphabetically by package name. =item * C<'path'> - returned in search-path discovery order: all plugins under the first C entry, then the second, and so on. Within a single path, order follows the underlying filesystem traversal. =item * C<0> - no sort is applied at all. Fastest option, for callers that will sort or otherwise order the results themselves. =back sort_results => 'path' =head1 TRIGGERS Triggers are callbacks passed as options to C. If a trigger returns C<0> (or any false value), the plugin is dropped at that point in the pipeline. =head2 before_require before_require => sub { my ($plugin) = @_; ... } Called with the plugin package name before it is Cd. Return C<0> to skip both the C and all subsequent steps for this plugin. =head2 on_require_error on_require_error => sub { my ($plugin, $err) = @_; ... } Called when C raises an exception. The default handler Cs the error and returns C<0> (dropping the plugin). Return a true value to keep the plugin in the list despite the error. =head2 after_require after_require => sub { my ($plugin) = @_; ... } Called after a successful C. Return C<0> to exclude the plugin from results even though it has already been loaded. =head2 before_instantiate before_instantiate => sub { my ($plugin) = @_; ... } Called with the plugin package name before the C method is invoked. Return C<0> to skip instantiation for this plugin. =head2 on_instantiate_error on_instantiate_error => sub { my ($plugin, $err) = @_; ... } Called when the C method raises an exception. The default handler Cs the error and returns C<0>. =head2 after_instantiate after_instantiate => sub { my ($plugin, $obj) = @_; return $obj } Called after successful instantiation with the plugin name and the newly created object. The return value is used as the plugin entry in the results list; return a false value to discard the plugin entirely. Use this to wrap or decorate plugin objects transparently. =head1 BEHAVIOUR UNDER TEST ENVIRONMENTS When C is present in C<%INC> and the calling file's path contains C, only C<@INC> entries that themselves contain C are searched. This prevents installed (non-blib) versions of plugins from interfering with tests. See C to override this. =head1 @INC HOOKS AND App::FatPacker If an entry in C<@INC> is a blessed object with a C method, its C list is searched for plugin modules. This is how L (from version 0.10.0 onwards) integrates with C, and how L can be made to do the same. See F for a working example. =head1 AUTHOR Simon Wistow =head1 COPYING Copyright, 2006-2026 Simon Wistow Distributed under the same terms as Perl itself. =head1 SEE ALSO L, L, L, L, L =cut Module-Pluggable-6.4/INSTALL0000755000175100017510000000021515231231640015145 0ustar runnerrunner Same as practically every other Perl module ... % perl Makefile.PL % make % make test % sudo make install Module-Pluggable-6.4/README0000755000175100017510000002410715231231640015002 0ustar runnerrunnerNAME Module::Pluggable - automatically give your module the ability to have plugins SYNOPSIS Simple use Module::Pluggable - package MyClass; use Module::Pluggable; and then later ... use MyClass; my $mc = MyClass->new(); # returns the names of all plugins installed under MyClass::Plugin::* my @plugins = $mc->plugins(); EXAMPLE Why would you want to do this? Say you have something that wants to pass an object to a number of different plugins in turn. For example you may want to extract meta-data from every email you get sent and do something with it. Plugins make sense here because then you can keep adding new meta data parsers and all the logic and docs for each one will be self contained and new handlers are easy to add without changing the core code. For that, you might do something like ... package Email::Examiner; use strict; use Email::Simple; use Module::Pluggable require => 1; sub handle_email { my $self = shift; my $email = shift; foreach my $plugin ($self->plugins) { $plugin->examine($email); } return 1; } .. and all the plugins will get a chance in turn to look at it. This can be trivally extended so that plugins could save the email somewhere and then no other plugin should try and do that. Simply have it so that the "examine" method returns 1 if it has saved the email somewhere. You might also want to be paranoid and check to see if the plugin has an "examine" method. foreach my $plugin ($self->plugins) { next unless $plugin->can('examine'); last if $plugin->examine($email); } And so on. The sky's the limit. DESCRIPTION Provides a simple but, hopefully, extensible way of having 'plugins' for your module. Obviously this isn't going to be the be all and end all of solutions but it works for me. Essentially all it does is export a method into your namespace that looks through a search path for .pm files and turn those into class names. Optionally it instantiates those classes for you. ADVANCED USAGE Alternatively, if you don't want to use 'plugins' as the method ... package MyClass; use Module::Pluggable sub_name => 'foo'; and then later ... my @plugins = $mc->foo(); Or if you want to look in another namespace package MyClass; use Module::Pluggable search_path => ['Acme::MyClass::Plugin', 'MyClass::Extend']; or directory use Module::Pluggable search_dirs => ['mylibs/Foo']; Or if you want to instantiate each plugin rather than just return the name package MyClass; use Module::Pluggable instantiate => 'new'; and then # whatever is passed to 'plugins' will be passed # to 'new' for each plugin my @plugins = $mc->plugins(@options); alternatively you can just require the module without instantiating it package MyClass; use Module::Pluggable require => 1; since requiring automatically searches inner packages, which may not be desirable, you can turn this off package MyClass; use Module::Pluggable require => 1, inner => 0; You can limit the plugins loaded using the except option, either as a string, array ref or regex package MyClass; use Module::Pluggable except => 'MyClass::Plugin::Foo'; or package MyClass; use Module::Pluggable except => ['MyClass::Plugin::Foo', 'MyClass::Plugin::Bar']; or package MyClass; use Module::Pluggable except => qr/^MyClass::Plugin::(Foo|Bar)$/; and similarly for only which will only load plugins which match. Remember you can use the module more than once package MyClass; use Module::Pluggable search_path => 'MyClass::Filters' sub_name => 'filters'; use Module::Pluggable search_path => 'MyClass::Plugins' sub_name => 'plugins'; and then later ... my @filters = $self->filters; my @plugins = $self->plugins; 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; INNER PACKAGES If you have, for example, a file lib/Something/Plugin/Foo.pm that contains package definitions for both "Something::Plugin::Foo" and "Something::Plugin::Bar" then as long as you either have either the require or instantiate option set then we'll also find "Something::Plugin::Bar". Nifty! OPTIONS You can pass a hash of options when importing this module. The options can be ... sub_name The name of the subroutine to create in your namespace. By default this is 'plugins' search_path An array ref of namespaces to look in. search_dirs An array ref of directorys to look in before @INC. instantiate Call this method on the class. In general this will probably be 'new' but it can be whatever you want. Whatever arguments are passed to 'plugins' will be passed to the method. The default is 'undef' i.e just return the class name. require Just require the class, don't instantiate (overrides 'instantiate'); inner If set to 0 will not search inner packages. If set to 1 will override "require". only Takes a string, array ref or regex describing the names of the only plugins to return. Whilst this may seem perverse ... well, it is. But it also makes sense. Trust me. except Similar to "only" it takes a description of plugins to exclude from returning. This is slightly less perverse. package This is for use by extension modules which build on "Module::Pluggable": passing a "package" option allows you to place the plugin method in a different package other than your own. file_regex By default "Module::Pluggable" only looks for *.pm* files. By supplying a new "file_regex" then you can change this behaviour e.g file_regex => qr/\.plugin$/ include_editor_junk By default "Module::Pluggable" ignores files that look like they were left behind by editors. Currently this means files ending in ~ (~), the extensions .swp or .swo, or files beginning with .#. Setting "include_editor_junk" changes "Module::Pluggable" so it does not ignore any files it finds. follow_symlinks Whether, when searching directories, to follow symlinks. Defaults to 1 i.e do follow symlinks. min_depth, max_depth This will allow you to set what 'depth' of plugin will be allowed. So, for example, "MyClass::Plugin::Foo" will have a depth of 3 and "MyClass::Plugin::Foo::Bar" will have a depth of 4 so to only get the former (i.e "MyClass::Plugin::Foo") do package MyClass; use Module::Pluggable max_depth => 3; and to only get the latter (i.e "MyClass::Plugin::Foo::Bar") package MyClass; use Module::Pluggable min_depth => 4; 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. before_require Gets passed the plugin name. If 0 is returned then this plugin will not be required either. on_require_error 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 "carp" the error and return 0. on_instantiate_error 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 "carp" the error and return 0. after_require Gets passed the plugin name. If 0 is returned then this plugin will be required but not returned as a plugin. METHODs search_path The method "search_path" is exported into you namespace as well. You can call that at any time to change or replace the search_path. $self->search_path( add => "New::Path" ); # add $self->search_path( new => "New::Path" ); # replace 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 "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; FUTURE PLANS This does everything I need and I can't really think of any other features I want to add. Famous last words of course Recently tried fixed to find inner packages and to make it 'just work' with PAR but there are still some issues. However suggestions (and patches) are welcome. DEVELOPMENT The master repo for this module is at https://github.com/simonwistow/Module-Pluggable AUTHOR Simon Wistow COPYING Copyright, 2006 Simon Wistow Distributed under the same terms as Perl itself. BUGS None known. SEE ALSO File::Spec, File::Find, File::Basename, Class::Factory::Util, Module::Pluggable::Ordered Module-Pluggable-6.4/META.yml0000644000175100017510000000221115231231755015367 0ustar runnerrunner--- abstract: 'automatically give your module the ability to have plugins' author: - 'Simon Wistow ' build_requires: Cwd: '0' Data::Dumper: '0' ExtUtils::MakeMaker: '0' File::Copy: '0' File::Find: '0' File::Path: '0' File::Spec::Functions: '0' File::Temp: '0' FindBin: '0' Test::More: '0' base: '0' parent: '0' strict: '0' warnings: '0' configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 0 generated_by: 'ExtUtils::MakeMaker version 7.78, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Module-Pluggable no_index: directory: - t - inc recommends: Module::Runtime: '0.012' requires: Exporter: '5.57' File::Basename: '0' File::Find: '0' File::Spec: '3.00' File::Spec::Functions: '0' Scalar::Util: '0' if: '0' perl: '5.006' strict: '0' resources: bugtracker: https://rt.cpan.org/Public/Dist/Display.html?Name=Module-Pluggable repository: https://github.com/simonwistow/Module-Pluggable.git version: '6.4' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' Module-Pluggable-6.4/MANIFEST.SKIP0000644000175100017510000000037715231231640016020 0ustar runnerrunnerBuild$ _build blib Makefile$ \.tar\.gz$ \.svn \.git \.bak$ ^Module-Pluggable t/lib/OddTest/Plugin/-Dodgy.pm t/lib/EditorJunk/Plugin/#Bar.pm# t/lib/EditorJunk/Plugin/.#Bar.pm ^MYMETA\.yml$ ^MYMETA\.json$ # macOS extended-attribute sidecar files (^|/)\._ Module-Pluggable-6.4/MANIFEST0000644000175100017510000000436215231231640015251 0ustar runnerrunnerChanges INSTALL lib/Devel/InnerPackage.pm lib/Module/Pluggable.pm lib/Module/Pluggable/Object.pm Makefile.PL MANIFEST This list of files MANIFEST.SKIP META.json META.yml README installscripts/remove_stale_core_copies.pl t/01use.t t/02alsoworks.t t/02works.t t/02works_taint.t t/03diffname.t t/04acmedir.t t/04acmedir_single.t t/04acmepath.t t/04acmepath_single.t t/05postpath.t t/06multipath.t t/07instantiate.t t/08nothing.t t/09require.t t/10innerpack.t t/10innerpack_inner.t t/10innerpack_noinner.t t/10innerpack_onefile.t t/10innerpack_override.t t/10innerpack_super.t t/11usetwice.t t/12only.t t/12onlyarray.t t/12onlyregex.t t/12onlyrequire.t t/13except.t t/13exceptarray.t t/13exceptregex.t t/14package.t t/15topicsafe.t t/16different_extension.t t/17devel_inner_package.t t/18skipped_package.t t/19can_ok_clobber.t t/20dodgy_files.t t/21editor_junk.t t/22trigger.t t/23depth.t t/24local_inc_object.t t/24local_inc_package.t t/25single_letter_package.t t/26inc_hook.t t/27app_fatpacker.t t/28appledouble.t t/29unreadable.t <<<<<<< HEAD t/30search_dirs_strict.t t/31per_path_opts.t ======= t/30per_path_opts.t t/31sort_results.t >>>>>>> 14fe60b (Add opt-in sort_results option.) t/acme/Acme/MyTest/Plugin/Foo.pm t/fp/app.pl t/fp/lib/App/TestMPFP/Plugin/A.pm t/lib/Acme/Foo-Bar.pm t/lib/Acme/MyTest/Plugin/Foo.pm t/lib/Apple/Double/Plugin/.AppleDouble/File.pm t/lib/Apple/Double/Plugin/File.pm t/lib/EditorJunk/Plugin/Bar.pm t/lib/EditorJunk/Plugin/Bar.pm.swo t/lib/EditorJunk/Plugin/Bar.pm.swp t/lib/EditorJunk/Plugin/Bar.pm~ t/lib/EditorJunk/Plugin/Foo.pm t/lib/ExtTest/Plugin/Bar.plugin t/lib/ExtTest/Plugin/Foo.plugin t/lib/ExtTest/Plugin/Quux/Foo.plugin t/lib/InnerTest/Plugin/Foo.pm t/lib/LocalIncTest/Plugin/Abbrev.pm t/lib/M/X.pm t/lib/MyOtherTest/Plugin/Bar.pm t/lib/MyOtherTest/Plugin/Foo.pm t/lib/MyOtherTest/Plugin/Quux.pm t/lib/MyOtherTest/Plugin/Quux/Foo.pm t/lib/MyTest/Extend/Plugin/Bar.pm t/lib/MyTest/Plugin/Bar.pm t/lib/MyTest/Plugin/Foo.pm t/lib/MyTest/Plugin/Quux/Foo.pm t/lib/No/Middle.pm t/lib/OddTest/Plugin/Foo.pm t/lib/TA/C/A/I.pm t/lib/TriggerTest/Plugin/After.pm t/lib/TriggerTest/Plugin/CallbackAllow.pm t/lib/TriggerTest/Plugin/CallbackDeny.pm t/lib/TriggerTest/Plugin/Deny.pm t/lib/TriggerTest/Plugin/Error.pm t/lib/Zot/.Zork.pm t/search_dirs_strict.t