È richiesto un hack perché require
(e quindi use
) entrambi compila ed esegue il modulo prima di tornare.
Lo stesso vale per eval
. eval
non può essere utilizzato per compilare il codice senza eseguirlo.
La soluzione meno invadente che ho trovato sarebbe quella di scavalcare DB::postponed
. Viene chiamato prima di valutare un file richiesto compilato. Sfortunatamente, viene chiamato solo quando debugging ( perl -d
).
Un'altra soluzione sarebbe quella di leggere il file, modificarlo e valutare il file modificato, un po 'come il seguente:
use File::Slurper qw( read_binary );
eval(read_binary("Foo.pm") . <<'__EOS__') or die $@;
package Foo {
no warnings qw( redefine );
sub bar { 7 }
}
__EOS__
Quanto sopra non è impostato correttamente %INC
, confonde il nome del file utilizzato dagli avvisi e simili, non chiama DB::postponed
, ecc. Di seguito è una soluzione più solida:
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;
Ho usato UNITCHECK
(che viene chiamato dopo la compilazione ma prima dell'esecuzione) perché ho anteposto l'override (usando unread
) anziché leggere l'intero file e aggiungere la nuova definizione. Se si desidera utilizzare tale approccio, è possibile ottenere un handle di file da restituire utilizzando
open(my $fh_for_perl, '<', \$modified_code);
return $fh_for_perl;
Complimenti a @Grinnz per aver menzionato i @INC
ganci.
Foo::bar
, mause Foo
eseguirà sia la fase di compilazione (ridefinendo la barra se qualcosa era stato precedentemente definito lì) sia la fase di runtime di Foo. L'unica cosa che mi viene in mente sarebbe un@INC
gancio profondamente confuso per modificare il modo in cui viene caricato Foo.