Ein Hack ist erforderlich, da require
(und damit use
) das Modul vor der Rückkehr kompiliert und ausgeführt wird.
Gleiches gilt für eval
. eval
kann nicht zum Kompilieren von Code verwendet werden, ohne ihn auch auszuführen.
Die am wenigsten aufdringliche Lösung, die ich gefunden habe, wäre das Überschreiben DB::postponed
. Dies wird aufgerufen, bevor eine kompilierte erforderliche Datei ausgewertet wird. Leider wird es nur beim Debuggen aufgerufen ( perl -d
).
Eine andere Lösung wäre, die Datei zu lesen, zu ändern und die geänderte Datei auszuwerten, ähnlich wie im Folgenden:
use File::Slurper qw( read_binary );
eval(read_binary("Foo.pm") . <<'__EOS__') or die $@;
package Foo {
no warnings qw( redefine );
sub bar { 7 }
}
__EOS__
Das oben Gesagte ist nicht richtig eingestellt %INC
, es bringt den Dateinamen durcheinander, der von Warnungen und dergleichen verwendet wird, es ruft nicht auf DB::postponed
usw. Das Folgende ist eine robustere Lösung:
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;
Ich habe verwendet UNITCHECK
(was nach der Kompilierung, aber vor der Ausführung aufgerufen wird), weil ich die Überschreibung (using unread
) vorangestellt habe, anstatt die gesamte Datei einzulesen und die neue Definition anzuhängen. Wenn Sie diesen Ansatz verwenden möchten, können Sie ein Dateihandle erhalten, mit dem Sie zurückkehren können
open(my $fh_for_perl, '<', \$modified_code);
return $fh_for_perl;
Ein großes Lob an @Grinnz für die Erwähnung von @INC
Hooks.
Foo::bar
, aber esuse Foo
wird sowohl die Kompilierungsphase (Neudefinition des Balkens, wenn dort zuvor etwas definiert wurde) als auch die Laufzeitphase von Foo ausgeführt. Das einzige, was ich mir vorstellen kann, wäre ein zutiefst hackiger@INC
Hook, um zu ändern, wie Foo geladen wird.