File Coverage

blib/lib/Sub/Operable.pm
Criterion Covered Total %
statement 68 72 94.4
branch 11 20 55.0
condition n/a
subroutine 18 18 100.0
pod 0 4 0.0
total 97 114 85.0


line stmt bran cond sub pod time code
1 4     4   278351 use 5.008009;
  4         43  
2 4     4   23 use strict;
  4         7  
  4         87  
3 4     4   18 use warnings;
  4         7  
  4         154  
4 4     4   5031 use overload ();
  4         4182  
  4         452  
5              
6             package Sub::Operable;
7              
8             our $AUTHORITY = 'cpan:TOBYINK';
9             our $VERSION = '0.001';
10              
11 4         657 use constant PREFIX_OPS => map split(/ /, $overload::ops{$_}), qw(
12             unary
13             func
14 4     4   33 );
  4         8  
15              
16 4         967 use constant INFIX_OPS => map split(/ /, $overload::ops{$_}), qw(
17             with_assign
18             num_comparison
19             3way_comparison
20             str_comparison
21             binary
22             matching
23 4     4   31 );
  4         6  
24              
25             'overload'->import(
26             (map {
27             my $op = $_;
28             $op => sub {
29 3 50   3   2599 my $next = $_[0]->can('do_prefix_op') or die;
30 3         15 splice(@_, 1, 0, $op); goto $next;
  3         12  
31             }
32             } PREFIX_OPS),
33             (map {
34             my $op = $_;
35             $op => sub {
36 9 50   9   2244 my $next = $_[0]->can('do_infix_op') or die;
37 9         33 splice(@_, 1, 0, $op); goto $next;
  9         30  
38             }
39             } INFIX_OPS),
40             );
41              
42 4     4   1856 use isa __PACKAGE__;
  4         17853  
  4         23  
43              
44 4     4   2396 use Exporter::Shiny qw( isa_Sub_Operable subop );
  4         17079  
  4         29  
45              
46             sub new {
47 28     28 0 190 my ( $class, $coderef ) = ( shift, @_ );
48 28         71 my $wrapped = $class->build_wrapped_coderef( $coderef );
49 28         145 bless $wrapped, $class;
50             }
51              
52             sub build_wrapped_coderef {
53 28     28 0 81 my ( $class, $orig ) = ( shift, @_ );
54             return sub {
55 66     66   5019 my $has_subops = grep isa_Sub_Operable($_), @_;
56 66 100       121 if ( $has_subops ) {
57 8         20 my @args = @_;
58             my $new = sub {
59 14     4   28 my @inner_args = @_;
60 14         28 @_ = ();
61 14         24 foreach my $arg ( @args ) {
62 14 50       50 push @_, isa_Sub_Operable($arg) ? $arg->(@inner_args) : $arg;
63             }
64 14         65 local *_ = \$_[0];
65 14         30 &$orig;
66 8         43 };
67 8         25 return $class->new($new);
68             }
69             else {
70 58         96 local *_ = \$_[0];
71 58         419 &$orig;
72             }
73 28         105 };
74             }
75              
76             sub do_infix_op {
77 9     9 0 25 my ( $x, $op, $y, $swap ) = @_;
78 9         21 my $class = ref($x);
79 9 50       21 ( $x, $y ) = ( $y, $x ) if $swap;
80            
81 9         25 my $code;
82            
83 9 50       52 if ( isa_Sub_Operable $x ) {
84 9 100       28 if ( isa_Sub_Operable $y ) {
85 6         28 $code = sprintf( 'sub { $x->(@_) %s $y->(@_) }', $op );
86             }
87             else {
88 3         13 $code = sprintf( 'sub { $x->(@_) %s $y }', $op );
89             }
90             }
91             else {
92 0 0       0 if ( isa_Sub_Operable $y ) {
93 0         0 $code = sprintf( 'sub { $x %s $y->(@_) }', $op );
94             }
95             else {
96 0         0 $code = sprintf( 'sub { $x %s $y }', $op );
97             }
98             }
99            
100 9         753 $class->new( scalar eval $code );
101             }
102              
103             sub do_prefix_op {
104 3     3 0 10 my ( $x, $op ) = @_;
105 3         8 my $class = ref($x);
106            
107 3 50       11 if ( $op eq 'neg' ) {
108 3         29 $op = '-';
109             }
110            
111 3         7 my $code;
112            
113 3 50       15 if ( isa_Sub_Operable $x ) {
114 3         14 $code = sprintf( 'sub { %s $x->(@_) }', $op );
115             }
116             else {
117 0         0 $code = sprintf( 'sub { %s $x }', $op );
118             }
119            
120 3         257 my $coderef = eval $code;
121 3         15 $class->new( $coderef );
122             }
123              
124             sub _generate_subop {
125 3     3   672 my ( $class ) = ( shift );
126 3     6   21 return sub (&) { $class->new(@_) };
  6         342  
127             }
128              
129             1;
130              
131             __END__