—————————package
Module::Refresh;
use
strict;
$VERSION
=
"0.18"
;
BEGIN {
# Turn on the debugger's symbol source tracing
$^P |= 0x10;
# Work around bug in pre-5.8.7 perl where turning on $^P
# causes caller() to be confused about eval {}'s in the stack.
# (See http://rt.perl.org/rt3/Ticket/Display.html?id=35059 for more info.)
eval
'sub DB::sub'
if
$] < 5.008007;
}
=head1 NAME
Module::Refresh - Refresh %INC files when updated on disk
=head1 SYNOPSIS
# During each request, call this once to refresh changed modules:
Module::Refresh->refresh;
# Each night at midnight, you automatically download the latest
# Acme::Current from CPAN. Use this snippet to make your running
# program pick it up off disk:
$refresher->refresh_module('Acme/Current.pm');
=head1 DESCRIPTION
This module is a generalization of the functionality provided by
L<Apache::StatINC> and L<Apache::Reload>. It's designed to make it
easy to do simple iterative development when working in a persistent
environment.
It does not require mod_perl.
=cut
=head2 new
Initialize the module refresher.
=cut
sub
new {
my
$proto
=
shift
;
my
$self
=
ref
(
$proto
) ||
$proto
;
$self
->update_cache(
$_
)
for
keys
%INC
;
return
(
$self
);
}
=head2 refresh
Refresh all modules that have mtimes on disk newer than the newest ones we've got.
Calls C<new> to initialize the cache if it had not yet been called.
Specifically, it will renew any module that was loaded before the previous call
to C<refresh> (or C<new>) and has changed on disk since then. If a module was
both loaded for the first time B<and> changed on disk between the previous call
and this one, it will B<not> be reloaded by this call (or any future one); you
will need to update the modification time again (by using the Unix C<touch> command or
making a change to it) in order for it to be reloaded.
=cut
sub
refresh {
my
$self
=
shift
;
return
$self
->new
if
!
%CACHE
;
foreach
my
$mod
(
sort
keys
%INC
) {
$self
->refresh_module_if_modified(
$mod
);
}
return
(
$self
);
}
=head2 refresh_module_if_modified $module
If $module has been modified on disk, refresh it. Otherwise, do nothing
=cut
sub
refresh_module_if_modified {
my
$self
=
shift
;
return
$self
->new
if
!
%CACHE
;
my
$mod
=
shift
;
if
(!
$INC
{
$mod
}) {
return
;
}
elsif
( !
$CACHE
{
$mod
} ) {
$self
->update_cache(
$mod
);
}
elsif
(
$self
->mtime(
$INC
{
$mod
} ) ne
$CACHE
{
$mod
} ) {
$self
->refresh_module(
$mod
);
}
}
=head2 refresh_module $module
Refresh a module. It doesn't matter if it's already up to date. Just do it.
Note that it only accepts module names like C<Foo/Bar.pm>, not C<Foo::Bar>.
=cut
sub
refresh_module {
my
$self
=
shift
;
my
$mod
=
shift
;
$self
->unload_module(
$mod
);
local
$@;
eval
{
require
$mod
; 1 } or
warn
$@;
$self
->update_cache(
$mod
);
return
(
$self
);
}
=head2 unload_module $module
Remove a module from C<%INC>, and remove all subroutines defined in it.
=cut
sub
unload_module {
my
$self
=
shift
;
my
$mod
=
shift
;
my
$file
=
$INC
{
$mod
};
delete
$INC
{
$mod
};
delete
$CACHE
{
$mod
};
$self
->unload_subs(
$file
);
return
(
$self
);
}
=head2 mtime $file
Get the last modified time of $file in seconds since the epoch;
=cut
sub
mtime {
return
join
' '
, (
stat
(
$_
[1] ) )[ 1, 7, 9 ];
}
=head2 update_cache $file
Updates the cached "last modified" time for $file.
=cut
sub
update_cache {
my
$self
=
shift
;
my
$module_pm
=
shift
;
# In case the module was not loaded successfully.
return
unless
defined
$INC
{
$module_pm
};
$CACHE
{
$module_pm
} =
$self
->mtime(
$INC
{
$module_pm
} );
}
=head2 unload_subs $file
Wipe out subs defined in $file.
=cut
sub
unload_subs {
my
$self
=
shift
;
my
$file
=
shift
;
foreach
my
$sym
(
grep
{
index
(
$DB::sub
{
$_
},
"$file:"
) == 0 }
keys
%DB::sub
)
{
warn
"Deleting $sym from $file"
if
(
$sym
=~ /freeze/ );
eval
{
undef
&$sym
};
warn
"$sym: $@"
if
$@;
delete
$DB::sub
{
$sym
};
{
no
strict
'refs'
;
if
(
$sym
=~ /^(.*::)(.*?)$/) {
delete
*{$1}->{$2};
}
}
}
return
$self
;
}
# "Anonymize" all our subroutines into unnamed closures; so we can safely
# refresh this very package.
BEGIN {
no
strict
'refs'
;
foreach
my
$sym
(
sort
keys
%{ __PACKAGE__ .
'::'
} ) {
next
if
$sym
eq
'VERSION'
;
# Skip the version sub, inherited from UNIVERSAL
my
$code
= __PACKAGE__->can(
$sym
) or
next
;
delete
${ __PACKAGE__ .
'::'
}{
$sym
};
*$sym
=
sub
{
goto
&$code
};
}
}
1;
=head1 BUGS
When we walk the symbol table to whack reloaded subroutines, we don't
have a good way to invalidate the symbol table properly, so we mess up
on things like global variables that were previously set.
=head1 SEE ALSO
L<Apache::StatINC>, L<Module::Reload>
=head1 COPYRIGHT
Copyright 2004,2011 by Jesse Vincent E<lt>jesse@bestpractical.comE<gt>,
Audrey Tang E<lt>audreyt@audreyt.orgE<gt>
This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=cut