perlhashconstantssubroutinebless

I am trying to modify a method that returns an error string so that it can accept a hash in Perl


Here is the current method:

sub new {
    my ($package, $message) = (shift, shift);
    my %params = @_;
    return bless { message => $message, %params }, $package;
}

This method returns basically an error string , but I want to modify it so it will also be able to take my new error structure and return the string from the error hash. I can't get my head around how to use bless. The %params accepts runtime parameter but they can be ignored for now I would think.

Here is the error structure:

 # this constant is an example of an error library (just included 1 error for example)
    use constant {

        CABLING_ERROR => {
        errorCode => 561,
        message => "Cabling incorrect to device in server A4r, contact DCT ",
        tt => { template => 'cable'},
        fatal => 1,
        link =>'http://www.error-fix.com/cabling
        },
};

I just started throwing down some code to start , it's a poor attempt but this is how I began modifying the new() method:

sub new {
    my ($package, $message) = (shift, shift);
    # defining new error hash
    my $hash($errorCode, $message, $tt, $fatal, $link) = (shift, shift, shift, shift);
    my %params = @_;

    if(exists $hash->{errorCode}) {
    return bless { errorCode => $errorCode, message => $message, tt => $tt, fatal => $fatal, link => $link}, %params;
}   
    else {
        return bless { message => $message, %params }, $package;
}
}

My understanding of bless is that it makes object of hash refs. The errors are held in the constant list. Here is an example of how it is to be executed:

if(!device_model) {
    die  ARC::Builder::Error->new(CABLING_ERROR);

}

UPDATE: I have been trying to unit test your solution @simbabque but I keep getting an empty string as a return value instead of the error message string. Maybe it's my test that it isn't set up correctly? Below is an example of the test I created:

my $error = CABLING_ERROR;

my $exp_out_A = ($error->{message});

my $error_in = new($error);

diag($error_in);

is($error_in, $exp_out_A, 'Correct message output');

Solution

  • What you actually want is your constructor to distinguish between if it was called with a message string, or with a hash of a known form that contains the message string. Basically you want to coerce the message attribute.

    sub new {
      my ($package, $thing) = (shift, shift);
      my %params = @_;
    
      if (ref $thing eq 'HASH') {
        return bless { message => $thing->{message}, %params }, $package;
      }
    
      return bless { message => $thing, %params }, $package;
    }
    

    That's it. Just check the ref of the param you are getting. If it's a hash reference, it will be 'HASH'. If it's a simple string, it will be undef.

    In case you want to build up more %params automatically from your hash reference, that would look something like this.

    return bless { message => $thing->{message}, foo => $thing->{foo}, %params }, $package;