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   47 use strict;
  8         61  
  8         205  
4 8     8   25 use warnings;
  8         11  
  8         180  
5 8     8   63 no warnings 'redefine';
  8         15  
  8         2530  
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   8861 my ($thing, $method) = @_;
16              
17 27 50       55 return if (! defined($thing));
18              
19             # Metadata call for methods
20 27 100       58 if (@_ == 1) {
21 4         11 my $meths = Object::InsideOut::meta(shift)->get_methods();
22 4 100       12 return (wantarray()) ? (keys(%$meths)) : [ keys(%$meths) ];
23             }
24              
25 23 50       41 return if (! defined($method));
26              
27             # First, try the original UNIVERSAL::can()
28 23         15 my $code;
29 23 100       40 if ($method =~ /^SUPER::/) {
30             # Superclass WRT caller
31 1         2 my $caller = caller();
32 1         1 eval { $code = $thing->Object::InsideOut::SUPER::can($caller.'::'.$method) };
  1         9  
33             } else {
34 22         23 eval { $code = $thing->Object::InsideOut::SUPER::can($method) };
  22         126  
35             }
36 23 100       43 if ($code) {
37 9         19 return ($code);
38             }
39              
40             # Handle various calling methods
41 14         11 my ($class, $super);
42 14 100       36 if ($method !~ /::/) {
    50          
    50          
43             # Ordinary method check
44             # $obj->can('x');
45 13   66     47 $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         2 $class = caller();
56 1         3 $method =~ s/SUPER:://;
57 1         2 $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         18 my $heritage = $$GBL{'heritage'};
67 14         20 my $automethods = $$GBL{'sub'}{'auto'};
68              
69             # Next, check with heritage objects and Automethods
70 14         11 my ($code_type, $code_dir, %code_refs);
71 14         14 foreach my $pkg (@{$$GBL{'tree'}{'bu'}{$class}}) {
  14         38  
72             # Skip self's class if SUPER
73 28 100 100     78 if ($super && $class eq $pkg) {
74 1         1 next;
75             }
76              
77             # Check heritage
78 27 100       54 if (exists($$heritage{$pkg})) {
79 8     8   34 no warnings;
  8         8  
  8         5290  
80 4         2 foreach my $pkg2 (keys(%{$$heritage{$pkg}{'cl'}})) {
  4         9  
81 4 50       12 if ($code = $pkg2->can($method)) {
82 4         12 return ($code);
83             }
84             }
85             }
86              
87             # Check with the Automethods
88 23 100       48 if (my $automethod = $$automethods{$pkg}) {
89             # Call the Automethod to get a code ref
90 14         14 local $CALLER::_ = $_;
91 14         10 local $_ = $method;
92 14         45 local $SIG{'__DIE__'} = 'OIO::trap';
93 14 100       26 if (my ($code, $ctype) = $automethod->($thing)) {
94 12 50       145 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       19 if (defined($ctype)) {
102 3         10 my ($type, $dir) = $ctype =~ /(\w+)(?:[(]\s*(.*)\s*[)])?/;
103 3 50 33     13 if ($type && $type =~ /CUM/i) {
104 3 100       4 if ($code_type) {
105 2         3 $type = ':Cumulative';
106 2 50 33     6 $dir = ($dir && $dir =~ /BOT/i) ? 'bottom up' : 'top down';
107 2 50 33     6 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         1 $code_type = ':Cumulative';
116 1 50 33     4 $code_dir = ($dir && $dir =~ /BOT/i) ? 'bottom up' : 'top down';
117             }
118 3         7 $code_refs{$pkg} = $code;
119 3         9 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       14 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         37 return ($code);
156             }
157             }
158             }
159              
160 1 50       3 if ($code_type) {
161 1 50       4 my $tree = ($code_dir eq 'bottom up') ? $$GBL{'tree'}{'bu'} : $$GBL{'tree'}{'td'};
162 1 50       4 $code = ($code_type eq ':Cumulative')
163             ? create_CUMULATIVE($method, $tree, \%code_refs)
164             : create_CHAINED($method, $tree, \%code_refs);
165 1         4 return ($code);
166             }
167              
168 0         0 return; # Can't
169             };
170              
171              
172             *Object::InsideOut::isa = sub
173             {
174 161     161   2260 my ($thing, $type) = @_;
175              
176 161 50       227 return ('') if (! defined($thing));
177              
178             # Metadata call for classes
179 161 100       233 if (@_ == 1) {
180 4         9 return Object::InsideOut::meta($thing)->get_classes();
181             }
182              
183             # Workaround for Perl bug #47233
184 157 50       184 return ('') if (! defined($type));
185              
186             # Try original UNIVERSAL::isa()
187 157 100       124 if (my $isa = eval { $thing->Object::InsideOut::SUPER::isa($type) }) {
  157         563  
188 49         140 return ($isa);
189             }
190              
191             # Next, check heritage
192 108   66     85 foreach my $pkg (@{$$GBL{'tree'}{'bu'}{ref($thing) || $thing}}) {
  108         359  
193 155 100       253 if (exists($$GBL{'heritage'}{$pkg})) {
194 31         21 foreach my $pkg (keys(%{$$GBL{'heritage'}{$pkg}{'cl'}})) {
  31         63  
195 31 100       131 if (my $isa = $pkg->isa($type)) {
196 15         48 return ($isa);
197             }
198             }
199             }
200             }
201              
202 93         244 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.04')
215             or die("Version mismatch\n");
216              
217             # EOF