Suppose you have a HUGE application "develoopped" ;) by a big team. Here is a simplified model of the potential disaster that may occur when somebody checks too deep in a data structure. If not possible to disable autovification completely or in scope, how to work around this? Thank you very much :) !!!!
假设你有一个巨大的应用程序“开发”;)由一个大团队。以下是当某人检查数据结构太深时可能发生的潜在灾难的简化模型。如果无法完全或在范围内禁用自动验证,如何解决此问题?非常感谢你 :) !!!!
use strict; use warnings;use Data::Dumper;
my $some_ref = {akey=>{deeper=>1}};
print Dumper($some_ref );
if($some_ref->{deep}{doot} == 1){
print 'too deep '.$/;
}
if($some_ref->{deep}){
print 'Already in a deep doot'.$/;
}
print Dumper($some_ref );
This outputs the following:
这输出如下:
$VAR1 = {
'akey' => {
'deeper' => 1
}
};
Use of uninitialized value in numeric eq (==) at autovivify_test.pl line 5.
Already in a deep doot
$VAR1 = {
'deep' => {},
'akey' => {
'deeper' => 1
}
};
Yes I know there is a warning, but... it may be too late.
是的我知道有警告,但是......可能为时已晚。
Hey guys it will may be help to say that my hashref references a tied HASH.
嘿伙计们,我的hashref引用一个绑定的HASH可能会有所帮助。
May be if I implement a good FETCH method which checks for checks deeper in the structure, I will solve easily my problem?
可能如果我实现了一个好的FETCH方法来检查结构中更深层次的检查,我会轻易解决我的问题吗?
I looked at Tie::StrictHash,Tie::Hash and perltie. Here is simplified version of my solution:
我看着Tie :: StrictHash,Tie :: Hash和perltie。这是我的解决方案的简化版本:
#!/usr/bin/env perl;
#test_tie.pl
package StrictHash;
use strict; use warnings;
use Tie::Hash;
our @ISA = qw(Tie::StdHash);
use Carp;
sub TIEHASH {
my $class = shift;
my $hash = bless {@_}, $class;
return $hash;
}
##========================================================================
## FETCH fails if applied to a member that doesn't exist.
##========================================================================
sub FETCH {
my ($hash, $key) = @_;
Carp::confess "key '$key' does not exist" unless exists $hash->{$key};
return $hash->{$key};
}
##========================================================================
package main;
use strict;use warnings;use Data::Dumper;
#Imagine StrictHash is in ./StrictHash.pm
#use StrictHash;
my %hash;
tie %hash, 'StrictHash', akey => {deeper=>1} ;
my $some_ref =\%hash;
print Dumper($some_ref );
if($some_ref->{deep}{doot} == 1){
print 'too deep '.$/;
}
What I achieved is to touch only one place in the app. Now all Places like if($some_ref->{deep}{doot}) will cause die with stack-trace. So I will easily find them and correct them. And new writings of this kind will NOT be possible. Perl is good for big apps too, you just need to know more ;).
我取得的成就是只触摸应用程序中的一个位置。现在所有像if($ some_ref - > {deep} {doot})这样的地方都会导致堆栈跟踪死亡。所以我很容易找到它们并纠正它们。这种新的着作是不可能的。 Perl也适用于大型应用程序,您只需要知道更多;)。
Thank you all! I hope this helps others too.
谢谢你们!我希望这也有助于其他人。
5 个解决方案
#1
You might want to use an object instead of the hash (see Moose) or use a strict tied hash. Or you can turn warnings into errors, if you really want to:
您可能希望使用对象而不是哈希(请参阅Moose)或使用严格绑定哈希。或者你可以将警告变成错误,如果你真的想:
use warnings NONFATAL => 'all', FATAL => 'uninitialized';
#2
Relatively new is the autovivification
module, which lets you do this:
相对较新的是autovivification模块,它允许您这样做:
no autovivification;
Pretty straightforward.
#3
You can lock the hash using one of the functions from Hash::Util (a core module).
您可以使用Hash :: Util(核心模块)中的一个函数来锁定哈希。
use Hash::Util qw( lock_keys unlock_keys );
my $some_ref = { akey => { deeper => 1 } };
lock_keys %$some_ref;
print "too deep" if $some_ref->{deep}{shit} == 1;
Now the last statement will throw an exception:
现在最后一个语句将抛出异常:
Attempt to access disallowed key 'deep' in a restricted hash
The downside is, of course, that you'll have to be very careful when checking for keys in the hash to avoid exceptions, i.e. use a lof of "if exists ...
" to check for keys before you access them.
当然,缺点是在检查散列中的密钥时必须非常小心以避免异常,即在访问密钥之前使用lof“if exists ...”来检查密钥。
If you need to add keys to the hash again later you can unlock it:
如果您需要稍后再次向哈希添加密钥,则可以将其解锁:
unlock_keys %$some_ref;
$some_ref->{foo} = 'bar'; # no exception
#4
I upvoted @zoul, but you should take it one step further.
我赞成@zoul,但你应该更进一步。
Write Tests
You should have your code covered with tests, and you should run some of those tests with
你应该用测试覆盖你的代码,你应该运行一些这些测试
use warnings FATAL => 'uninitialized';
declared in the test-case itself. Its the only way to address the concern you have with developers not checking things in advance properly. Make sure their code is tested.
在测试用例本身中声明。它是解决您对开发人员的问题的唯一方法,而不是事先正确地检查事物。确保他们的代码经过测试。
And take it another step further, and make it easy to run your tests under Devel::Cover to get a coverage report.
然后再向前迈出一步,并在Devel :: Cover下轻松运行测试以获得覆盖率报告。
cover -delete
PERL5OPT='-MDevel::Cover' prove -l
cover -report Html_basic
And then check the lines of code and statements are being executed by the tests, otherwise making those warnings fatal will just make code die at an unexpected time later.
然后检查代码行和语句正在由测试执行,否则使这些警告致命只会使代码在以后的意外时间死亡。
#5
Another option is to use Data::Diver to access your data structures.
另一种选择是使用Data :: Diver来访问您的数据结构。
if( 1 == Dive($some_ref, qw/ deep structures are not autovivified now / )) {
Do_Stuff();
}
#1
You might want to use an object instead of the hash (see Moose) or use a strict tied hash. Or you can turn warnings into errors, if you really want to:
您可能希望使用对象而不是哈希(请参阅Moose)或使用严格绑定哈希。或者你可以将警告变成错误,如果你真的想:
use warnings NONFATAL => 'all', FATAL => 'uninitialized';
#2
Relatively new is the autovivification
module, which lets you do this:
相对较新的是autovivification模块,它允许您这样做:
no autovivification;
Pretty straightforward.
#3
You can lock the hash using one of the functions from Hash::Util (a core module).
您可以使用Hash :: Util(核心模块)中的一个函数来锁定哈希。
use Hash::Util qw( lock_keys unlock_keys );
my $some_ref = { akey => { deeper => 1 } };
lock_keys %$some_ref;
print "too deep" if $some_ref->{deep}{shit} == 1;
Now the last statement will throw an exception:
现在最后一个语句将抛出异常:
Attempt to access disallowed key 'deep' in a restricted hash
The downside is, of course, that you'll have to be very careful when checking for keys in the hash to avoid exceptions, i.e. use a lof of "if exists ...
" to check for keys before you access them.
当然,缺点是在检查散列中的密钥时必须非常小心以避免异常,即在访问密钥之前使用lof“if exists ...”来检查密钥。
If you need to add keys to the hash again later you can unlock it:
如果您需要稍后再次向哈希添加密钥,则可以将其解锁:
unlock_keys %$some_ref;
$some_ref->{foo} = 'bar'; # no exception
#4
I upvoted @zoul, but you should take it one step further.
我赞成@zoul,但你应该更进一步。
Write Tests
You should have your code covered with tests, and you should run some of those tests with
你应该用测试覆盖你的代码,你应该运行一些这些测试
use warnings FATAL => 'uninitialized';
declared in the test-case itself. Its the only way to address the concern you have with developers not checking things in advance properly. Make sure their code is tested.
在测试用例本身中声明。它是解决您对开发人员的问题的唯一方法,而不是事先正确地检查事物。确保他们的代码经过测试。
And take it another step further, and make it easy to run your tests under Devel::Cover to get a coverage report.
然后再向前迈出一步,并在Devel :: Cover下轻松运行测试以获得覆盖率报告。
cover -delete
PERL5OPT='-MDevel::Cover' prove -l
cover -report Html_basic
And then check the lines of code and statements are being executed by the tests, otherwise making those warnings fatal will just make code die at an unexpected time later.
然后检查代码行和语句正在由测试执行,否则使这些警告致命只会使代码在以后的意外时间死亡。
#5
Another option is to use Data::Diver to access your data structures.
另一种选择是使用Data :: Diver来访问您的数据结构。
if( 1 == Dive($some_ref, qw/ deep structures are not autovivified now / )) {
Do_Stuff();
}