Home > other >  Perl - how to check if a hashref is an object?
Perl - how to check if a hashref is an object?

Time:07-24

My perl code makes a call to an API, passing a reference to an array of string IDs, and expects to get an array of objects back.

my @customers = $api->get_customer_details(\@customer_ids);

for my $customer (@customers) {

   # debug
   print $customer . "/n";
   print ref($customer) . "/n";
   print Dumper ($customer);

   if (!$customer) {
      print "No customer object\n";
      # do stuff
      last;
   }
   
   print "Got the customer object\n";
   $info->{customer_objects}{$customer->customer_id} = $customer;
}

99% of the time, when I run this I get customer objects back. However, I'll occasionally get the output:

HASH(0x12345)
HASH
{}
Got the customer object
Can't call method "customer_id" on unblessed reference at ..

I've tried to edit my IF statement to check for an empty hashref, but it will always ignore the IF:

if (!$customer || !%$customer)

When I test this out via command line, the empty hashref works as expected:

$ perl -E 'my $hr = {};  if (!$hr || !%$hr) { say "empty" } else { "nonempty" }'

empty

I'm not understanding what the issue is. It appears that my debug output isn't accurate, and I am actually not getting an empty hashref. Can someone please explain what is going on, and how I might figure out exactly what $customer is and how to ignore this case in my IF statement?

CodePudding user response:

You can check blessed, as you see in the comments to the question.

However, it looks like you might have some cases where you query a particular ID, but there's no record for that ID. Maybe that's not the case, so just ignore this if it's not even close to the cause.

In that case, you might get back something that's not a customer object (because there is no customer). I'd much rather push that complexity down so the application doesn't have to think that hard about it and I catch it sooner.

This work probably happens in get_customer_details.

The trick then is to figure out how to represent the customer that does not exist. One way is to simply have get_customer_details not return anything for a non-existent ID. The returned list simply does not have those entries. Then there's no problem with later method calls. However, you then have silent failures and you can get fewer items than the arguments you supplied.

Another way is to return undef in the list, then look for defined values. The undef positions would correlate to the bad customer ID in the @customer_ids, and you can handle that .

my @customers = $api->get_customer_details(\@customer_ids);

for( my $i = 0; $i < @customers; $i   ) {
    unless( defined $customers[$i] ) {
        warn "No customer for $customer_ids[$i]";
        next;
        }
    ...
    }

But I tend to favor something where there's a parallel object that acts like the null customer. That would be able to tell you that there was no customer, what the ID was, and so on. It might respond to all the normal customer methods, but warn and return undef (or die, or whatever). There might be some method to tell you which objects are real customers, like exists (or some better name):

my @customers = $api->get_customer_details(\@customer_ids);

foreach my $customer ( @customers ) {
    unless( $customers->exists ) {
        # this would be a Customer::Null or something
        # that responds to the same methods
        warn "No customer for " . $customer->id";
        next;
        }
    
    ...
    }

I think this last approach because as these objects pass through the program, they carry with them the story of their creation. These things might even store the part of the API data structure so you can see what's going on. Maybe there is a customer but whatever turns it into a Customer doesn't understand something about it.

  • Related