将动态代码添加到 Perl 应用程序的最佳方法

2023-12-22

我知道这个问题的具体实例之前已经得到回答:

  • 如何在不使用 eval 的情况下动态包含 Perl 模块? https://stackoverflow.com/questions/1917261/how-can-i-dynamically-include-perl-modules-without-using-eval
  • 如何使用仅在运行时已知的 Perl 包? https://stackoverflow.com/questions/442710/how-do-i-use-a-perl-package-known-only-in-runtime

Perl Monks 也有很好的答案:

  • 编写动态加载其他模块的 Perl 模块 http://www.perlmonks.org/?node_id=1021324.
  • 即时创建子例程 http://www.perlmonks.org/?node_id=431623

但我想要一种强大的方法来向 Perl 应用程序添加功能,即:

  1. 高效的:如果不需要代码,则不应编译。
  2. 易于调试:如果动态代码出现问题,则错误报告,应该指向动态代码的正确位置。
  3. 易于扩展:添加新代码应该像添加新文件或目录+文件一样简单。
  4. 易于调用:主应用程序应该能够使用“附加组件”而不会有太多麻烦。一个有效的机制来检查“附加”是否已经加载,如果没有加载,将是一个优点。

为了说明这一点,以下是一些可以从良好解决方案中受益的示例:

  • 一组从不同应用程序移动数据的脚本。例如,将数据从 OpenCart 移动到 Prestashop,其中每个entity数据模型中有一个特定的“附加组件”来处理输入或输出;然后中间数据模型负责数据的转换。这可用于在任何方向上甚至在同一电子商务的不同版本之间移动数据。

  • 需要在不同位置呈现不同类型 HTML 的 Web 应用程序。每个“模块”都知道如何处理特定信息并接受参数来执行此操作。一个模块输出 HTML,另一个模块输出文档列表,另一个模块输出文档,另一个模块输出横幅,等等。

以下是我使用过并且有效的一些示例。

在运行时加载函数并输出可能的编译错误:

eval `cat $file_with_function`;
if( $@ ) {
  print STDERR $@, "\n";
  die "Errors at file $file_with_function\n";
}

或者更稳健地使用File::Slurp:

eval read_file("$file_with_function", binmode => ':utf8');

检查某个函数是否已定义:

if( !defined &myfunction ) {
  die "myfunction is not defined\n";
}

可以从那里调用该函数。这对于一种功能来说没问题,但对于很多功能来说就不行了。

如果将函数放入模块中:

require $file_with_function; # needs the ".pm" extension, i.e. addon/func.pm
$name_of_module->import();   # need to know the module name, i.e. Addon::Func

$name_of_module->myfunction(...);

哪里的require可能会受到保护内部eval然后使用$@像之前一样。

With 模块::加载 http://search.cpan.org/~bingos/Module-Load-0.32/lib/Module/Load.pm:

load $name_of_module;

随后是import并以同样的方式使用。安全不应该是一个问题因为可以假设动态代码来自受信任的地方。还有更好的方法吗?会考虑哪种方式好的做法?

如果它有帮助,我将在以下范围内使用该解决方案(以及其他地方,但不限于此)Dancer http://perldancer.org/框架。

EDIT:鉴于评论,我添加更多信息。我想到的所有案例都有一个共同点:

  1. 有不止一段动态代码。可能有很多开始。
  2. 每一位代码都有相同的界面.

鉴于评论和缺乏回应,我做了一些研究来回答我自己的问题。欢迎评论或其他答案!

动态代码

