prefer Sub::Util to Sub::Name
Graham Knop [Thu, 19 Nov 2015 20:18:57 +0000 (15:18 -0500)]
Sub::Util can name subs as well, and is in core in newer versions.  Try
to use it instead of Sub::Name, although prefer Sub::Name if it is
already loaded.

Each module has some edge cases though.  Sub::Name has a memory leak
prior to 0.08.  Some systems can end up with a version of Sub::Util that
doesn't have the set_subname sub.  Check for these cases as well when
picking a module to use.

Also optimize out the sub naming if neither module is available.

dist.ini
lib/Try/Tiny.pm
t/named.t

index df88c9c..fa1d1f1 100644 (file)
--- a/dist.ini
+++ b/dist.ini
@@ -18,6 +18,7 @@ Git::NextVersion_version_regexp = ^Try-Tiny-(.+)$
 skip = ^perl$
 ; tests for optional Sub::Name stuff
 skip = ^Sub::Name$
+skip = ^Sub::Util$
 ; tests optionally require Capture::Tiny
 skip = ^Capture::Tiny$
 
@@ -26,4 +27,4 @@ perl = 5.006
 
 [Prereqs / TestRecommends]
 Capture::Tiny = 0.12 ; capture_stderr
-Sub::Name = 0
+Sub::Util = 0
index 4e8e06d..50f8e43 100644 (file)
@@ -11,7 +11,21 @@ our @EXPORT = our @EXPORT_OK = qw(try catch finally);
 use Carp;
 $Carp::Internal{+__PACKAGE__}++;
 
-BEGIN { eval "use Sub::Name; 1" or *{subname} = sub {1} }
+BEGIN {
+  my $su = $INC{'Sub/Util.pm'} && defined &Sub::Util::set_subname;
+  my $sn = $INC{'Sub/Name.pm'} && eval { Sub::Name->VERSION(0.08) };
+  unless ($su || $sn) {
+    $su = eval { require Sub::Util; } && defined &Sub::Util::set_subname;
+    unless ($su) {
+      $sn = eval { require Sub::Name; Sub::Name->VERSION(0.08) };
+    }
+  }
+
+  *_subname = $su ? \&Sub::Util::set_subname
+            : $sn ? \&Sub::Name::subname
+            : sub { $_[1] };
+  *_HAS_SUBNAME = ($su || $sn) ? sub(){1} : sub(){0};
+}
 
 # Need to prototype as @ not $$ because of the way Perl evaluates the prototype.
 # Keeping it at $$ means you only ever get 1 sub because we need to eval in a list
@@ -53,9 +67,8 @@ sub try (&;@) {
 
   # name the blocks if we have Sub::Name installed
   my $caller = caller;
-  subname("${caller}::try {...} " => $try);
-  subname("${caller}::catch {...} " => $catch) if $catch;
-  subname("${caller}::finally {...} " => $_) foreach @finally;
+  _subname("${caller}::try {...} " => $try)
+    if _HAS_SUBNAME;
 
   # save the value of $@ so we can set $@ back to it in the beginning of the eval
   # and restore $@ after the eval finishes
@@ -116,6 +129,9 @@ sub catch (&;@) {
 
   croak 'Useless bare catch()' unless wantarray;
 
+  my $caller = caller;
+  _subname("${caller}::catch {...} " => $block)
+    if _HAS_SUBNAME;
   return (
     bless(\$block, 'Try::Tiny::Catch'),
     @rest,
@@ -127,6 +143,9 @@ sub finally (&;@) {
 
   croak 'Useless bare finally()' unless wantarray;
 
+  my $caller = caller;
+  _subname("${caller}::finally {...} " => $block)
+    if _HAS_SUBNAME;
   return (
     bless(\$block, 'Try::Tiny::Finally'),
     @rest,
index 7de53b1..b6ba4e9 100644 (file)
--- a/t/named.t
+++ b/t/named.t
@@ -6,8 +6,9 @@ use warnings;
 use Test::More;
 
 BEGIN {
-    plan skip_all => "Sub::Name required"
-        unless eval { require Sub::Name; 1 };
+    plan skip_all => "Sub::Util or Sub::Name required"
+        unless eval { require Sub::Util; defined &Sub::Util::set_subname; }
+            || eval { require Sub::Name; Sub::Name->VERSION(0.08) };
     plan tests => 3;
 }