Perl模块加载 - 防止:也许你忘了加载“Bla”?

时间:2011-11-07 00:01:20

标签: perl qa perl-module

当您运行perl -e "Bla->new"时,您会收到以下众所周知的错误:

Can't locate object method "new" via package "Bla"
(perhaps you forgot to load "Bla"?)

由于我的监督,前几天发生在Perl服务器进程中。有多个脚本,其中大多数都有适当的use语句。但是有一个脚本在{123}中的Bla->new处进行了sub blub但在顶部缺少use Bla,当点击时没有任何其他脚本使用{ {1}}之前由服务器进程加载,然后爆炸!

单独测试脚本将是防止这一特定错误的明显方法,但唉,代码依赖于一个巨大的环境。你知道另一种防止这种疏忽的方法吗?

更新

以下是一个示例Bla(尽管其优点)在Perl视图中的限制:

PPI

设置标题并指定use strict; use HTTP::Request::Common; my $req = GET 'http://www.example.com'; $req->headers->push_header( Bla => time ); my $au=Auweia->new; __END__ PPI::Token::Symbol '$req' PPI::Token::Operator '->' PPI::Token::Word 'headers' PPI::Token::Operator '->' PPI::Token::Word 'push_header' PPI::Token::Symbol '$au' PPI::Token::Operator '=' PPI::Token::Word 'Auweia' PPI::Token::Operator '->' PPI::Token::Word 'new' 解析相同的内容。所以我不确定你如何在这样一个摇摇欲坠的基础上建立起来。我认为问题是Auweia->new也可能是一个子程序; Auweia直到运行时才能说出来。

进一步更新

好的,从@ Schwern下面的指导性评论中我了解到perl.exe只是一个标记器,如果您接受其限制,可以构建它。

2 个答案:

答案 0 :(得分:10)

测试是唯一值得付出努力的答案。如果代码包含忘记加载类的错误,则可能包含其他错误。无论遇到什么障碍,都要考验一下。否则你正在修补筛子。

那就是说,你有两种选择。您可以使用Class::Autouse,如果模块尚未加载,它将尝试加载模块。它很方便,但因为它会影响整个过程,所以会产生意想不到的效果。

或者您可以使用PPI扫描代码并查找所有类方法调用。 PPI::Dumper非常方便了解PPI如何看待Perl。

use strict;
use warnings;

use PPI;
use PPI::Dumper;

my $file = shift;
my $doc = PPI::Document->new($file);

# How PPI sees a class method call.
#    PPI::Token::Word      'Class'
#    PPI::Token::Operator  '->'
#    PPI::Token::Word      'method'
$doc->find( sub {
    my($node, $class) = @_;

    # First we want a word
    return 0 unless $class->isa("PPI::Token::Word");

    # It's not a class, it's actually a method call.
    return 0 if $class->method_call;

    my $class_name = $class->literal;

    # Next to it is a -> operator
    my $op = $class->snext_sibling or return 0;
    return 0 unless $op->isa("PPI::Token::Operator") and $op->content eq '->';

    # And then another word which PPI identifies as a method call.
    my $method = $op->snext_sibling or return 0;
    return 0 unless $method->isa("PPI::Token::Word") and $method->method_call;

    my $method_name = $method->literal;

    printf "$class->$method_name seen at %s line %d.\n", $file, $class->line_number;
});

答案 1 :(得分:0)

您没有说明您正在运行的服务器环境,但是从您所说的内容来看,您可以在执行任何单个页面之前预先加载所有模块。这不仅可以防止您描述的问题(每个脚本都必须记住加载它使用的所有模块),但它也可以节省您的内存。

在pre-forking服务器(通常与mod_perl和Apache一起使用)中,你真的想在第一次你的服务器之前加载尽可能多的代码,以便代码存储一次在copy-on-write共享内存中,而不是在每个子进程按需加载时多次。

有关在Apache中预加载的信息,请参阅Practical mod_perl

部分