ハックがあるため必要とされるrequire
(従って、use
戻る前に両方のコンパイルおよび実行するモジュール)。
同じことが当てはまりますeval
。eval
実行せずにコードをコンパイルするために使用することはできません。
私が見つけた最も邪魔にならない解決策は、オーバーライドすることDB::postponed
です。これは、コンパイル済みの必要なファイルを評価する前に呼び出されます。残念ながら、これはデバッグ時にのみ呼び出されperl -d
ます()。
別の解決策は、ファイルを読み取って変更し、変更されたファイルを評価することです。
use File::Slurper qw( read_binary );
eval(read_binary("Foo.pm") . <<'__EOS__') or die $@;
package Foo {
no warnings qw( redefine );
sub bar { 7 }
}
__EOS__
上記は適切に設定%INC
されておらず、警告などで使用されているファイル名をめちゃくちゃにしている、呼び出していないDB::postponed
などです。以下はより堅牢なソリューションです。
use IO::Unread qw( unread );
use Path::Class qw( dir );
BEGIN {
my $preamble = '
UNITCHECK {
no warnings qw( redefine );
*Foo::bar = sub { 7 };
}
';
my @libs = @INC;
unshift @INC, sub {
my (undef, $fn) = @_;
return undef if $_[1] ne 'Foo.pm';
for my $qfn (map dir($_)->file($fn), @libs) {
open(my $fh, '<', $qfn)
or do {
next if $!{ENOENT};
die $!;
};
unread $fh, "$preamble\n#line 1 $qfn\n";
return $fh;
}
return undef;
};
}
use Foo;
私が使用UNITCHECK
私がオーバーライドを先頭に追加するので(使用して(コンパイル後が、実行前に呼び出される)unread
)ではなく、全体のファイルの読み込みと新しい定義を追加します。そのアプローチを使用したい場合は、使用して返すファイルハンドルを取得できます。
open(my $fh_for_perl, '<', \$modified_code);
return $fh_for_perl;
@INC
フックについて言及している@Grinnzへの称賛。
Foo::bar
が、use Foo
はコンパイル段階(以前に何か定義されていた場合はバーを再定義)とFooのランタイム段階の両方を実行します。私が考えられる唯一のこと@INC
は、Fooのロード方法を変更するための非常にハックなフックです。