File Coverage

blib/lib/Object/InsideOut/Foreign.pm
Criterion Covered Total %
statement 43 118 36.4
branch 11 60 18.3
condition 1 6 16.6
subroutine 5 7 71.4
pod n/a
total 60 191 31.4


line stmt bran cond sub pod time code
1             package Object::InsideOut; {
2              
3 4     4   19 use strict;
  4         4  
  4         97  
4 4     4   11 use warnings;
  4         4  
  4         83  
5 4     4   9 no warnings 'redefine';
  4         3  
  4         3807  
6              
7             # Installs foreign inheritance methods
8             sub inherit
9             {
10             my ($GBL, $call, @args) = @_;
11             push(@{$$GBL{'export'}}, qw(inherit heritage disinherit));
12             $$GBL{'init'} = 1;
13              
14             *Object::InsideOut::inherit = sub
15             {
16 4     4   36 my $self = shift;
17              
18             # Must be called as an object method
19 4         13 my $obj_class = Scalar::Util::blessed($self);
20 4 50       10 if (! $obj_class) {
21 0         0 OIO::Method->die('message' => q/'inherit' called as a class method/);
22             }
23              
24             # Inheritance takes place in caller's package
25 4         7 my $pkg = caller();
26              
27             # Restrict usage to inside class hierarchy
28 4 50       15 if (! $obj_class->isa($pkg)) {
29 0         0 OIO::Method->die('message' => "Can't call restricted method 'inherit' from class '$pkg'");
30             }
31              
32             # Flatten arg list
33 4         5 my (@arg_objs, $_arg);
34 4         10 while (defined($_arg = shift)) {
35 4 50       11 if (ref($_arg) eq 'ARRAY') {
36 0         0 push(@arg_objs, @{$_arg});
  0         0  
37             } else {
38 4         12 push(@arg_objs, $_arg);
39             }
40             }
41              
42             # Must be called with at least one arg
43 4 50       11 if (! @arg_objs) {
44 0         0 OIO::Args->die('message' => q/Missing arg(s) to '->inherit()'/);
45             }
46              
47             # Get 'heritage' field and 'classes' hash
48 4         7 my $herit = $$GBL{'heritage'};
49 4 50       11 if (! exists($$herit{$pkg})) {
50 0         0 create_heritage($pkg);
51             }
52 4         6 my $objects = $$herit{$pkg}{'obj'};
53 4         8 my $classes = $$herit{$pkg}{'cl'};
54              
55             # Process args
56 4 50       13 my $objs = exists($$objects{$$self}) ? $$objects{$$self} : [];
57 4         31 while (my $obj = shift(@arg_objs)) {
58             # Must be an object
59 3         11 my $arg_class = Scalar::Util::blessed($obj);
60 3 50       7 if (! $arg_class) {
61 0         0 OIO::Args->die('message' => q/Arg to '->inherit()' is not an object/);
62             }
63             # Must not be in class hierarchy
64 3 50 33     39 if ($obj_class->Object::InsideOut::SUPER::isa($arg_class) ||
65             $arg_class->isa($obj_class))
66             {
67 0         0 OIO::Args->die('message' => q/Args to '->inherit()' cannot be within class hierarchy/);
68             }
69             # Add arg to object list
70 3         89 push(@{$objs}, $obj);
  3         5  
71             # Add arg class to classes hash
72 3         9 $$classes{$arg_class} = undef;
73             }
74             # Add objects to heritage field
75 4         21 $self->set($objects, $objs);
76             };
77              
78              
79             *Object::InsideOut::heritage = sub
80             {
81 0     0   0 my $self = shift;
82              
83             # Must be called as an object method
84 0         0 my $obj_class = Scalar::Util::blessed($self);
85 0 0       0 if (! $obj_class) {
86 0         0 OIO::Method->die('message' => q/'heritage' called as a class method/);
87             }
88              
89             # Inheritance takes place in caller's package
90 0         0 my $pkg = caller();
91              
92             # Restrict usage to inside class hierarchy
93 0 0       0 if (! $obj_class->isa($pkg)) {
94 0         0 OIO::Method->die('message' => "Can't call restricted method 'heritage' from class '$pkg'");
95             }
96              
97             # Anything to return?
98 0 0 0     0 if (! exists($$GBL{'heritage'}{$pkg}) ||
99             ! exists($$GBL{'heritage'}{$pkg}{'obj'}{$$self}))
100             {
101 0         0 return;
102             }
103              
104 0         0 my @objs;
105 0 0       0 if (@_) {
106             # Filter by specified classes
107             @objs = grep {
108 0         0 my $obj = $_;
109 0         0 grep { ref($obj) eq $_ } @_
  0         0  
110 0         0 } @{$$GBL{'heritage'}{$pkg}{'obj'}{$$self}};
  0         0  
111             } else {
112             # Return entire list
113 0         0 @objs = @{$$GBL{'heritage'}{$pkg}{'obj'}{$$self}};
  0         0  
114             }
115              
116             # Return results
117 0 0       0 if (wantarray()) {
118 0         0 return (@objs);
119             }
120 0 0       0 if (@objs == 1) {
121 0         0 return ($objs[0]);
122             }
123 0         0 return (\@objs);
124             };
125              
126              
127             *Object::InsideOut::disinherit = sub
128             {
129 0     0   0 my $self = shift;
130              
131             # Must be called as an object method
132 0         0 my $class = Scalar::Util::blessed($self);
133 0 0       0 if (! $class) {
134 0         0 OIO::Method->die('message' => q/'disinherit' called as a class method/);
135             }
136              
137             # Disinheritance takes place in caller's package
138 0         0 my $pkg = caller();
139              
140             # Restrict usage to inside class hierarchy
141 0 0       0 if (! $class->isa($pkg)) {
142 0         0 OIO::Method->die('message' => "Can't call restricted method 'disinherit' from class '$pkg'");
143             }
144              
145             # Flatten arg list
146 0         0 my (@args, $_arg);
147 0         0 while (defined($_arg = shift)) {
148 0 0       0 if (ref($_arg) eq 'ARRAY') {
149 0         0 push(@args, @{$_arg});
  0         0  
150             } else {
151 0         0 push(@args, $_arg);
152             }
153             }
154              
155             # Must be called with at least one arg
156 0 0       0 if (! @args) {
157 0         0 OIO::Args->die('message' => q/Missing arg(s) to '->disinherit()'/);
158             }
159              
160             # Get 'heritage' field
161 0 0       0 if (! exists($$GBL{'heritage'}{$pkg})) {
162 0         0 OIO::Code->die(
163             'message' => 'Nothing to ->disinherit()',
164             'Info' => "Class '$pkg' is currently not inheriting from any foreign classes");
165             }
166 0         0 my $objects = $$GBL{'heritage'}{$pkg}{'obj'};
167              
168             # Get inherited objects
169 0 0       0 my @objs = exists($$objects{$$self}) ? @{$$objects{$$self}} : ();
  0         0  
170              
171             # Check that object is inheriting all args
172 0         0 foreach my $arg (@args) {
173 0 0       0 if (Scalar::Util::blessed($arg)) {
174             # Arg is an object
175 0 0       0 if (! grep { $_ == $arg } @objs) {
  0         0  
176 0         0 my $arg_class = ref($arg);
177 0         0 OIO::Args->die(
178             'message' => 'Cannot ->disinherit()',
179             'Info' => "Object is not inheriting from an object of class '$arg_class' inside class '$class'");
180             }
181             } else {
182             # Arg is a class
183 0 0       0 if (! grep { ref($_) eq $arg } @objs) {
  0         0  
184 0         0 OIO::Args->die(
185             'message' => 'Cannot ->disinherit()',
186             'Info' => "Object is not inheriting from an object of class '$arg' inside class '$class'");
187             }
188             }
189             }
190              
191             # Delete args from object
192 0         0 my @new_list = ();
193             OBJECT:
194 0         0 foreach my $obj (@objs) {
195 0         0 foreach my $arg (@args) {
196 0 0       0 if (Scalar::Util::blessed($arg)) {
197 0 0       0 if ($obj == $arg) {
198 0         0 next OBJECT;
199             }
200             } else {
201 0 0       0 if (ref($obj) eq $arg) {
202 0         0 next OBJECT;
203             }
204             }
205             }
206 0         0 push(@new_list, $obj);
207             }
208              
209             # Set new object list
210 0 0       0 if (@new_list) {
211 0         0 $self->set($objects, \@new_list);
212             } else {
213             # No objects left
214 0         0 delete($$objects{$$self});
215             }
216             };
217              
218              
219             *Object::InsideOut::create_heritage = sub
220             {
221             # Private
222 7     7   10 my $caller = caller();
223 7 50       13 if ($caller ne 'Object::InsideOut') {
224 0         0 OIO::Method->die('message' => "Can't call private subroutine 'Object::InsideOut::create_heritage' from class '$caller'");
225             }
226              
227 7         8 my $pkg = shift;
228              
229             # Check if 'heritage' already exists
230 7 50       16 if (exists($$GBL{'dump'}{'fld'}{$pkg}{'heritage'})) {
231 0         0 OIO::Attribute->die(
232             'message' => "Can't inherit into '$pkg'",
233             'Info' => "'heritage' already specified for another field using '$$GBL{'dump'}{'fld'}{$pkg}{'heritage'}{'src'}'");
234             }
235              
236             # Create the heritage field
237 7         9 my $objects = {};
238              
239             # Share the field, if applicable
240 7 50       15 if (is_sharing($pkg)) {
241 0         0 threads::shared::share($objects)
242             }
243              
244             # Save the field's ref
245 7         6 push(@{$$GBL{'fld'}{'ref'}{$pkg}}, $objects);
  7         17  
246              
247             # Save info for ->dump()
248 7         14 $$GBL{'dump'}{'fld'}{$pkg}{'heritage'} = {
249             fld => $objects,
250             src => 'Inherit'
251             };
252              
253             # Save heritage info
254 7         12 $$GBL{'heritage'}{$pkg} = {
255             obj => $objects,
256             cl => {}
257             };
258              
259             # Set up UNIVERSAL::can/isa to handle foreign inheritance
260 7         14 install_UNIVERSAL();
261             };
262              
263              
264             # Do the original call
265             @_ = @args;
266             goto &$call;
267             }
268              
269             } # End of package's lexical scope
270              
271              
272             # Ensure correct versioning
273             ($Object::InsideOut::VERSION eq '4.04')
274             or die("Version mismatch\n");
275              
276             # EOF