【问题标题】:"Can't call method "category" without a package or object reference at" in perl“无法在 perl 中调用没有包或对象引用的方法“类别”
【发布时间】:2016-05-13 16:42:20
【问题描述】:

我似乎无法弄清楚这一点,我在网上找不到任何东西。所以这是我的代码:

类型.pm

use constant {
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 => 'www.error-fix.com/',
     },
};

错误.pm 新方法以stringify中显示的格式输出错误信息

# Method for creating error message
  sub new {
      my ( $class, $error, %args ) = @_;
      # Initialize error with data
      my $self = $error;
      # If the error contains context parameters... Insert parameters into string template
      if(%args) {
          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);
          $self->stringify;
          }
          return bless $self, $class;
      }
      else { return bless $self, $class; }
  }

stringify {
    my ($self) = @_;
    return sprintf("%s : %s\nMore info: %s", $self->category, $self->message, $self->wiki_page);
}

这是我收到的错误。

#   Failed test 'Return the correct message'
    #   at t/67_Error.t line 47.
    #          got: undef
    #     expected: 'Could not ping switch 192.192.0.0 in 30 seconds.'
Can't call method "category" without a package or object reference at Error.pm line 77.

第 77 行是 stringify 中的 return 语句。

如果您需要更多信息或代码,请告诉我。

尝试解决方案(到目前为止)

 sub new {
    my ( $class, $error, %args ) = @_;
    # Initialize error with data
    my $self = $error;
    # If the error contains context parameters... Insert parameters into string template
        if($self eq 'HASH' && %args) {
            bless $self, $class;
            foreach my $key (@{ $self->{context} } ) {
                 # bless $self, $class;
                 # 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);
            my $output = _stringify($self->category, $self->message, $self->wiki_page);
            bless $output, $class;
            return $output;
            }
            else { return bless $self, $class; }
   }

测试代码:t/67_Error.t

#!/usr/bin/env perl

use lib ('./t/lib');
use strict;
no strict 'refs';
use warnings;

use ASC::Builder::Error;
use ASC::Builder::Error::Type;
use ASC::Builder::Error::Type 'code';
use Test::More;
use Test::Exception;
use LWP::Simple 'head'; # Used to test if wiki link is giving a response

subtest 'Functionality of Error' => sub {

    my $example_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://www.error-fix.com',
    };
    # Correct case
    {
        my $error = ASC::Builder::Error->new( $example_error, timeout => 30, switch_ip => '192.192.0.0' );

        isa_ok ($error, 'ASC::Builder::Error');

        can_ok ($error, 'category');
        is ($error->category(), 'Connection Error', 'Return the correct category');

        can_ok ($error, 'template');
        is ($error->template(), 'Could not ping switch %s in %s seconds.', 'Return the correct category');

        can_ok ($error, 'tt');
        is ($error->tt(), 'disabled', 'Return the correct tt template');

        can_ok ($error, 'context');
        is_deeply($error->context(), ['switch_ip', 'timeout'], 'Return the correct context params');

        can_ok ($error, 'is_fatal');
        ok($error->is_fatal(), 'Return the correct value');

        can_ok ($error, 'message');
        is ($error->message(), 'Could not ping switch 192.192.0.0 in 30 seconds.', 'Return the correct message');
        can_ok ($error, 'stringify');
        is ($error->stringify(), "Connection Error : Could not ping switch 192.192.0.0 in 30 seconds.\nMore info: http://www.error-fix.com", 'stringify creates the correct message');

};

    # Too many arguments (this is okay)
    lives_ok( sub { ASC::Builder::Error->new($example_error, timeout => 1, switch_ip => 2, extra => 3 ) }, 'Creating with too many arguments lives. (allows for additional context          string to be added in the code)' );
    };

    subtest 'Correctness of Type.pm' => sub {

# These test cases contain all the errors from Type.pm
    my @test_cases = (
       {
            name => 'UNABLE_TO_PING_SWITCH_ERROR',
            args => {
                switch_ip => '192.192.0.0',
                timeout => 30,
            },
            message => 'Could not ping switch 192.192.0.0 in 30 seconds.',
        },
    );


    foreach my $t (@test_cases) {
        subtest $t->{name} => sub {
            no strict 'refs'; # Because we need to use variable to get to a constant
            ASC::Builder::Error::Type->import($t->{name});

            # Create the Error object from the test data
            # Will also fail if the name was not exported by Type.pm
            my $error;
            lives_ok( sub { $error = ASC::Builder::Error->new( &{ $t->{name} },%{ $t->{args} }) }, 'Error can be created');

            # See if it has the right values
            is ($error->message, $t->{message}, 'Error message is correct');

            # Using LWP::Simple to check if the wiki page link is not broken
            #ok head($error->wiki_page); #CANT'T GET THIS TEST TO WORK

        }
    }
};
done_testing;

更新:消息方法:

它只是一个吸气剂。

 sub message {
        return shift->{message};
 }

