Um hack é necessário porque require
(e, portanto,use
), compila e executa o módulo antes de retornar.
O mesmo vale para eval
.eval
não pode ser usado para compilar código sem também executá-lo.
A solução menos intrusiva que encontrei seria substituir DB::postponed
. Isso é chamado antes de avaliar um arquivo necessário compilado. Infelizmente, ele é chamado apenas durante a depuração (perl -d
).
Outra solução seria ler o arquivo, modificá-lo e avaliar o arquivo modificado, como o seguinte:
use File::Slurper qw( read_binary );
eval(read_binary("Foo.pm") . <<'__EOS__') or die $@;
package Foo {
no warnings qw( redefine );
sub bar { 7 }
}
__EOS__
O item acima não está configurado corretamente %INC
, altera o nome do arquivo usado pelos avisos e, portanto, não chama DB::postponed
etc. A seguir, é uma solução mais robusta:
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;
Eu usei UNITCHECK
(que é chamado após a compilação, mas antes da execução) porque eu antecede a substituição (usando unread
) em vez de ler o arquivo inteiro e anexar a nova definição. Se você quiser usar essa abordagem, poderá obter um identificador de arquivo para retornar usando
open(my $fh_for_perl, '<', \$modified_code);
return $fh_for_perl;
Parabéns ao @Grinnz por mencionar @INC
ganchos.
Foo::bar
, masuse Foo
funcionará tanto a fase de compilação (redefinindo a barra, se alguma coisa foi definida anteriormente lá) quanto a fase de execução do Foo. A única coisa em que consigo pensar seria em um@INC
gancho profundamente hacky para modificar como o Foo é carregado.