动态代码是指在以下位置评估的代码run-time。一般来说,我认为最好编译应用程序,以便在开始执行之前进行 Perl 编译器可以提供的所有错误检查。添加到use strict and use warnings,这样你就可以发现许多常见的错误。那么为什么要使用动态代码呢?我认为的原因有以下几点:

  1. 应用程序执行许多不同的操作,这些操作是根据执行上下文选择的。例如,应用程序从文件中提取某些属性。提取它们的方式取决于文件类型,我们想要处理许多文件类型,但我们不想为添加的每个新文件类型更改应用程序。我们还希望应用程序能够快速启动。
  2. 应用程序需要扩展在飞行中以不需要重新启动应用程序的方式。
  3. 我们有一个包含许多功能的大型应用程序。当我们部署应用程序时,我们不想一直提供所有可能的功能,也许是因为我们单独许可它们,也许是因为并非所有功能都能够在所有平台上运行。通过仅添加具有我们想要的功能的文件,我们可以得到分配不需要更改任何代码或config files.

我们该怎么做呢?

鉴于 Perl 提供的可能性,添加动态代码的解决方案有两种:eval并使用require。然后还有modules这可能有助于以更简单或更易于维护的方式做事。

快速而肮脏的方法

The eval https://perldoc.perl.org/functions/eval.html方式使用形式eval EXPR编译一段 Perl 代码run-time。该表达式可以是一个字符串,但我建议将代码放入一个文件中,并将其他类似的文件分组到一个方便的位置。然后,如果可能的话使用文件::啜饮 http://search.cpan.org/~uri/File-Slurp-9999.19/lib/File/Slurp.pm:

eval read_file("$file_with_code", binmode => ':utf8');
if( $@ ) {
  die "$file_with_code: error $@\n";
}
if( !defined &myfunction ) {
  die "myfunction is not defined at $file_with_code\n";
}

指定字符集为read_file确保该文件将被正确解释。检查编译是否正确以及我们期望的函数是否已定义也很好。所以在$file_with_code, 我们将有:

sub myfunction(...) {
  # Do whatever; maybe return something
}

然后就可以正常调用该函数了。该函数将根据加载的文件而有所不同。简单而动态。

模块化方式(推荐)

考虑到可维护性,我的做法是使用require https://perldoc.perl.org/functions/require.html。不像use,即评估于编译时, require可用于加载模块run-time。从各种调用方式中require,我会选择:

my $mymodule = 'MyCompany::MyModule'; # The module name ends up in $mymodule
require $mymodule;

也不像use, require将加载模块但不会执行import。因此,我们可以使用模块内的任何函数,并且这些函数名称不会污染调用名称空间。要访问该功能,我们需要使用:

$mymodule->myfunction($a, $b);

请参阅下文了解参数如何传递。这种调用函数的方式会在前面添加一个参数$a and $b通常被命名为$self。如果您对面向对象一无所知,您可以忽略它。

As require将尝试加载模块,该模块可能不存在或可能无法编译,要捕获错误,最好使用:

eval "require $mymodule";

Then $@可用于检查加载+编译过程中的错误。我们还可以检查该函数是否已定义为:

if( $mymodule->can('myfunction') ) {
  die "myfunction is not defined at module $mymodule\n";
}

在这种情况下,我们需要为模块创建一个目录和一个带有以下内容的文件.pm每一项的扩展名:

MyCompany
  MyModule.pm

Inside MyModule.pm我们将有:

package MyCompany::MyModule;

sub myfunction {
  my ($self, $a, $b);

  # Do whatever; maybe return something
  # $self will be 'MyCompany::MyModule'
}

1;

The package https://perldoc.perl.org/functions/package.htmlbit 是必不可少的,它将确保我们放入的任何定义都会出现在MyCompany::MyModule命名空间。这1;最后会告诉require模块初始化正确。

如果我们想使用其他可能污染调用者命名空间的库来实现该模块,我们可以使用命名空间::干净 http://search.cpan.org/~ribasushi/namespace-clean-0.27/lib/namespace/clean.pm模块。该模块将确保调用者不会从我们定义的模块中获得任何对命名空间的添加。它的使用方式如下:

package MyCompany::MyModule;

# Definitions by these modules will not be available to the code doing the require
use Library1 qw(def1 def2);
use Library2 qw(def3 def4);
...

