File Coverage

blib/lib/Object/InsideOut/Universal.pm
Criterion Covered Total %
statement 84 105 80.0
branch 46 72 63.8
condition 11 33 33.3
subroutine 7 7 100.0
pod n/a
total 148 217 68.2


line stmt bran cond sub pod time code
1             package Object::InsideOut; {
2              
3 8     8   71 use strict;
  8         20  
  8         271  
4 8     8   44 use warnings;
  8         15  
  8         249  
5 8     8   41 no warnings 'redefine';
  8         14  
  8         3716  
6              
7             # Install versions of UNIVERSAL::can/isa that understands :Automethod and
8             # foreign inheritance
9             sub install_UNIVERSAL
10             {
11             my ($GBL) = @_;
12              
13             *Object::InsideOut::can = sub
14             {
15 27     27   15245 my ($thing, $method) = @_;
16              
17 27 50       83 return if (! defined($thing));
18              
19             # Metadata call for methods
20 27 100       75 if (@_ == 1) {
21 4         14 my $meths = Object::InsideOut::meta(shift)->get_methods();
22 4 100       16 return (wantarray()) ? (keys(%$meths)) : [ keys(%$meths) ];
23             }
24              
25 23 50       56 return if (! defined($method));
26              
27             # First, try the original UNIVERSAL::can()
28 23         71 my $code;
29 23 100       69 if ($method =~ /^SUPER::/) {
30             # Superclass WRT caller
31 1         3 my $caller = caller();
32 1         3 eval { $code = $thing->Object::InsideOut::SUPER::can($caller.'::'.$method) };
  1         9  
33             } else {
34 22         41 eval { $code = $thing->Object::InsideOut::SUPER::can($method) };
  22         167  
35             }
36 23 100       65 if ($code) {
37 9         28 return ($code);
38             }
39              
40             # Handle various calling methods
41 14         27 my ($class, $super);
42 14 100       56 if ($method !~ /::/) {
    50          
    50          
43             # Ordinary method check
44             # $obj->can('x');
45 13   66     56 $class = ref($thing) || $thing;
46              
47             } elsif ($method !~ /SUPER::/) {
48             # Fully-qualified method check
49             # $obj->can('FOO::x');
50 0         0 ($class, $method) = $method =~ /^(.+)::([^:]+)$/;
51              
52             } elsif ($method =~ /^SUPER::/) {
53             # Superclass method check
54             # $obj->can('SUPER::x');
55 1         3 $class = caller();
56 1         5 $method =~ s/SUPER:://;
57 1         3 $super = 1;
58              
59             } else {
60             # Qualified superclass method check
61             # $obj->can('Foo::SUPER::x');
62 0         0 ($class, $method) = $method =~ /^(.+)::SUPER::([^:]+)$/;
63 0         0 $super = 1;
64             }
65              
66 14         30 my $heritage = $$GBL{'heritage'};
67 14         34 my $automethods = $$GBL{'sub'}{'auto'};
68              
69             # Next, check with heritage objects and Automethods
70 14         28 my ($code_type, $code_dir, %code_refs);
71 14         23 foreach my $pkg (@{$$GBL{'tree'}{'bu'}{$class}}) {
  14         45  
72             # Skip self's class if SUPER
73 28 100 100     86 if ($super && $class eq $pkg) {
74 1         4 next;
75             }
76              
77             # Check heritage
78 27 100       56 if (exists($$heritage{$pkg})) {
79 8     8   69 no warnings;
  8         16  
  8         7663  
80 4         6 foreach my $pkg2 (keys(%{$$heritage{$pkg}{'cl'}})) {
  4         14  
81 4 50       17 if ($code = $pkg2->can($method)) {
82 4         16 return ($code);
83             }
84             }
85             }
86              
87             # Check with the Automethods
88 23 100       59 if (my $automethod = $$automethods{$pkg}) {
89             # Call the Automethod to get a code ref
90 14         25 local $CALLER::_ = $_;
91 14         22 local $_ = $method;
92 14         58 local $SIG{'__DIE__'} = 'OIO::trap';
93 14 100       42 if (my ($code, $ctype) = $automethod->($thing)) {
94 12 50       179 if (ref($code) ne 'CODE') {
95             # Not a code ref
96 0         0 OIO::Code->die(
97             'message' => ':Automethod did not return a code ref',
98             'Info' => ":Automethod in package '$pkg' invoked for method '$method'");
99             }
100              
101 12 100       35 if (defined($ctype)) {
102 3         17 my ($type, $dir) = $ctype =~ /(\w+)(?:[(]\s*(.*)\s*[)])?/;
103 3 50 33     26 if ($type && $type =~ /CUM/i) {
104 3 100       8 if ($code_type) {
105 2         4 $type = ':Cumulative';
106 2 50 33     8 $dir = ($dir && $dir =~ /BOT/i) ? 'bottom up' : 'top down';
107 2 50 33     11 if ($code_type ne $type || $code_dir ne $dir) {
108             # Mixed types
109 0         0 my ($pkg2) = keys(%code_refs);
110 0         0 OIO::Code->die(
111             'message' => 'Inconsistent code types returned by :Automethods',
112             'Info' => "Class '$pkg' returned type $type($dir), and class '$pkg2' returned type $code_type($code_dir)");
113             }
114             } else {
115 1         2 $code_type = ':Cumulative';
116 1 50 33     4 $code_dir = ($dir && $dir =~ /BOT/i) ? 'bottom up' : 'top down';
117             }
118 3         13 $code_refs{$pkg} = $code;
119 3         16 next;
120             }
121 0 0 0     0 if ($type && $type =~ /CHA/i) {
122 0 0       0 if ($code_type) {
123 0         0 $type = ':Chained';
124 0 0 0     0 $dir = ($dir && $dir =~ /BOT/i) ? 'bottom up' : 'top down';
125 0 0 0     0 if ($code_type ne $type || $code_dir ne $dir) {
126             # Mixed types
127 0         0 my ($pkg2) = keys(%code_refs);
128 0         0 OIO::Code->die(
129             'message' => 'Inconsistent code types returned by :Automethods',
130             'Info' => "Class '$pkg' returned type $type($dir), and class '$pkg2' returned type $code_type($code_dir)");
131             }
132             } else {
133 0         0 $code_type = ':Chained';
134 0 0 0     0 $code_dir = ($dir && $dir =~ /BOT/i) ? 'bottom up' : 'top down';
135             }
136 0         0 $code_refs{$pkg} = $code;
137 0         0 next;
138             }
139              
140             # Unknown automethod code type
141             OIO::Code->die(
142 0         0 'message' => "Unknown :Automethod code type: $ctype",
143             'Info' => ":Automethod in package '$pkg' invoked for method '$method'");
144             }
145              
146 9 50       20 if ($code_type) {
147             # Mixed types
148 0         0 my ($pkg2) = keys(%code_refs);
149 0         0 OIO::Code->die(
150             'message' => 'Inconsistent code types returned by :Automethods',
151             'Info' => "Class '$pkg' returned an 'execute immediately' type, and class '$pkg2' returned type $code_type($code_dir)");
152             }
153              
154             # Just a one-shot - return it
155 9         63 return ($code);
156             }
157             }
158             }
159              
160 1 50       4 if ($code_type) {
161 1 50       4 my $tree = ($code_dir eq 'bottom up') ? $$GBL{'tree'}{'bu'} : $$GBL{'tree'}{'td'};
162 1 50       6 $code = ($code_type eq ':Cumulative')
163             ? create_CUMULATIVE($method, $tree, \%code_refs)
164             : create_CHAINED($method, $tree, \%code_refs);
165 1         5 return ($code);
166             }
167              
168 0         0 return; # Can't
169             };
170              
171              
172             *Object::InsideOut::isa = sub
173             {
174 161     161   5109 my ($thing, $type) = @_;
175              
176 161 50       345 return ('') if (! defined($thing));
177              
178             # Metadata call for classes
179 161 100       338 if (@_ == 1) {
180 4         13 return Object::InsideOut::meta($thing)->get_classes();
181             }
182              
183             # Workaround for Perl bug #47233
184 157 50       302 return ('') if (! defined($type));
185              
186             # Try original UNIVERSAL::isa()
187 157 100       243 if (my $isa = eval { $thing->Object::InsideOut::SUPER::isa($type) }) {
  157         783  
188 49         195 return ($isa);
189             }
190              
191             # Next, check heritage
192 108   66     172 foreach my $pkg (@{$$GBL{'tree'}{'bu'}{ref($thing) || $thing}}) {
  108         478  
193 155 100       352 if (exists($$GBL{'heritage'}{$pkg})) {
194 31         62 foreach my $pkg (keys(%{$$GBL{'heritage'}{$pkg}{'cl'}})) {
  31         112  
195 31 100       259 if (my $isa = $pkg->isa($type)) {
196 15         69 return ($isa);
197             }
198             }
199             }
200             }
201              
202 93         319 return (''); # Isn't
203             };
204              
205              
206             # Stub ourself out
207       14     *Object::InsideOut::install_UNIVERSAL = sub { };
208             }
209              
210             } # End of package's lexical scope
211              
212              
213             # Ensure correct versioning
214             ($Object::InsideOut::VERSION eq '4.05')
215             or die("Version mismatch\n");
216              
217             # EOF