【问题标题】:Call a subroutine from a file that was required in a different namespace从不同命名空间中所需的文件调用子例程
【发布时间】:2014-07-06 11:17:30
【问题描述】:

我正在重构一些旧的、糟糕的 Perl 代码。在许多地方,存储在外部.pl 文件中的数据如下:

sub data{{ huge => 'datastructure', etc => '...' }};
1;

是这样导入的:

require 'file_with_data.pl';
my $data = data();

我无法更改这些数据的存储方式,但我将所有这些 require 逻辑移动到一个包中,这样任何人都可以访问这些数据。

我的问题是,我必须循序渐进地做到这一点,即在一段时间内,一些模块会使用旧的、丑陋的方式,而另一些模块会使用新的、稍微不那么丑陋的方式。

这是我的新包中的一种方法:

sub load_from_subroutine {
    no strict 'refs';
    my ($self, $file, $subroutine) = @_;
    my $data;
    try {
        if (exists $INC{$file}) {
            die "What should I do now?";  # <-- this is what I want to change
        }
        else {
            untaint $file;
            require $file;
            if (defined &$subroutine) {
                $data = &$subroutine;
            }
            else {
                die "Subroutine $subroutine is not defined";
            }
        }
    }
    catch {
        croak "Unable to load resource from file $file: $_";
    };
    return $data;
}

如果exists $INC{$file} 测试为真,我可以使用什么技巧来获得&amp;$subroutine?暗示,在这个命名空间中,defined &amp;$subroutine 是假的。

临时代码

与此同时,这是我正在使用的:

warn "Doing dirty trick in order to be able to load $file!\n";
my $tmp = '/tmp/data' . time . rand(100) . '.pl';
copy $file, $tmp;
require $tmp;
# do the loading stuff as shown...
unlink $tmp;

这真的很糟糕,我渴望更好的解决方案。

【问题讨论】:

    标签: perl namespaces include require


    【解决方案1】:

    听起来 ikegami 帮助解决了您最初的问题。

    请注意,由于您将所有这些文件导入到一个包中,因此可能会出现一些名称重叠,尤其是如果之前的编码器重用了名称 sub data。因此,我建议您为这些文件中的每一个提供自己的包并缓存结果。

    下面演示了如何做到这一点:

    package MyNamespace::DataLoader;
    
    use strict;
    use warnings;
    use autodie;
    
    use Carp;
    
    # Both of the below subs output a string, like:  sub data { "foo" }
    print load_from_subroutine(qw(data2.pl data)), "\n";
    print load_from_subroutine(qw(data3.pl data)), "\n";
    
    our %files;
    our $inc = 0;
    
    sub load_from_subroutine {
        my ($file, $subroutine) = @_;
    
        # Load the file
        if (!$files{$file}) {
            my $data = do {
                local $/;
                open my $fh, '<', $file;
                <$fh>;
            };
    
            carp "File already has a package declaration: $file" if $data =~ /^package/;
    
            my $pkg = __PACKAGE__ . '::Package' . ++$inc;
            $files{$file}{pkg} = $pkg;
    
            eval "package $pkg;\n$data\n1;";
            croak "Unable to load '$file': $@" if $@;
        }
    
        # Reference the subroutine
        if (!$files{$file}{sub}{$subroutine}) {
            my $subname = $files{$file}{pkg} . '::' . $subroutine;
    
            no strict 'refs';
            if (! defined &{"$subname"}) {
                croak "Subroutine doesn't exist: $file->$subroutine";
            }
    
            $files{$file}{sub}{$subroutine} = \&{"$subname"};
        }
    
        return $files{$file}{sub}{$subroutine}->();
    }
    

    【讨论】:

    • 在我的数据加载器类中,我实际上将数据存储在哈希中,并设置了自动加载数据的访问器if !exists $$self-&gt;{$data_name},我认为它更具可读性和更快(即使它确实如此)污染命名空间)。另一方面,您的解决方案非常适合直接替换仍留在代码其他部分的那些 require-and-load 位。
    【解决方案2】:

    它没有加载到不同的命名空间中(注意缺少package)。缺少package 也意味着您应该使用do 而不是requiredo 忽略 %INC

    【讨论】:

    • 它不是加载到require'd它的包的命名空间中吗?
    • 是的,它被加载到与需要它的代码相同的 命名空间中。
    猜你喜欢
    • 2018-09-21
    • 1970-01-01
    • 1970-01-01
    • 2019-05-11
    • 2011-08-15
    • 1970-01-01
    • 2011-12-05
    • 1970-01-01
    • 2015-08-04
    相关资源
    最近更新 更多