demonstrate eval tracking
Matt S Trout [Sun, 21 Jun 2009 19:12:24 +0000 (15:12 -0400)]
lib/MooseX/Antlers/EvalTracker.pm [new file with mode: 0644]
t/track_eval.t [new file with mode: 0644]

diff --git a/lib/MooseX/Antlers/EvalTracker.pm b/lib/MooseX/Antlers/EvalTracker.pm
new file mode 100644 (file)
index 0000000..68a85cc
--- /dev/null
@@ -0,0 +1,71 @@
+package MooseX::Antlers::EvalTracker;
+
+use Moose;
+use Scalar::Util qw(weaken refaddr);
+use PadWalker qw(closed_over);
+use namespace::clean -except => 'meta';
+
+has '_original_eval_closure' => (is => 'ro', lazy_build => 1);
+has '_our_eval_closure' => (is => 'ro', lazy_build => 1);
+
+has 'recorded_coderefs' => (is => 'ro', default => sub { {} });
+
+has 'is_enabled' => (is => 'rw');
+
+sub _build__original_eval_closure {
+  Class::MOP::Method::Generated->can('_eval_closure');
+}
+
+sub _build__our_eval_closure {
+  my $eval_tracker = shift;
+  my $orig = $eval_tracker->_original_eval_closure;
+  weaken($eval_tracker); # avoid circ ref-ing $self
+  # _eval_closure returns a CODE ref and we want that too
+  sub {
+    my $self = shift;
+    # until we stop passing these let's make anything that uses it
+    # crash out so at least we don't break anything
+    #delete @{$_[0]}{qw($meta $attr)};
+    my $cr = $self->$orig(@_);
+    $eval_tracker->_eval_closure_called_for($cr => @_);
+    return $cr;
+  };
+}
+
+sub enable {
+  my $self = shift;
+  return if $self->is_enabled;
+  $self->_install('our');
+  $self->is_enabled(1);
+  return;
+}
+
+sub disable {
+  my $self = shift;
+  return unless $self->is_enabled;
+  $self->_install('original');
+  $self->is_enabled(0);
+  return;
+}
+
+sub _install {
+  my ($self, $type) = @_;
+  my $code = $self->${\"_${type}_eval_closure"};
+  { # we're instrumenting the MOP so bypass it and use the old fashioned way
+    no strict 'refs'; no warnings 'redefine';
+    *Class::MOP::Method::Generated::_eval_closure = $code;
+  }
+}
+
+sub _eval_closure_called_for {
+  my ($self, $cr, $captures, $body) = @_;
+  use Data::Dumper; $Data::Dumper::Indent = 1;
+  my %captured = %{closed_over $cr};
+  delete @{$captures}{grep !exists($captured{$_}), keys %$captures};
+  warn Dumper($captures);
+  warn Dumper($body);
+}
+
+sub DEMOLISH { shift->disable }
+
+1;
diff --git a/t/track_eval.t b/t/track_eval.t
new file mode 100644 (file)
index 0000000..d81fb35
--- /dev/null
@@ -0,0 +1,15 @@
+use strict;
+use warnings;
+use aliased 'MooseX::Antlers::EvalTracker';
+
+my $et = EvalTracker->new;
+
+$et->enable;
+
+{
+  package Foo;
+
+  use Moose;
+
+  has 'test' => (is => 'rw', trigger => sub { warn "trigger!\n" });
+}