Wymagany jest hack, ponieważ require
(a zatem use
) zarówno kompiluje, jak i wykonuje moduł przed zwróceniem.
To samo dotyczy eval
. eval
nie można go użyć do kompilacji kodu bez jego wykonania.
Najtrudniejszym rozwiązaniem, jakie znalazłem, byłoby zastąpienie DB::postponed
. Jest to wywoływane przed oceną skompilowanego wymaganego pliku. Niestety jest wywoływany tylko podczas debugowania ( perl -d
).
Innym rozwiązaniem byłoby odczytanie pliku, zmodyfikowanie go i ocena zmodyfikowanego pliku, podobnie jak to robi:
use File::Slurper qw( read_binary );
eval(read_binary("Foo.pm") . <<'__EOS__') or die $@;
package Foo {
no warnings qw( redefine );
sub bar { 7 }
}
__EOS__
Powyższe nie ustawia się poprawnie %INC
, miesza nazwę pliku używaną przez ostrzeżenia i takie, nie wywołuje DB::postponed
itp. Poniżej przedstawiono bardziej niezawodne rozwiązanie:
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;
Użyłem UNITCHECK
(który jest wywoływany po kompilacji, ale przed wykonaniem), ponieważ wstawiłem zastąpienie (używanie unread
) zamiast wczytywania całego pliku i dołączania nowej definicji. Jeśli chcesz zastosować to podejście, możesz uzyskać uchwyt pliku, z którego będziesz mógł powrócić
open(my $fh_for_perl, '<', \$modified_code);
return $fh_for_perl;
Uznanie dla @Grinnz za wzmianki o @INC
hakach.
Foo::bar
, aleuse Foo
uruchomi zarówno fazę kompilacji (przedefiniowanie paska, jeśli coś tam wcześniej zdefiniowano), jak i fazę działania Foo. Jedyne, co mogę wymyślić, to głęboko zhakowany@INC
hak do modyfikacji sposobu ładowania Foo.