【问题讨论】:

  • 你为什么在你的构造函数中调用stringify
  • 请显示触发此问题的实际测试代码。
  • ok 将添加测试代码。
  • 所以看起来好像是对message() 的调用导致了问题。你能告诉我们那个潜艇吗?
  • @stevieb 添加了消息存根

标签: perl object methods


【解决方案1】:

简而言之:(1) stringify 前面缺少关键字 sub (2) 一旦解决了这个问题,我们就会看到一个未受祝福的 $self 尝试调用方法 (3) 最后,没有方法category


(1) 缺少 sub 关键字会触发引发诊断的行为。 stringify { ... } 被简单地称为类方法,正式传递一个 hashref。当然,该 hashref 没有任何问题,我们得到了早期的错误,将其识别为伪造的。来自perldiag

不能在没有包或对象引用的情况下调用方法“%s”
(F) 您使用了方法调用的语法,但对象引用或包名填充的槽包含一个表达式,该表达式返回既不是对象引用也不是包名的定义值。这样的事情会重现错误:

$BADREF = 42;
process $BADREF 1,2,3;
$BADREF->process(1,2,3);

调用$self->stringify 与它无关,并且永远不会执行,因为这是一个致命错误。 (在 v5.10 下,确切的错误消息对我来说是不同的)

(2) 当sub 关键字被放在它的位置时,我们遇到了问题:$self 在被bless 编辑之前调用了一个方法。 ThisSuitIsBlackNot 在评论中也观察到了这一点。现在无需猜测为什么它甚至可以工作,但我们得到了预期的错误

不能在 ErrorPack.pm 的 unblessed 引用上调用方法“stringify”...

(3) 当我们在调用方法之前首先bless hashref,我们得到

在 ErrorPack.pm 上无法通过包“ErrorPack”定位对象方法“category”...

正如预期的那样,因为$self 现在是一个对象,并且确实没有方法“类别”。这可以通过更改 printf 中的调用来解决,以便取消对 hashref 的引用,而不是调用方法(通过添加 curlies)。 bipll 的回答中也提到了。

我不知道这些变化如何与海报的意图一起发挥作用,但与所有这些有关

文件 ErrorPack.pm

package ErrorPack;

use warnings;
use strict;

# Method for creating error message
sub new {
    my ( $class, $error, %args ) = @_; 
    # Initialize error with data
    my $self = $error;
    bless $self, $class;
    # If the error contains context parameters... [...]
    if (%args) {
        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);
        $self->stringify;
    }   
    return $self;
}

sub stringify {
    my ($self) = @_; 
    return sprintf("%s : %s\nMore info: %s", $self->{category}, 
        $self->{message}, $self->{wiki_page});
}
sub prn { print "$_[0]->{category}\n" }
1;

如果有任何理由%args 处理之前将$self 设为对象,请在调用stringify 之前发出bless $self, $class; 然后return $self;

带有 ma​​in::

的脚本
use warnings;
use strict;

use ErrorPack;

# Taken from testing code that is now posted
my $err = {
    category => 'Connection Error',
    template => "Could not ping switch %s in %s seconds.",
    context => [ qw(switch_ip  timeout) ],
    tt => {template => 'disabled'},
    fatal => 1,
    wiki_page => 'www.error-fix.com/',
};  

my $eobj = ErrorPack->new($err, timeout => 30, switch_ip => '192.192.0.0');
$eobj->prn();

这会打印一行Connection Error$eobj可以直接查询key等。

【讨论】:

  • 是否有可能让新方法(在调用时)打印出值而不是显式调用 prn() ?
  • 那是 stringify 的错字。对不起
  • @PaulRussell 是的,您可以随意从包文件中打印。因此,您可以将打印语句添加到new,并且它们将在调用new 时运行,因此无论何时创建对象。请记住,在bless 语句执行之前(在new 中)$self 还不是对象,因此您不能在其上调用方法。不过,您可以从中提取数据,只需取消其键的引用即可。 (我添加了一个单独的玩具方法,只是为了证明这一切都有效。虽然您可能希望实际拥有一个打印对象所有数据的方法,就这样。)
  • @PaulRussell 是的,我想这是一个错字之类的。有趣的是,触发的特定错误(由于此类代码的解释方式)混淆了问题。
【解决方案2】:
return sprintf("%s : %s\nMore info: %s", $self->category, $self->message, $self->wiki_page);

这里的代码看起来像是在$self 上调用了名为categorymessagewiki_page 的方法。如果它们实际上是哈希键,则应将它们放在大括号中。

【讨论】:

  • 它们可能应该作为构造函数之外的方法调用以保持封装(尽管尚不清楚 OP 是否实际实现了任何方法)。发生 OP 的错误是因为 $self 是一个 unblessed hashref,而不是一个对象,当他们调用 $self->stringify 时。
  • @ThisSuitIsBlackNot 它是如何被调用的呢?
  • @ThisSuitIsBlackNot,最上面的代码sn-ps的常量哈希包含categorywiki_page作为键,所以我想...
  • @hobbs 哎呀,好电话。他们不能打电话给new,因为这会产生(不同的)致命错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多