0

I am trying to figure out how to access an error from a list of errors (a constant list). By access I mean that I have a new() method which outputs/creates an error that is posted. My new method does not match up properly with the way my list 'code' at the minute, so I am asking what changes I would have to make so that it will be able to retrieve an error (hash) from the list like this: ASC::Builder:Error->new(code => UNABLE_TO_PING_SWITCH_ERROR, switch_ip => $switch_ip, timeout => $timeout); (if this is possible, I have been told it may not be, without taking code as an argument and then deleting it)

Here are my files below:

Error.pm

    package ASC::Builder::Error;

    use strict;
    use warnings;

    use ASC::Builder::Error::Type 'code';


    use parent 'Exporter';
    our @EXPORT_OK = qw/new/;

    sub new {
        my ($class, %args) = @_;
        my @args = keys %args;
        # Takes code in as an argument and then removes it so the error hash it self can be assigned to it
        my $self = delete $args{code};
        if (is_error($self)) {
            return $self;
        }
        # 1st argument will be error hash. Any other arguments will be context params & inserted into
        # context field of the error hash
        if (ref $self eq 'HASH' && (@args > 1)) {
            foreach my $key (@{ $self->{context} } ) {
                # And take the ones we need
                $self->{args}->{$key} = $args{$key};
            }
            my @template_args = map { $self->{args}->{$_} } @{ $self->{context} };

            # Map/Insert arguments into context hash and insert into string template
            $self->{message} = sprintf ($self->{template}, @template_args);
            return bless $self, $class;
            }
            # Supporting the old error messaage (string & parameters)
            else {
                 return bless { message => $args[0]}, $class;
            }
        }

      # Accessor for category
      sub category {
          return shift->{category};
      }

      # Accessor for message
      sub template {
          return shift->{template};
      }
      # Accessor for context
      sub context {
          return shift->{context};
      }
      # Accessor for template option
      sub tt {
          return shift->{tt}{template};
      }
      # Accessor for fatal
      sub is_fatal {
          return shift->{fatal};
      }
      # Setter for is_fatal
      sub set_is_fatal {
          my ($self, $fatal) = @_;
          $self->{fatal} = $fatal;
      }

      # Accessor for wiki_page
      sub wiki_page {
          return shift->{wiki_page};
      }
    # Accessor for args. args are a hash ref of context parameters that are
      # passed in as a list at construction
      sub args {
          return shift->{args};
      }
      # Accessor for error message which is processed inside the new() method.
      # Will return error message with context parameters inserted.
      sub message {
          return shift->{message};

      }
      # Stringifies the error to a log message (for SB dashboard), including the
      # category, message, and wiki_page.
      sub stringify {
          my ($self) = @_;
          return sprintf ("%s: %s\nMore info: %s",$self->{category}, $self->{message}, $self->{wiki_page});
      }

      # Accessor for old error message type
      sub details {
          my $self = shift;
          return $self->{details} || $self->{message};
      }
      sub code {
          return shift->{code};
      }

      # Used to deserializ from build json.
      sub recreate_from_hash {
          my($class, $hash) = @_;
          return bless $hash, $class;
      }

      # Use to check if something is out error.
      sub is_error {
          if (scalar(@_) > 1) { # Called as $class->is_error
              shift; # Get rid of class
          }
          return UNIVERSAL::isa(shift, 'ASC::Builder::Error');
      }
      1;

Type.pm (This is the list I am exporting)

package ASC::Builder::Error::Type;
  use strict;
  use warnings;
  use parent 'Exporter';

  # Export the list of errors
  our @EXPORT_OK = ('code');

  # List of error messages
  use constant code => {
      UNABLE_TO_PING_SWITCH_ERROR => {
          category => 'Connection Error',
          template => "Could not ping switch %s in %s seconds.",
          context => [ qw(switch_ip  timeout) ],
          tt => {template => 'disabled'},
          fatal => 1,
          wiki_page => 'http://w.error-fix.com/index.php/Builder/ErrorCodes/UNABLE_TO_PING_SWITCH_ERROR',
      },
  # Add errors to this library
  };
  1;

N.B.: Can include the unit test for this design if needed.

