summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2014-02-25 21:48:11 -0500
committerJesse Luehrs <doy@tozt.net>2014-02-25 21:48:11 -0500
commit6792afefe83326f5a967444c462d4eb17c7930b0 (patch)
tree049df25038f1faeb074202360e68cf95dd12cab6
parent352f306279f8ba07e3b6bd1d534ca45312305f90 (diff)
downloadclass-refresh-6792afefe83326f5a967444c462d4eb17c7930b0.tar.gz
class-refresh-6792afefe83326f5a967444c462d4eb17c7930b0.zip
handle new moose behavior a bit better (fixes #3)
new moose sets %INC for autogenerated classes, so we have to do a better job at skipping those now (since actually trying to reload them isn't going to work). also added a warning for when this happens, since it's quite possibly actually doing something wrong (it was just doing it wrong silently before).
-rw-r--r--lib/Class/Refresh.pm42
-rw-r--r--t/moose-metaclasses.t7
2 files changed, 48 insertions, 1 deletions
diff --git a/lib/Class/Refresh.pm b/lib/Class/Refresh.pm
index 320490d..c5a288d 100644
--- a/lib/Class/Refresh.pm
+++ b/lib/Class/Refresh.pm
@@ -3,6 +3,7 @@ use strict;
use warnings;
# ABSTRACT: refresh your classes during runtime
+use Carp 'carp';
use Class::Unload;
use Class::Load;
use Try::Tiny;
@@ -152,7 +153,39 @@ sub refresh_module {
my @to_refresh = grep { exists $INC{ $class->_mod_to_file($_) } }
$class->_dependent_modules($mod);
+ # immutable metaclasses will be automatically recreated when the metaclass
+ # itself is loaded, so we don't want to try to do it here (it won't work,
+ # since it's an autogenerated class)
+ my %metas_for_immutable;
+ if (Class::Load::is_class_loaded('Class::MOP')) {
+ my @immutable_metas = grep {
+ defined $_ && $_->isa('Class::MOP::Class') && $_->is_immutable
+ } map { Class::MOP::class_of($_) } @to_refresh;
+ for my $meta (@immutable_metas) {
+ $metas_for_immutable{ref $meta} = 1;
+ }
+ }
+
+ # XXX don't know what else to do here
+ if (Class::Load::is_class_loaded('Class::MOP')) {
+ my @new_to_refresh;
+ for my $to_refresh (@to_refresh) {
+ my $inc = $INC{ $class->_mod_to_file($to_refresh) } || '';
+ if (!$metas_for_immutable{$to_refresh} && $inc eq '(set by Moose)' && Class::MOP::class_of($to_refresh)) {
+ carp("Not reloading $to_refresh since it was created dynamically");
+ next;
+ }
+ push @new_to_refresh, $to_refresh;
+ }
+ @to_refresh = @new_to_refresh;
+ }
+
$class->unload_module($_) for @to_refresh;
+
+ if (Class::Load::is_class_loaded('Class::MOP')) {
+ @to_refresh = grep { !$metas_for_immutable{$_} } @to_refresh;
+ }
+
$class->load_module($_) for @to_refresh;
}
@@ -324,6 +357,15 @@ For instance, modifying a L<Moose::Exporter> module which is used in a class
won't cause the class to be refreshed, even if the change to the exporter would
cause a change in the class's metaclass.
+=item Classes which aren't completely defined in a single file and files which define multiple classes cause problems
+
+If a class is defined across multiple files, there's no easy guaranteed way to
+restore the entire state of the class, since there may be load order issues.
+This includes L<Moose> classes which have C<make_immutable> called on them from
+outside of the class file itself.
+
+Also, files which define multiple classes cause problems since we can't always determine which classes are defined in the file, and so reloading the file may cause class definitions to be run more than once.
+
=item Classes which build themselves differently based on the state of other classes may not work properly
This module attempts to handle several cases of this sort for L<Moose> classes
diff --git a/t/moose-metaclasses.t b/t/moose-metaclasses.t
index f8e655e..a3c9b34 100644
--- a/t/moose-metaclasses.t
+++ b/t/moose-metaclasses.t
@@ -35,7 +35,12 @@ is_deeply(\%reloaded,
sleep 2;
update_temp_dir_for('moose-metaclasses', $dir);
-Class::Refresh->refresh;
+{
+ my $warnings;
+ local $SIG{__WARN__} = sub { $warnings .= $_[0] };
+ Class::Refresh->refresh;
+ like($warnings, qr/Not reloading Moose::Meta::Class::__ANON__::SERIAL::/);
+}
does_ok(Foo->meta, 'Foo::Meta::Class');
ok(!Moose::Util::does_role(Bar->meta, 'Foo::Meta::Class'),