File Coverage

blib/lib/Sub/Block.pm
Criterion Covered Total %
statement 81 85 95.2
branch 12 16 75.0
condition 11 15 73.3
subroutine 24 25 96.0
pod 6 6 100.0
total 134 147 91.1


line stmt bran cond sub pod time code
1 1     1   25632 use 5.008;
  1         4  
  1         39  
2 1     1   5 use strict;
  1         2  
  1         25  
3 1     1   5 use warnings;
  1         6  
  1         66  
4              
5             package Sub::Block;
6              
7             our $AUTHORITY = 'cpan:TOBYINK';
8             our $VERSION = '0.002';
9              
10 1     1   954 use Moo;
  1         17488  
  1         6  
11              
12 1     1   1792 use Carp qw(carp croak);
  1         2  
  1         69  
13 1     1   822 use Exporter::Tiny qw();
  1         2339  
  1         24  
14 1     1   6 use Scalar::Util qw(blessed refaddr);
  1         2  
  1         156  
15 1     1   815 use Sub::Quote qw();
  1         3907  
  1         23  
16              
17 1     1   950 use namespace::clean;
  1         14074  
  1         7  
18              
19             {
20             our @ISA = 'Exporter::Tiny';
21             our @EXPORT = 'block';
22             sub _generate_block {
23 1     1   121 my $class = shift;
24 1     10   8 sub (&) { $class->new(@_) };
  10         25487  
25             }
26             }
27              
28             use overload (
29 11     11   7650 q[&{}] => sub { $_[0]{sub} },
30 4 50   4   544 q[>>] => sub { __PACKAGE__->sequence($_[2] ? @_[1,0] : @_[0,1]) },
31 1     1   2110 );
  1         1083  
  1         11  
32              
33             has sub => (is => 'ro', required => 1);
34             has [qw/ map grep /] => (is => 'lazy');
35              
36             my $deparse;
37             sub BUILDARGS
38             {
39 20     20 1 1493 my $class = shift;
40            
41 20 50 66     180 if (@_ == 1 and ref($_[0]) eq q(HASH))
    100 66        
42             {
43 0         0 return $_[0];
44             }
45             elsif (@_ == 1 and ref($_[0]) eq q(CODE))
46             {
47 12         84 require B::Deparse;
48 12         878 require PadWalker;
49 12   66     870 $deparse ||= 'B::Deparse'->new;
50            
51 12         23 my $coderef = shift;
52 12         37 $class->_check_coderef($coderef);
53            
54 12         365 my $closures = PadWalker::closed_over($coderef);
55 12         18089 my $perlcode = $deparse->coderef2text($coderef);
56            
57 12         179 $perlcode =~ s/(?:\A\{)|(?:\}\z)//g;
58 12         54 return +{ sub => Sub::Quote::quote_sub($perlcode, $closures) }
59             }
60             else
61             {
62 8         27 return +{ sub => scalar Sub::Quote::quote_sub(@_) };
63             }
64             }
65              
66             sub _check_coderef
67             {
68 12     12   51 require B;
69 12         17 my $class = shift;
70 12         16 my ($coderef) = @_;
71            
72             local *B::OP::__Sub_Block_callback = sub
73             {
74 128     128   616 my $name = $_[0]->name;
75 128 100 100     4254 return if $name ne 'return' && $name ne 'wantarray';
76 2         4 local $Carp::CarpLevel = $Carp::CarpLevel + 2;
77 2         462 carp("Coderef $coderef appears to contain an explicit `$name` statement; not suitable for inlining");
78 12         61 };
79            
80 12         184 B::svref_2object($coderef)->ROOT->B::walkoptree('__Sub_Block_callback');
81             }
82              
83             sub execute
84             {
85 0     0 1 0 my $self = shift;
86 0         0 my $sub = $self->{sub};
87 0         0 goto $sub;
88             }
89              
90             sub code
91             {
92 14     14 1 931 Sub::Quote::quoted_from_sub( $_[0]->{sub} )->[1];
93             }
94              
95             sub closures
96             {
97 13     13 1 2082 Sub::Quote::quoted_from_sub( $_[0]->{sub} )->[2];
98             }
99              
100             sub inlinify
101             {
102 1     1 1 1328 my $self = shift;
103 1         32 Sub::Quote::inlinify($self->code, join(q[,], @_), '', 1);
104             }
105              
106             sub sequence
107             {
108 4     4 1 8 my $class = __PACKAGE__;
109 4 50       12 $class = shift if !ref $_[0];
110            
111 4 100       27 my @subs = map { blessed($_) ? $_ : $class->new($_) } @_;
  8         81  
112            
113 4         344 my $code = '';
114 4         7 my $vars = {};
115            
116 4         8 for my $sub (@subs)
117             {
118 8         81 my $sub_closures = $sub->closures;
119 8         124 for my $k (sort keys %$sub_closures)
120             {
121 6 100 66     37 next if exists($vars->{$k}) && refaddr($vars->{$k})==refaddr($sub_closures->{$k});
122 4 50       9 croak "Attempted to close over two variables named $k" if exists($vars->{$k});
123 4         12 $vars->{$k} = $sub_closures->{$k};
124             }
125 8         14 $code .= "\@_ = do { ${\ $sub->code } };\n"
  8         27  
126             }
127            
128 4         65 $code .= 'eval { wantarray ? @_ : $_[-1] };'."\n";
129            
130 4         100 return $class->new($code, $vars);
131             }
132              
133             sub _build_from_template
134             {
135 4     4   8 my $self = shift;
136 4         14 my $code = sprintf($_[0], $self->code);
137 4         87 ref($self)->new($code, $self->closures);
138             }
139              
140             sub _build_map
141             {
142 3     3   1224 shift->_build_from_template('map { local @_ = ($_); %s } @_');
143             }
144              
145             sub _build_grep
146             {
147 1     1   734 shift->_build_from_template('grep { local @_ = ($_); %s } @_');
148             }
149              
150             1;
151              
152             __END__