Paul Russell
  • 179
  • 10
  • If what I want to do is not possible, will someone please explain the reason why ? :) – Paul Russell Jun 01 '16 at 14:41
  • your `Type.pm` module fails to compile due to a missing `}`. Please fix. – Paul L Jun 01 '16 at 14:47
  • Woops, sorry I took out the whole list of errors just for the purpose of the question, as only one error is needed, will fix now :) – Paul Russell Jun 01 '16 at 14:53
  • I'm confused as to what your intended structure is. Did you want the constant to be `code` or did you want the constant to be `UNABLE_TO_PING_SWITCH_ERROR`? Right now, you have a constant named `code`. That constant is a reference to a hash. That hash contains one key/value pair. The key is the string `'UNABLE_TO_PING_SWITCH_ERROR'`, and the value is a reference to another hash. But in the constructor you listed, you're passing a string `'code'`, followed by what should give you a bareword warning, `UNABLE_TO_PING_SWITCH_ERROR`. Is that your intent? – Paul L Jun 01 '16 at 14:54
  • the constant list is meant to be `code`and then I want to be able to access it something like this , if possible ; 'ASC::Builder::Error->new(code => 'UNABLE_TO_PING_SWITCH_ERROR', switch_ip => $switch_ip, timeout => $timeout)` ... The new method should take in code pointing to the error first, and the rest of the parameter are context/runtime parameter that will be inserted into the error message. Will it work if I use a string instead of bareword. At the minute I can get it to work by just having an unnamed list, but I am required to name the list `code`` ... Which provides no benefit IMO – Paul Russell Jun 01 '16 at 15:08
  • "The new method shoudl take in the code pointing to the error first". But you're exporting the error list, and importing it into your second module. Why would it have to be passed in through `new()`'s parameter list? I think the problem here is you're using `code` to mean two different things. It's both a constant, equaling a reference to a hash, and it's also a string being linked to another string `'UNABLE_TO_PING_SWITCH_ERROR'` in your constructor. I will post an answer to try to clean it up with my recommendations. – Paul L Jun 01 '16 at 15:11
  • Yeah I am very confused at the minute. I want to find out a way that I can take access the constant list named `code` in this way `code => **followed by the error name**` , I'm not sure the error should be a string or if it should be a bareword? Going from what you said , it should not be a bareword. Anyway, yeah an answer would be great thanks :) – Paul Russell Jun 01 '16 at 15:16

1 Answers1

0

Don't reuse the symbol code, it's confusing the issue.

You can leave your Type.pm file as is. It defines the constant code to be a reference to a hash that has one key/value pair. The key of that hash is the name of the error you eventually want to access.

In your Error.pm module, you are importing the constant code. There is no reason to be passing that in to a constructor. You can just go ahead and use it.

Change the call to your constructor to:

ASC::Builder:Error->new(error_name => 'UNABLE_TO_PING_SWITCH_ERROR', switch_ip => $switch_ip, timeout => $timeout);

Then within your constructor, you can do:

sub new {
    my ($class, %args) = @_;
    my $error_name = $args{error_name};

    my $error_hash = code->{$error_name};

At this point, $error_hash will be a reference to the inner hash you created in Type.pm:

{
      category => 'Connection Error',
      template => "Could not ping switch %s in %s seconds.",
      context => [ qw(switch_ip  timeout) ],
      tt => {template => 'disabled'},
      fatal => 1,
      wiki_page => 'http://w.error-fix.com/index.php/Builder/ErrorCodes/UNABLE_TO_PING_SWITCH_ERROR',
}

and you can use it accordingly. I hope that helps get you in the right direction.

Paul L
  • 939
  • 4
  • 14
  • Okay so error_hash is pointing is reference to the error name.. I can understand that. Does error_name contain the first argument that is passed into the new method? then that name is found in the constant list by `code->{$error_name}` – Paul Russell Jun 01 '16 at 15:27
  • 1
    This is the answer I was looking for all along. I think haha. I've asked this question a few times on here , but couldn't get what I was looking for, maybe I was asking the question wrong, But thanks for this, I will work of this!! :) – Paul Russell Jun 01 '16 at 15:29
  • `'error_name'` is just a string. We use it as a key in the constructor call, pointing at the value `'UNABLE_TO_PING_SWITCH_ERROR'`. (I'm using the terms "key" and "value" very loosely here, because the parameter list is just that, a list, not a real hash, even though we're using the `=>` that generally indicates a hash). Within the constructor, we create the hash `%args` out of the parameter list, at which point `'error_name'` does indeed become a key that points to the value `'UNABLE_TO_PING_SWITCH_ERROR'`. That string is then used as the key to the hash that `code` references. – Paul L Jun 01 '16 at 15:32