From: Karen Etheridge Date: Tue, 3 Oct 2017 00:49:48 +0000 (-0700) Subject: preserve list context for the sub being called by $_call_if_can X-Git-Tag: v1.000008~2 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=p5sagit%2FSafe-Isa.git;a=commitdiff_plain;h=2e029f319a43f444105ec4ea2587103980a2b7e7 preserve list context for the sub being called by $_call_if_can also add some additional tests to demonstrate behaviour in list context (some of which failed before the fix) --- diff --git a/Changes b/Changes index e09ce96..71a2409 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,7 @@ Revision history for Safe-Isa + - fix scalar/list context handling for $_call_if_can + 1.000007 - 2017-09-22 - added new interface: $obj->$_call_if_can diff --git a/lib/Safe/Isa.pm b/lib/Safe/Isa.pm index e7a1337..01933ab 100644 --- a/lib/Safe/Isa.pm +++ b/lib/Safe/Isa.pm @@ -26,7 +26,8 @@ our ($_isa, $_can, $_does, $_DOES) = map { our $_call_if_can = sub { my ($obj, $method) = (shift, shift); - $obj->$_call_if_object(can => $method) && $obj->$method(@_); + return unless $obj->$_call_if_object(can => $method); + return $obj->$method(@_); }; 1; @@ -123,6 +124,9 @@ class names that you might not want to treat as one (like say "Matt") - the C function from L is a good way to check for something you might be able to call methods on if you want to do that. +We are careful to make sure that scalar/list context is preserved for the +method that is eventually called. + =head1 EXPORTS =head2 $_isa diff --git a/t/safe_isa.t b/t/safe_isa.t index 6657722..d650af1 100644 --- a/t/safe_isa.t +++ b/t/safe_isa.t @@ -1,9 +1,9 @@ use strict; use warnings; -use Test::More tests => 38; +use Test::More tests => 68; { package Foo; sub new { bless({}, $_[0]) } } -{ package Bar; our @ISA = qw(Foo); sub bar { $_[1] } } +{ package Bar; our @ISA = qw(Foo); sub bar { wantarray ? ( 5, 6 ) : $_[1] } } my $foo = Foo->new; my $bar = Bar->new; @@ -25,6 +25,8 @@ ok(!eval { $undef->can('bar'); 1 }, 'undef goes poof'); use Safe::Isa; +note 'scalar context..'; + ok($foo->$_isa('Foo'), 'foo $_isa Foo'); ok($bar->$_isa('Foo'), 'bar $_isa Foo'); ok(eval { is($blam->$_isa('Foo'), undef, 'blam isn\'t Foo'); 1 }, 'no boom today'); @@ -49,3 +51,64 @@ is($bar->$_call_if_can(bar => ), undef, 'bar $_call_if_can(bar => )'); is($bar->$_call_if_can(bar => 2), 2, 'bar $_call_if_can(bar => 2)'); ok(eval { is($blam->$_call_if_can(isa => 'Foo'), undef, 'blam can\'t call anything'); 1 }, 'no boom today'); ok(eval { is($undef->$_call_if_can(isa => 'Foo'), undef, 'undef can\'t call anything'); 1 }, 'and no boom tomorrow either'); + + +note 'list context..'; + +# isa always returns true/false +is_deeply([ $foo->$_isa('Foo') ], [ 1 ], 'foo $_isa Foo'); +is_deeply([ $bar->$_isa('Foo') ], [ 1 ], 'bar $_isa Foo'); +ok( + eval { is_deeply([ $blam->$_isa('Foo') ], [], 'blam isn\'t Foo'); 1 }, + 'no boom today', +); +ok( + eval { is_deeply([ $undef->$_isa('Foo') ], [], 'undef isn\'t Foo either'); 1 }, + 'and no boom tomorrow either', +); + +# can returns ref/undef if it ran, or false if not an object. +is_deeply([ $foo->$_can('bar') ], [ undef ], 'foo !$_can bar'); +is_deeply([ $bar->$_can('bar') ], [ \&Bar::bar ], 'bar $_can bar'); +ok( + eval { is_deeply([ $blam->$_can('bar') ], [], 'blam can\'t bar'); 1 }, + 'no boom today', +); +ok( + eval { is_deeply([ $undef->$_can('bar') ], [], 'undef can\'t bar either'); 1 }, + 'and no boom tomorrow either', +); + +# _call_if_object has the same behaviour as the method it is calling and +# propagates context. +is_deeply([ $foo->$_call_if_object(isa => 'Foo') ], [ 1 ], 'foo $_call_if_object(isa => Foo)'); +is_deeply([ $bar->$_call_if_object(isa => 'Foo') ], [ 1 ], 'bar $_call_if_object(isa => Foo)'); +is_deeply([ $bar->$_call_if_object(bar => ) ], [ 5, 6 ], 'bar $_call_if_object(bar => undef): wantarray is true'); +is_deeply([ $bar->$_call_if_object(bar => 2) ], [ 5, 6 ], 'bar $_call_if_object(bar => 2): wantarray is true'); +ok( + eval { is_deeply([ $blam->$_call_if_object(isa => 'Foo') ], [], 'blam can\'t call anything'); 1 }, + 'no boom today', +); +ok( + eval { is_deeply([ $undef->$_call_if_object(isa => 'Foo') ], [], 'undef can\'t call anything'); 1 }, + 'and no boom tomorrow either', +); + +# _call_if_can has the same behaviour as the method it is calling and +# propagates context. +is_deeply([ $foo->$_call_if_can(isa => 'Foo') ], [ 1 ], 'foo $_call_if_can(isa => Foo)'); +is_deeply([ $bar->$_call_if_can(isa => 'Foo') ], [ 1 ], 'bar $_call_if_can(isa => Foo)'); +ok( + eval { is_deeply([ $foo->$_call_if_can(bar => ) ], [], 'foo can\'t call bar'); 1 }, + 'no boom today', +); +is_deeply([ $bar->$_call_if_can(bar => ) ], [ 5, 6 ], 'bar $_call_if_can(bar => ): wantarray is true'); +is_deeply([ $bar->$_call_if_can(bar => 2) ], [ 5, 6 ], 'bar $_call_if_can(bar => 2): wantarray is true'); +ok( + eval { is_deeply([ $blam->$_call_if_can(isa => 'Foo') ], [], 'blam can\'t call anything'); 1 }, + 'no boom today', +); +ok( + eval { is_deeply([ $undef->$_call_if_can(isa => 'Foo') ], [], 'undef can\'t call anything'); 1 }, + 'and no boom tomorrow either', +);