{
package Naughty;
use base 'Exporter';
our @EXPORT = 'some_func';
sub some_func { 'naughty function. no biscuit' }
}
{
package Unsuspecting;
Naughty->import;
sub new { bless {}, shift }
sub _private { 'Unsuspecting _private' }
}
if ( Unsuspecting->can('some_func') ) {
die "a horrible death";
}
elsif ( Unsuspecting->can('_private') ) {
die "Don't rely on private methods!";
}
else {
print "home free";
}
####
package strict::can;
use warnings;
use strict;
use vars qw( $VERSION );
use B qw/svref_2object/;
use base 'Exporter';
our @EXPORT = qw(can);
my $package_for = sub {
my $package;
eval {
my $stash = svref_2object(shift)->STASH;
if ( $stash && $stash->can('NAME') ) {
$package = $stash->NAME;
}
else {
$package = '';
}
};
if ($@) {
warn "Could not determine calling package: $@";
}
return $package;
};
sub can {
my ( $proto, $method ) = @_;
return if '_' eq substr $method, 0, 1;
my $class = ref $proto || $proto;
if ( my $code = $proto->SUPER::can($method) ) {
my $code_package = $package_for->($code);
if ( $code_package eq $class || $class->isa($code_package) ) {
return $code;
}
else {
return;
}
}
return;
}
1;
####
#!perl
#use Test::More tests => 1;
use Test::More qw/no_plan/;
BEGIN {
chdir 't' if -d 't';
unshift @INC, '../lib';
}
{
package Naughty;
use base 'Exporter';
our @EXPORT = 'some_func';
sub some_func { 'naughty function. no biscuit' }
}
{
package Unsuspecting;
use strict::can;
Naughty->import;
sub new { bless {}, shift }
sub _private { 'Unsuspecting _private' }
}
{
package Unsuspecting::Subclass;
our @ISA = 'Unsuspecting';
}
ok my $new = Unsuspecting->can('new'),
'can() should return something if a package->can do something';
is ref $new, 'CODE', '... and that something should be a code reference';
ok my $object = Unsuspecting->$new, '... which we should be able to call';
is ref $object, 'Unsuspecting', '... and it should behave as we expect';
ok !Unsuspecting->can('some_func'),
'strict::can() should not report imported functions';
ok Unsuspecting::some_func(), '... even though the package can still call it';
ok !Unsuspecting->can('_private'),
'strict::can should ignore functions which begin with an underscore';
ok $new = Unsuspecting::Subclass->can('new'),
'can() should return something if a subclass->can do something';
is ref $new, 'CODE', '... and that something should be a code reference';
ok $object = Unsuspecting::Subclass->$new,
'... which we should be able to call';
is ref $object, 'Unsuspecting::Subclass',
'... and it should behave as we expect';
ok !Unsuspecting::Subclass->can('some_func'),
'strict::can() should not report imported functions';
ok !Unsuspecting::Subclass->can('_private'),
'strict::can should ignore functions which begin with an underscore';
####
sub can {
my ( $proto, $method ) = @_;
my ($callpack) = caller(0);
my $class = ref $proto || $proto;
if ( my $coderef = $proto->SUPER::can($method) ) {
return $coderef if 'can' eq $method;
my $code_package = $package_for->($coderef);
if (
$class->isa($code_package)
&&
('_' ne substr($method, 0, 1) || $callpack->isa($code_package))
)
{
return $coderef;
}
}
return;
}