File Coverage

blib/lib/mro/EVERY.pm
Criterion Covered Total %
statement 37 41 90.2
branch 5 12 41.6
condition n/a
subroutine 12 12 100.0
pod n/a
total 54 65 83.0


line stmt bran cond sub pod time code
1             ########################################################################
2             # housekeeping
3             ########################################################################
4 9     9   2732965 use v5.24;
  9         52  
5              
6             package mro::EVERY v1.0.3;
7 9     9   58 use mro;
  9         22  
  9         92  
8              
9 9     9   296 use Carp qw( croak );
  9         19  
  9         914  
10 9     9   64 use Scalar::Util qw( blessed );
  9         19  
  9         617  
11 9     9   64 use Symbol qw( qualify_to_ref );
  9         16  
  9         9261  
12              
13             ########################################################################
14             # package varaibles
15             ########################################################################
16              
17             our @CARP_NOT = ( __PACKAGE__, qw( mro ) );
18             my %class2dfs = ();
19              
20             ########################################################################
21             # utility subs
22             ########################################################################
23              
24             my $find_name
25             = sub
26             {
27             my $proto = shift;
28             my $auto = shift;
29             my ($name) = $auto =~ m{ (\w+) $}x;
30             my $class = blessed $proto || $proto;
31              
32             $proto->can( $name )
33             or croak "Botched EVERY: '$proto' cannot '$name'";
34              
35             # class at entry point gets to decide the mro type.
36              
37             my $mro = $class2dfs{ $class } || $class->mro::get_mro;
38             my @isa = $class->mro::get_linear_isa( $mro )->@*;
39              
40             # @found preserves array context of map.
41             #
42             # this should never croak afer the can
43             # check unless they have an overloaded
44             # can and forgot qw( autoload );
45              
46             my @found
47             = map
48             {
49             *{ qualify_to_ref $name => $_ }{ CODE }
50             or
51             ()
52             }
53             @isa
54             or
55             croak "Bogus $proto: '$name' not in @isa";
56              
57             @found
58             };
59              
60             my $find_auto
61             = sub
62             {
63             my $proto = shift;
64             my $auto = shift;
65             my ($name) = $auto =~ m{ (\w+) $}x;
66              
67             $proto->can( $name )
68             or croak "Botched EVERY: '$proto' cannot '$name'";
69              
70             local $" = ',';
71             my @isa = $proto->mro::get_linear_isa->@*;
72              
73             # @found preserves array context of map.
74              
75             my @found
76             = grep
77             {
78             $_
79             }
80             map
81             {
82             *{ qualify_to_ref $name => $_ }{ CODE }
83             or
84             do
85             {
86             my $isa = qualify_to_ref ISA => $_;
87             my $ref = qualify_to_ref AUTOLOAD => $_;
88              
89             local *$isa = [];
90              
91             # at this point can is isolated to the
92             # single pacakge.
93              
94             my $al
95             = $_->can( $name )
96             ? *{ $ref }{ CODE }
97             : ''
98             ;
99              
100             $al
101             ? sub
102             {
103             # at this point if package can $name and
104             # has an AUTOLOAD but not the named sub.
105             #
106             # install $AUTOLOAD and bon voyage!
107              
108             local *{ $ref } = $auto;
109             goto &$al;
110             }
111             : ()
112             ;
113             }
114             }
115             @isa
116             or
117             croak "Bogus $proto: '$name' & AUTOLOAD not in @isa";
118              
119             @found
120             };
121              
122             my $finder = $find_name;
123              
124             sub import
125             {
126 9     9   85 shift;
127 9         26 my $caller = caller;
128              
129 9         27 for( @_ )
130             {
131 4         31 my ( $status, $arg ) = m{ (no)? (dfs|autoload) }x;
132              
133 4 50       24 if( $arg eq 'dfs' )
    50          
134             {
135             # delay the lookup of mro::get_mro until runtim
136             # to allow classes to fiddle with it at runtime.
137              
138 0 0       0 if( $status )
139             {
140 0         0 delete $class2dfs{ $caller }
141             }
142             else
143             {
144 0         0 $class2dfs{ $caller } = 'dfs'
145             }
146             }
147             elsif( $arg eq 'autoload' )
148             {
149 4 50       30 $finder
150             = $status
151             ? $find_name
152             : $find_auto
153             }
154             else
155             {
156 0         0 croak "Botched EVERY: unknown argument '$_'";
157             }
158             }
159              
160             return
161 9         130 }
162              
163             ########################################################################
164             # downstream packages
165             ########################################################################
166              
167             package EVERY;
168 9     9   78 use Carp qw( croak );
  9         17  
  9         682  
169 9     9   64 use List::Util qw( uniq );
  9         17  
  9         1796  
170              
171             our @CARP_NOT = ( __PACKAGE__, qw( mro ) );
172             our $AUTOLOAD = '';
173              
174             AUTOLOAD
175             {
176 26 50   26   156585 my $proto = shift
177             or croak "Bogus EVERY, called without an object.";
178              
179             # remaining arguments left on the stack.
180              
181             $proto->$_( @_ )
182 26         100 for uniq $proto->$finder( $AUTOLOAD );
183             }
184              
185             package EVERY::LAST;
186 9     9   70 use Carp qw( croak );
  9         28  
  9         561  
187 9     9   58 use List::Util qw( uniq );
  9         16  
  9         1771  
188              
189             our @CARP_NOT = ( __PACKAGE__, qw( mro ) );
190             our $AUTOLOAD = '';
191              
192             AUTOLOAD
193             {
194 26 50   26   31502 my $proto = shift
195             or croak "Bogus EVERY::LAST, called without an object.";
196              
197             # remaining arguments left on the stack.
198              
199             $proto->$_( @_ )
200 26         86 for uniq reverse $proto->$finder( $AUTOLOAD );
201             }
202              
203             # keep require happy
204             1
205             __END__