Perl 为什么用代码编写配置数据是个坏主意?
现实生活案例(从)举例说明短问题主题:Perl 为什么用代码编写配置数据是个坏主意?,perl,language-agnostic,Perl,Language Agnostic,现实生活案例(从)举例说明短问题主题: $CONFIG{'owner'} = q{Peter Palfrader}; $CONFIG{'email'} = q{peter@palfrader.org}; $CONFIG{'keyid'} = [ qw{DE7AAF6E94C09C7F 62AF4031C82E0039} ]; $CONFIG{'keyserver'} = 'wwwkeys.de.pgp.net'; $CONFIG{'mailer-send'} = [ 'testfile' ];
$CONFIG{'owner'} = q{Peter Palfrader};
$CONFIG{'email'} = q{peter@palfrader.org};
$CONFIG{'keyid'} = [ qw{DE7AAF6E94C09C7F 62AF4031C82E0039} ];
$CONFIG{'keyserver'} = 'wwwkeys.de.pgp.net';
$CONFIG{'mailer-send'} = [ 'testfile' ];
然后在代码中:eval`cat$config`
,访问%config
提供列出一般问题的答案,不仅仅针对示例。这种方法的一个主要问题是配置的可移植性不强。如果在Java中构建了功能相同的工具,则必须重新加载配置。如果Perl和Java变体都使用了简单的
key=value
布局,例如:
$CONFIG{'unhappy_employee'} = `rm -rf /`
owner = "Peter Palfrader"
email = "peter@peter@palfrader.org"
...
他们可以共享配置文件
此外,在配置文件上调用eval
,似乎会打开此系统,使其遭受攻击。如果一个恶意的人想要造成一些破坏,他们可以向这个配置文件添加什么?您是否意识到配置文件中的任意代码都将被执行
另一个问题是,这是非常反直觉的(至少对我来说)。我希望某个配置加载程序读取配置文件,而不是作为可运行的代码执行。这并不严重,但可能会让不习惯的新开发人员感到困惑
最后,虽然像
p{…}
这样的结构的实现不太可能改变,但如果它们改变了,这可能无法继续工作。将配置数据放在编译代码中是个坏主意,因为用户不容易改变它。对于脚本,只需确保它与其余部分完全分离,并对其进行良好的文档记录即可 这里的主要问题是在可以使用多种语言的环境中的可重用性。如果您的配置文件使用的是语言A,那么您希望与语言B共享此配置,则必须进行一些重写
如果您有更复杂的配置(例如apache配置文件),并且试图找出如何处理数据结构中的潜在差异,那么这将更加复杂。如果您使用JSON、YAML等,那么该语言中的解析器将知道如何映射该语言的数据结构
在一种语言中没有它们的一个主要缺点是,您失去了将配置值设置为动态数据的潜力。原因1。美学虽然没有人会因为难闻的气味而受到伤害,但人们往往会努力消除它 理由2。运营成本。对于一个5人的团队来说,这可能是可以的,但一旦您实现了开发人员/系统管理员的分离,您就必须雇佣理解Perl的系统管理员(即$$$),或者允许开发人员访问生产系统(大$$)
更糟糕的是,当您突然需要配置引擎时,您没有时间(也是$$$)引入它 避免在代码中进行配置有很多原因,我将在中的配置一章中介绍其中的一些原因
- 任何配置更改都不应带来破坏程序的风险。它当然不应该承担破坏编译阶段的风险
- 人们不必编辑源代码来获得不同的配置
- 人们应该能够共享相同的应用程序,而无需使用公共设置组,而只需重新安装应用程序来更改配置
- 应该允许用户创建多个不同的配置并成批运行,而无需编辑源代码
- 您应该能够在不同的设置下测试应用程序,而无需更改代码
- 人们不必学习如何编程才能使用您的工具
- 您应该只将配置数据结构松散地绑定到信息源,以使以后的体系结构更改更容易李>
- 您确实需要一个接口,而不是在应用程序级别直接访问
我在精通Perl课程中总结了这一点,告诉大家编程的第一条规则是创建一种情况,在这种情况下,您所做的工作更少,而其他人则不理您。当您将配置放入代码中时,您将花费更多的时间处理安装问题和响应中断。除非你喜欢这类事情,否则给人们一种改变设置的方法,而不会给你带来更多的工作。一个让我感到惊讶的原因是还没有人提到测试。当代码中包含config时,您必须编写疯狂的、扭曲的测试才能安全地进行测试。您可能会编写重复测试代码的测试,这使得测试几乎毫无用处;大多只是测试自己,容易漂移,难以维护
与测试同时进行的是前面提到的部署。当某个东西很容易测试时,它就很容易部署。我同意蒂姆·安德森的观点。这里有人将代码中的配置混淆为不可配置的配置。对于已编译代码,这是正确的 perl或ruby文件都是读取和解释的,yml文件或包含配置数据的xml文件也是读取和解释的。我选择yml是因为它在视觉上比在代码中更容易,因为按照测试环境、开发、阶段和生产进行分组,而在代码中涉及更多。。代码
另一方面,XML与“简单易懂”完全矛盾。我发现XML配置广泛用于编译语言,这很有趣。请原谅代码清单太长。下面是我在许多系统中使用过的一个方便的Conf.pm模块,它允许您为不同的生产、登台和开发环境指定不同的变量。然后,我构建我的程序,要么在命令行上接受环境参数,要么将此文件存储在源代码管理树之外,这样就永远不会写过头 自动加载为变量检索提供了自动方法
# Instructions:
# use Conf;
# my $c = Conf->new("production");
# print $c->root_dir;
# print $c->log_dir;
package Conf;
use strict;
our $AUTOLOAD;
my $default_environment = "production";
my @valid_environments = qw(
development
production
);
#######################################################################################
# You might need to change this.
sub set_vars {
my ($self) = @_;
$self->{"access_token"} = 'asdafsifhefh';
if ( $self->env eq "development" ) {
$self->{"root_dir"} = "/Users/patrickcollins/Documents/workspace/SysG_perl";
$self->{"server_base"} = "http://localhost:3000";
}
elsif ($self->env eq "production" ) {
$self->{"root_dir"} = "/mnt/SysG-production/current/lib";
$self->{"server_base"} = "http://api.SysG.com";
$self->{"log_dir"} = "/mnt/SysG-production/current/log"
} else {
die "No environment defined\n";
}
#######################################################################################
# You shouldn't need to configure this.
# More dirs. Move these into the dev/prod sections if they're different per env.
my $r = $self->{'root_dir'};
my $b = $self->{'server_base'};
$self->{"working_dir"} ||= "$r/working";
$self->{"bin_dir"} ||= "$r/bin";
$self->{"log_dir"} ||= "$r/log";
# Other URLs. Move these into the dev/prod sections if they're different per env.
$self->{"new_contract_url"} = "$b/SysG-training-center/v1/contract/new";
$self->{"new_documents_url"} = "$b/SysG-training-center/v1/documents/new";
}
#######################################################################################
# Code, don't change below here.
sub new {
my ($class,$env) = @_;
my $self = {};
bless ($self,$class);
if ($env) {
$self->env($env);
} else {
$self->env($default_environment);
}
$self->set_vars;
return $self;
}
sub AUTOLOAD {
my ($self,$val) = @_;
my $type = ref ($self) || die "$self is not an object";
my $field = $AUTOLOAD;
$field =~ s/.*://;
#print "field: $field\n";
unless (exists $self->{$field} || $field =~ /DESTROY/ )
{
die "ERROR: {$field} does not exist in object/class $type\n";
}
$self->{$field} = $val if ($val);
return $self->{$field};
}
sub env {
my ($self,$in) = @_;
if ($in) {
die ("Invalid environment $in") unless (grep($in,@valid_environments));
$self->{"_env"} = $in;
}
return $self->{"_env"};
}
1;
在我编写的许多小脚本中,配置的主要问题是它们通常包含我使用的服务的登录数据(用户名和密码或身份验证令牌)。后来,当脚本
$CONFIG{'user'} = 'username';
$CONFIG{'password'} = '123456';