oli*_*erg 1 xml perl module libxml2 xml-libxml
我基本上是模块的新手,我试图在我的脚本中使用它们.我无法找到正确使用它们的正确方法,我想了解你的建议.
让我快速解释一下我要做的事情:
我的脚本正在根据XML文件中的数据进行一些文件传输.
所以基本上,我有XML文件,内容如下:
<fftg>
<actions>
<!-- Rename file(s) -->
<rename>
<mandatory>0</mandatory>
<file name="foo" to="bar" />
</rename>
<!-- Transfer file(s) -->
<transfer>
<mandatory>0</mandatory>
<protocol>SFTP</protocol>
<server>fqdn</server>
<port>22</port>
<file name="bar" remotefolder="toto" />
</transfer>
<!-- Transfer file(s) -->
<transfer>
<mandatory>0</mandatory>
<protocol>SFTP</protocol>
<server>fqdn</server>
<port>22</port>
<file name="blabla" remotefolder="xxxx" />
<file name="blabla2" remotefolder="xxxx" />
</transfer>
</actions>
</fftg>
Run Code Online (Sandbox Code Playgroud)
简而言之,我有一个执行"动作"的脚本.每个动作都可以重复X次.
现在,而不是一个重要的脚本与一堆子程序等..我认为为我的应用程序创建模块应该更好,并将操作放在模块中.
例如 :
FFTG::Rename
FFTG::Transfer
FFTG::Transfer::SFTP
FFTG::Transfer::FTP
Run Code Online (Sandbox Code Playgroud)
等等(我创建了所有这些模块,它们可以独立工作)
并根据XML文件中指定的操作调用这些模块.如果需要,人们可以创建新的模块/操作(我希望模块化).
现在,我不知道如何正确地做到这一点.
所以我的问题是:请问最好的方法是什么?
目前,我的脚本正在阅读这些动作:
# Load XML file
my $parser = XML::LibXML->new();
my $doc = $parser->parse_file($FFTG_TSF . "/" . $tid . ".xml");
# Browse XML file
foreach my $transfer ($doc->findnodes('/fftg')) {
# Grab generic information
my($env) = $transfer->findnodes('./environment');
my($desc) = $transfer->findnodes('./description');
my($user) = $transfer->findnodes('./user');
print $env->to_literal, "\n";
# Browse Actions
foreach my $action ($doc->findnodes('/fftg/actions/*')) {
my $actiontype = ucfirst($action->nodeName());
# how do i select a module from the $actiontype here ? ($actiontype = Rename or Transfer)
# i can't do : use FFTG::$actiontype::execaction(); or something for example, it doesnt work
# and is it the right way of doing it ?
}
}
Run Code Online (Sandbox Code Playgroud)
但也许这不是正确的思考方式.(我正在使用Lib :: LibXML)我如何"动态"调用模块(使用名称中的变量,例如FFTG :: $ actiontype,并且,这是否意味着我必须具有相同的子例程在每个模块中?例如:子执行
因为我想向模块发送不同的数据......
任何提示?再次感谢,
首先,您需要提供一个清晰的界面.每个模块都需要具有相同的结构.它是否是OOP并不重要,但它们都需要暴露相同的接口.
这是一个非-OOp实现的示例FFTG::Rename.我遗漏了很多东西,但我觉得很清楚发生了什么.
package FFTG::Rename;
use strict;
use warnings;
sub run {
my ($args) = @_;
if ($args->{mandatory}) {
# do stuff here
}
# checks args...
# do sanity checks...
return unless -f $args->{file}->{name}; # or whatever...
rename $args->{file}->{name}, $args->{file}->{to} or die $!;
return; # maybe return someting meaningful?
}
Run Code Online (Sandbox Code Playgroud)
现在让我们假设我们有一堆这些.我们如何加载它们?有几种方法可以做到这一点.我省略了将参数引入run函数的部分.你需要从XML中获取内容并以与所有这些函数相同的方式传递它,但我认为这与问题无关.
最明显的是手动加载脚本中的所有内容.
#!/usr/bin/env perl
use strict;
use warnings;
use XML::LibXML;
# load FFTG modules
use FFTG::Rename;
# ...
Run Code Online (Sandbox Code Playgroud)
加载后,您可以调用该功能.该exist关键字很方便,因为它也可用于检查函数是否存在.
foreach my $action ( $doc->findnodes('/fftg/actions/*') ) {
my $actiontype = ucfirst( $action->nodeName );
no strict 'refs';
if ( exists &{"FFTG::${actiontype}::run"} ) {
&{"FFTG::${actiontype}::run"}->( $parsed_node_information );
} else {
# this module was not loaded
}
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,非OO方法需要no strict 'refs',这不是很好.以面向对象的方式做这件事可能会更好.但我会坚持这个答案.
这种方式的明显缺点是您需要始终加载所有模块,并且每当创建新模块时,都需要添加它.这是最简单的方式,但也具有最高的维护.
另一种方法是使用自动加载和定义允许的操作的查找表.如果您希望程序只按需加载模块,因为您知道在每次调用时都不需要所有模块,但是您也希望控制加载的内容,这是有道理的.
加载可以外包给Module :: Runtime,而不是加载所有这些.
use Module::Runtime 'require_module';
use Try::Tiny;
my %modules = (
'rename' => 'FFTG::Rename',
# ...
);
foreach my $action ( $doc->findnodes('/fftg/actions/*') ) {
try {
no strict 'refs';
require_module $modules{$action};
&{"FFTG::${actiontype}::run"}->($parsed_node_information);
}
catch {
# something went wrong
# maybe the module does not exist or it's not listed in the lookup table
warn $_;
};
}
Run Code Online (Sandbox Code Playgroud)
我还添加了Try :: Tiny来处理错误.它可以让你控制出错的地方做什么.
这种方法可以让你控制允许的动作,如果你是偏执狂,那就很好.但它仍然需要您维护脚本并将新模块添加到%modules查找表中.
第三种,最通用的方法是使用Module :: Runtime动态加载内容而不使用查找表.
use Module::Runtime 'require_module';
use Try::Tiny;
foreach my $action ( $doc->findnodes('/fftg/actions/*') ) {
try {
my $actiontype = ucfirst($action->nodeName);
require_module "FFTG::${actiontype}";
no strict 'refs';
&{"FFTG::${actiontype}::run"}->($parsed_node_information);
}
catch {
# something went wrong
# the module does not exist
};
}
Run Code Online (Sandbox Code Playgroud)
这是维护最少的,但它更危险.你不知道有什么数据进入,现在没有健全性检查.我想不出一种方法来利用我的头脑,但可能有一个.但是,现在不需要编辑脚本并保持模块列表是最新的.
我可能会采用第二种方法.它可以让你控制并保持动态.我不会选择我使用过的非OOP方法.
你可以保持非OOP并仍然no strict 'refs'通过使用->对象表示法来调用类方法.然后你的包装看起来像这样.
package FFTG::Rename;
use strict;
use warnings;
sub run {
my (undef, $args) = @_;
# ...
}
Run Code Online (Sandbox Code Playgroud)
该undef是不能捕捉到$class(没有$self),因为我们并不需要它.或许我们这样做,用于记录.这取决于.但是有了这个,你可以基本上为查找表解决方案调用类方法.
require_module $modules{$action};
$modules{$action}->run($parsed_node_information);
Run Code Online (Sandbox Code Playgroud)
这显然更清晰,更可取.
| 归档时间: |
|
| 查看次数: |
132 次 |
| 最近记录: |