# Private functions go here and will not be visible from the code doing the require
sub private_function1 {
  ...
}
...

use namespace::clean;

# myfunction will be available
sub myfunction {
  # Do whatever; maybe return something
}
...

1;

如果我们多次包含一个模块会发生什么?

简短的答案是nothing。 Perl 使用以下命令跟踪已加载的模块以及从何处加载%INC多变的。两个都use and require不会加载库两次。use会将所有导出的名称添加到调用者名称空间中。require也不会那样做。如果您想检查模块是否已加载,您可以使用%INC或者更好的是,你可以使用模块::已加载 https://perldoc.perl.org/Module/Loaded.html这是现代 Perl 版本核心的一部分:

use Module::Loaded;

if( !is_loaded( $mymodule ) {
  eval "require $mymodule" );
  ...
}

如何确保 Perl 找到我的模块文件?

For use and requirePerl 使用@INC变量来定义将用于查找库的目录列表。向其中添加新目录可以通过将其添加到PERL5LIB环境变量或使用:

use lib '/the/path/to/my/libs';

辅助库

我发现了一些库,可以用来使使用动态机制的代码更易于维护。他们是:

  • The if http://search.cpan.org/~rjbs/if-0.0606/if.pmmodule:根据条件加载或不加载模块:use if CONDITION, MODULE => ARGUMENTS;。也可用于卸载模块。
  • 模块::加载::有条件 http://search.cpan.org/dist/Module-Load-Conditional/lib/Module/Load/Conditional.pm:在尝试加载模块时不会死掉,也可用于检查模块版本或其依赖项。它还能够一次加载所有模块列表,甚至在加载之前检查它们的版本。

摘自 Module::Load::Conditional 文档:

use Module::Load::Conditional qw(can_load);

my $use_list = {
        CPANPLUS        => 0.05,
        LWP             => 5.60,
        'Test::More'    => undef,
};

print can_load( modules => $use_list )
        ? 'all modules loaded successfully'
        : 'failed to load required modules';
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

