I don't see how a tied hash (ref) is a special case.
use strict;
use warnings;
package Nothing;
sub isa { 0 }
package NotHash;
use overload '%{}' => sub { {} };
sub new { bless [], shift }
package OverHash;
use overload '%{}' => sub { {} };
sub new { bless {}, shift }
package main;
use Tie::Memoize;
use Test::More;
my @test_cases = ( { name => 'unblessed hash',
test => {},
is_hash => 1, },
{ name => 'unblessed array',
test => [],
is_hash => 0, },
{ name => q{hash blessed as 'HASH'},
test => bless( {}, 'HASH' ),
is_hash => 1, },
{ name => q{array blessed as 'HASH'},
test => bless( [], 'HASH' ),
is_hash => 0, },
{ name => q{hash blessed as 'ARRAY'},
test => bless( {}, 'ARRAY' ),
is_hash => 1, },
{ name => 'hash in package 0',
test => bless( {}, '0' ),
is_hash => 1, },
{ name => 'array in package 0',
test => bless( [], '0' ),
is_hash => 0, },
{ name => 'hash with ->isa overridden',
test => bless( {}, 'Nothing' ),
is_hash => 1, },
{ name => 'blessed array with %{} overloaded',
test => NotHash->new(),
is_hash => 1, },
{ name => 'blessed hash with %{} overloaded',
test => OverHash->new(),
is_hash => 1, },
{ name => 'tied hash',
test => get_tied_hash(),
is_hash => 1, },
{ name => 'not a reference',
test => 'HASH',
is_hash => 0, },
);
sub get_tied_hash {
tie my %h, 'Tie::Memoize', sub {};
return \%h;
}
plan 'tests' => scalar @test_cases;
foreach my $test ( @test_cases ) {
is( !!isHash( $test->{test} ), !!$test->{is_hash}, $test->{name});
}
use Scalar::Util qw( reftype blessed );
sub isHash {
my $suspected_hash = shift;
return 0 if '' eq ref $suspected_hash;
return 1 if 'HASH' eq reftype $suspected_hash;
if ( blessed $suspected_hash
&& overload::Method( $suspected_hash, '%{}' ) ) {
return 1;
}
return 0;
}
The code for isHash is based heavily on blokhead's from "Is it a hashref" vs "Can I use it like a hashref?"
Have I missed an important test case here?