use Devel::Hints where possible
Jesse Luehrs [Wed, 10 Nov 2010 11:13:27 +0000 (05:13 -0600)]
this will avoid breaking memoization when generating coderefs with
descriptions, and should be more robust and useful

dist.ini
lib/Eval/Closure.pm
t/03-description.t
t/05-memoize.t
t/11-line-differences.t [new file with mode: 0644]

index 75234df..620f458 100644 (file)
--- a/dist.ini
+++ b/dist.ini
@@ -7,6 +7,7 @@ copyright_holder = Jesse Luehrs
 dist = Eval-Closure
 
 [Prereqs]
+Devel::Hints = 0.22
 Scalar::Util = 0
 Sub::Exporter = 0
 Try::Tiny = 0
index b03df5b..ee93a7a 100644 (file)
@@ -8,11 +8,14 @@ use Sub::Exporter -setup => {
 # ABSTRACT: safely and cleanly create closures via string eval
 
 use Carp;
+use Devel::Hints qw(cop_file cop_line);
 use overload ();
 use Memoize;
 use Scalar::Util qw(reftype);
 use Try::Tiny;
 
+use constant USE_DEVEL_HINTS => ($] >= 5.010);
+
 =head1 SYNOPSIS
 
   use Eval::Closure;
@@ -93,14 +96,23 @@ sub eval_closure {
     $args{source} = _canonicalize_source($args{source});
     _validate_env($args{environment} ||= {});
 
-    $args{source} = _line_directive($args{description}) . $args{source}
-        if defined $args{description};
+    if (!USE_DEVEL_HINTS) {
+        $args{source} = _line_directive($args{description}) . $args{source}
+            if defined $args{description};
+    }
 
     my ($code, $e) = _clean_eval_closure(@args{qw(source environment)});
 
     croak("Failed to compile source: $e\n\nsource:\n$args{source}")
         unless $code;
 
+    if (USE_DEVEL_HINTS) {
+        if (defined $args{description}) {
+            cop_file($code, $args{description});
+            cop_line($code, 1);
+        }
+    }
+
     return $code;
 }
 
@@ -147,7 +159,7 @@ sub _validate_env {
 sub _line_directive {
     my ($description) = @_;
 
-    return qq{#line 1 "$description"\n};
+    return qq{#line 0 "$description"\n};
 }
 
 sub _clean_eval_closure {
index 97f8372..c9e8b21 100644 (file)
@@ -32,7 +32,7 @@ SOURCE
 
     like(
         exception { $code->() },
-        qr/^foo at accessor foo \(defined at Class\.pm line 282\) line 2\n/,
+        qr/^foo at accessor foo \(defined at Class\.pm line 282\) line 1\n/,
         "description is set"
     );
 }
index 02fd11f..e4b582b 100644 (file)
@@ -8,7 +8,12 @@ use Test::Requires 'Test::Output';
 use Eval::Closure;
 
 {
-    my $source = 'BEGIN { warn "foo\n" } sub { $foo * 2 }';
+    my $source = <<'SOURCE';
+    sub {
+        $foo * 2;
+    };
+    BEGIN { warn "foo\n" }
+SOURCE
 
     my $code;
     my $bar = 15;
@@ -38,7 +43,12 @@ use Eval::Closure;
 }
 
 {
-    my $source = 'BEGIN { warn "bar\n" } sub { $bar * 2 }';
+    my $source = <<'SOURCE';
+    sub {
+        $bar * 2;
+    };
+    BEGIN { warn "bar\n" }
+SOURCE
 
     my $code;
     my $foo = 60;
@@ -56,7 +66,8 @@ use Eval::Closure;
 
     my $code2;
     my $baz = 23;
-    { local $TODO = "description breaks memoization";
+    { local $TODO = $] < 5.010 ? "description breaks memoization on 5.8"
+                               : undef;
     stderr_is {
         $code2 = eval_closure(
             source      => $source,
@@ -72,7 +83,12 @@ use Eval::Closure;
 }
 
 {
-    my $source = 'BEGIN { warn "baz\n" } sub { Carp::confess "baz" }';
+    my $source = <<'SOURCE';
+    sub {
+        Carp::confess "baz";
+    };
+    BEGIN { warn "baz\n" }
+SOURCE
 
     my $code;
     stderr_is {
@@ -86,7 +102,8 @@ use Eval::Closure;
          "got the right description");
 
     my $code2;
-    { local $TODO = "description breaks memoization";
+    { local $TODO = $] < 5.010 ? "description breaks memoization on 5.8"
+                               : undef;
     stderr_is {
         $code2 = eval_closure(
             source      => $source,
diff --git a/t/11-line-differences.t b/t/11-line-differences.t
new file mode 100644 (file)
index 0000000..4dd3625
--- /dev/null
@@ -0,0 +1,61 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+use Test::Requires 'Test::Output';
+
+use Eval::Closure;
+
+{
+    my $code = eval_closure(
+        source      => 'sub { warn "foo" }',
+        description => 'bar',
+    );
+    { local $TODO = $] < 5.010 ? "line numbers from #line are slightly different" : undef;
+    stderr_is { $code->() } "foo at bar line 1.\n", "got the right line";
+    }
+}
+
+{
+    my $code = eval_closure(
+        source      => <<'SOURCE',
+    sub {
+
+        warn "foo";
+
+    }
+SOURCE
+        description => 'bar',
+    );
+    { local $TODO = $] < 5.010 ? "line numbers from #line are slightly different" : undef;
+    stderr_is { $code->() } "foo at bar line 1.\n", "got the right line";
+    }
+}
+
+{
+    my $code = eval_closure(
+        source      => <<'SOURCE',
+
+    sub {
+        warn "foo";
+    }
+SOURCE
+        description => 'bar',
+    );
+    { local $TODO = $] < 5.010 ? "line numbers from #line are slightly different" : undef;
+    stderr_is { $code->() } "foo at bar line 1.\n", "got the right line";
+    }
+}
+
+{
+    my $code = eval_closure(
+        source      => '$sub',
+        environment => { '$sub' => \sub { warn "foo" } },
+        description => 'bar',
+    );
+    { local $TODO = $] < 5.010 ? "#line can't adjust line numbers inside non-evaled subs" : undef;
+    stderr_is { $code->() } "foo at bar line 1.\n", "got the right line";
+    }
+}
+
+done_testing;