在Perl发行版中包含数据的3种方法

作为模块作者,能够在Perl发行版中包含数据非常有用。数据可以用于配置和编写数据驱动测试等。以下是包含Perl发行版中数据的三种方法。
编辑: 文章于2014年2月9日更新,包括ExtUtils::MakeMaker解决方案选项3。
使用 __DATA__
“__DATA__”是一个Perl关键字,表示文件中代码的结束。该标记之后出现的任何文本将在运行时自动读取到DATA文件句柄中。例如,让我们在Perl测试文件中包含过去十年Perl TIOBE统计数据的YAML数据
use strict;
use warnings;
use YAML::XS;
use Test::More;
my $yaml = do { local $/; <main::DATA> };
my $data = Load $yaml;
do { ... };
done_testing();
__DATA__
---
2014: 0.917
2013: 2.264
2012: 2.768
2011: 2.857
2010: 3.562
2009: 4.303
2008: 5.247
2007: 6.237
2006: 7.045
2005: 8.861
这里我们使用do块将main::DATA文件句柄拖入$yaml。然后我们使用YAML::XS的“Load”函数将$yaml解码成存储在$data中的Perl数据结构。从这里我们可以自由地使用这些数据在我们的测试中。
关于__DATA__方法的优点是它简单、编码速度快、跨平台功能强大,你永远不会在查找数据时遇到麻烦(与外部文件不同)。__DATA__的缺点是它强制你将数据包含在与代码相同的文件中。如果你有大量数据怎么办?每次模块被使用时,数据都会增加使用该模块的负担,无论这些数据是否真的被使用。此外,__DATA__的内容通常是固定的——只有开发者才能覆盖它。
使用FindBin定位数据文件
FindBin是一个小巧而神奇的模块,它是Perl的核心模块之一,提供了“Bin”函数,该函数返回当前文件目录的绝对路径。这里的模式是在与Perl文件相同的目录中包含一个数据文件,并使用FindBin的Bin函数引用该数据文件。让我们看一个例子
首先,我们有Tiobe Perl YAML数据,保存在文件perl_tiobe.yaml中
---
2014: 0.917
2013: 2.264
2012: 2.768
2011: 2.857
2010: 3.562
2009: 4.303
2008: 5.247
2007: 6.237
2006: 7.045
2005: 8.861
接下来,我们在修改后的测试脚本中引用该文件
use strict;
use warnings;
use YAML::XS;
use Test::More;
use FindBin;
open (my $DATA, '<', "$FindBin::Bin/perl_tiobe.yaml") or die $!;
my $yaml = do { local $/; <$DATA> };
my $data = Load $yaml;
do { ... };
done_testing();
让我们回顾一下与上一个版本相比,此脚本中发生了哪些变化。首先,我们导入Findbin。然后,我们打开一个名为$DATA的文件句柄,该句柄指向FindBin::Bin返回的当前目录加上数据文件名。
如果可以保证数据文件与代码文件在同一位置,FindBin模式效果很好。这使得它非常适合测试文件,因为(按照惯例)它们总是在t目录中,并且不会被作为模块安装的一部分复制到其他地方。您可以在与Perl应用程序一起分发数据文件时使用此模式(例如,在Makefile中,在EXE_FILES指令中同时包含二进制文件和数据文件)。但是,这也意味着数据文件将被复制到目标bin目录,这是一种很快就会引起愤怒的文件污染。
更新Makefile.PL / Build.PL并使用File::Share
另一种在Perl发行版中包含数据文件的方法是将它们放置在发行版根目录中的“share”目录下,更新Makefile.PL / Build.PL以在安装期间复制数据文件,然后使用File::Share来访问文件。
如果您的发行版使用ExtUtils::MakeMaker,您可以在Makefile.PL中使用File::ShareDir::Install来复制数据文件。以下是虚构模块“Data::File”的纯真Makefile.PL
use 5.006;
use strict;
use warnings FATAL => 'all';
use ExtUtils::MakeMaker;
use File::ShareDir::Install;
install_share dist => 'share';
WriteMakefile(
NAME => 'Data::Dir',
AUTHOR => q{David Farrell },
VERSION_FROM => 'lib/Data/Dir.pm',
ABSTRACT_FROM => 'lib/Data/Dir.pm',
LICENSE => 'Artistic_2_0',
PL_FILES => {},
MIN_PERL_VERSION => 5.006,
CONFIGURE_REQUIRES => {
'ExtUtils::MakeMaker' => 0,
},
BUILD_REQUIRES => {
'Test::More' => 0,
},
PREREQ_PM => {
#'ABC' => 1.6,
#'Foo::Bar::Module' => 5.0401,
},
dist => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', },
clean => { FILES => 'Data-Dir-*' },
);
package MY;
use File::ShareDir::Install 'postamble';
在Makefile中,我们导入了File::ShareDir:Install,并将我们的“share”目录作为参数传递给“install_share”函数。Makefile中奇怪的最后一行包括对MY的包声明以及对File::ShareDir::Install的“postamble”方法的导入。务必包含这两行,否则数据文件将不会被复制。
如果您使用Module::Build,请更新Build.PL文件中的share_dir指令。以下是一个虚构模块“Data::File”的普通Build.PL示例:
use 5.006;
use strict;
use warnings FATAL => 'all';
use Module::Build;
my $builder = Module::Build->new(
module_name => 'Data::File',
license => 'Artistic_2_0',
dist_author => q{David Farrell },
dist_version_from => 'lib/Data/File.pm',
release_status => 'stable',
configure_requires => {
'Module::Build' => 0,
},
build_requires => {
'Test::More' => 0,
},
requires => {
#'ABC' => 1.6,
#'Foo::Bar::Module' => 5.0401,
},
add_to_cleanup => [ 'Data-File-*' ],
create_makefile_pl => 'traditional',
share_dir => 'share',
);
$builder->create_build_script();
上面示例中的Build.PL中的“share_dir”指令指示Module::Build在安装时将分发共享目录中的任何文件复制到分发自动目录。
无论您的分发使用Makefile.PL还是Build.PL,访问数据文件现在都是一个代码问题。以下是我们虚构的模块“Data::File”中删除所有内容的File.pm文件。
package Data::File;
use strict;
use warnings;
use YAML::XS;
use File::Share ':all';
sub read_data {
my $data_location = dist_file('Data-File', 'perl_tiobe.yaml');
open (my $DATA, '<', $data_location) or die $!;
my $yaml = do { local $/; <$DATA> };
my $data = Load $yaml;
do { ... };
}
1;
这部分代码应该看起来很熟悉。在“read_data”子程序中,我们使用File::Share的“dist_file”函数来获取数据文件的绝对文件路径。“dist_file”函数非常棒:它将在测试期间和模块安装后找到数据文件。在那行之后,我们打开文件句柄并像往常一样处理它。
这种方法比前两种需要更多的工作,但回报也很大:我们能够将数据包含在分发中,并在安装和运行时访问它。我们的代码文件不会因为可能不需要的额外数据而变得拥挤,并且我们不仅限于将数据文件包含在与消费代码文件相同的目录中。甚至可以使用“dist_file”(使用分发)从分发共享数据到另一个。
结论
示例主要关注包含YAML数据,但这些解决方案适用于大多数数据类型。将数据包含在Perl分发中并不像应该的那样容易。然而,通过这里描述的三个解决方案,您应该能够应对典型的场景。
喜欢这篇文章吗?请帮助我们转发,转发它!
本文最初发表在PerlTricks.com。
标签
反馈
这篇文章有什么问题吗?请通过在GitHub上打开问题或拉取请求来帮助我们。