将动态代码添加到 Perl 应用程序的最佳方法 的相关文章

  • 缓存施瓦茨变换

    我正在学习 中级 Perl 它非常酷 我刚刚读完 施瓦茨变换 部分 在理解它之后 我开始想知道为什么变换不使用缓存 在具有多个重复值的列表中 转换会重新计算每个值的值 因此我想为什么不使用哈希来缓存结果 这是一些代码 a place to
  • 在 Perl 中查找标量变量的数据类型

    我有一个接受用户输入的函数 输入可以是整数 浮点数或字符串 我有三个重载函数 应该根据输入数据的数据类型调用它们 例如 如果用户输入一个整数 比如100 则应该调用具有整数参数的函数 如果用户输入字符串 例如 100 则应调用具有字符串参数
  • 如何在 Perl 中的不同包之间共享全局值?

    是否有一种标准方法可以对模块进行编码以保存要包含在每个其他包中的全局应用程序参数 例如 use Config 一个简单的包 只包含our变量 只读变量怎么样 已经有一个标准配置模块 http perldoc perl org Config
  • 哪些字符可以用作正则表达式分隔符?

    哪些字符可以用作 Perl 正则表达式的分隔符 m re m re and m re 一切似乎都有效 但我想知道所有可能性 From perlop http perldoc perl org perlop html 通过 m 您可以使用任意
  • api网关CORS设置

    我正在尝试在部署脚本中使用 aws cli 从命令行设置 aws CORS 我使用以下 perl to shell 命令创建了 POST 资源 我正在尝试将集成响应设置为 就像启用核心一样 aws apigateway put method
  • 无法在 Mac 上安装 DBD::mysql

    第一次发帖 格式可能不太对 请见谅 我一直在尝试使用 cpan 在 macOS Catalina 上安装 DBD mysql 但尚未成功 我使用了perlbrew 并尝试了mysql和mariaDB 但仍然没有成功 另外 我在网上研究了不同
  • 使用 unix ksh shell 脚本或 perl 脚本监视文件夹中的新文件并触发 perl 脚本

    我已经在谷歌搜索和溢出了一段时间 但找不到任何可用的东西 我需要一个脚本来监视公共文件夹并在创建新文件时触发 然后将文件移动到私有位置 我有一个 samba 共享文件夹 exam ple 在 UNIX 上映射到X 在窗户上 在某些操作中 t
  • 有没有对数字(千)进行分组的函数?

    小 模块中是否隐藏着一个函数 它为我执行此操作 my var 23654325432 var reverse var var s d 3 K d g var reverse var I like 数字 格式 http search cpan
  • Perl LWP::简单 HTTPS 错误

    我正在尝试获取网站的内容并打印 该代码按照我希望的方式在常规 HTTP 网站上运行 但它不适用于 HTTPS 我已经查找了此问题的修复程序 但它们在我的程序中不起作用 这是我目前拥有的代码 usr bin perl use strict u
  • 真实设备中的 Android strace

    我有以下情况 我想监控Android手机上的系统调用 所以 我编写了一个脚本来做到这一点 使用 Android 模拟器可以完美地工作 将应用程序的痕迹写入我的 Ubuntu 上的特定文件中 问题是当我连接一个真实的手机来分析它时 它在结果文
  • Perl Moose TypeDecorator 错误。我该如何调试?

    我最近遇到了一个问题 非常感谢您的见解 我在圣诞节前在 PerlMonks 上发布了类似的问题 并提供了一些从 MooseX Declare 切换的反馈 http www perlmonks org node id 877703 1 我现在
  • Perl:LWP::UserAgent 对于重定向 URL 始终返回代码 200

    我有一个简单的 url 它执行 302 临时错误 移至另一页 我尝试在 URL 返回代码 200 表示 OK 时检索它 并在返回 200 以外的其他内容时停止 My code my ua LWP UserAgent gt new env p
  • 如何从 R 运行带有特定模块的 perl 脚本?

    我可以从终端运行 perl 脚本 myperlscript pl 没有任何问题 但是 如果我尝试从 RStudio 中运行相同的 perl 脚本 则会出现以下错误 command lt myperlscript pl outputfile
  • Path::Class::File 或 ::Dir & Moose 初始化和强制

    目前有 package Local use warnings use Moose use Method Signatures Simple use Path Class File use Path Class Dir method buil
  • unix df 上的正则表达式帮助

    我需要一些帮助来调整我的代码以查找此 UNIX 中的另一个属性df output Ex Filesystem Size Used Avail Capacity Mounted on dev ad4s1e 61G 46G 9 7G 83 ho
  • 为什么我只得到第一个捕获组?

    https stackoverflow com a 2304626 6607497 https stackoverflow com a 2304626 6607497 and https stackoverflow com a 370042
  • 在perl中调用基本构造函数

    从 Perl 中的类构造函数调用基本构造函数的正确方法是什么 我见过这样的语法 my class shift my a shift my b shift my self class gt SUPER new a b return self
  • 使用包管理器时如何管理 Perl 模块?

    A 最近的问题 https stackoverflow com questions 397817 unable to find perl modules in intrepid ibex ubuntu这让我开始思考 在我尝试过的大多数 Li
  • 是否有理由在 Perl 中使用 open(...) 的双参数形式?

    是否有任何理由使用双参数形式open 在 Perl 中而不是三个或更多参数的版本 我能想到的唯一原因是明显的观察结果 即两个参数的形式更短 但是假设冗长不是问题 是否有任何其他原因会让您选择双参数形式open 一参数和二参数 open 应用
  • 在 Perl 中如何用空格填充字符串的一部分?

    你更喜欢哪个版本 usr bin env perl use warnings use strict use 5 010 my p 7 33 my prompt my key very important text my value Hell

随机推荐