Simplify the logic to throw an error on an undef or ! blessed delegatee.
[gitmo/Moose.git] / t / 020_attributes / 010_attribute_delegation.t
CommitLineData
452bac1b 1#!/usr/bin/perl
2
3use strict;
4use warnings;
5
6148c167 6use Test::More tests => 90;
452bac1b 7use Test::Exception;
8
7ff56534 9
452bac1b 10
f4f3e701 11# -------------------------------------------------------------------
12# HASH handles
13# -------------------------------------------------------------------
452bac1b 14# the canonical form of of the 'handles'
d03bd989 15# option is the hash ref mapping a
452bac1b 16# method name to the delegated method name
17
18{
19 package Foo;
452bac1b 20 use Moose;
21
d03bd989 22 has 'bar' => (is => 'rw', default => 10);
452bac1b 23
24 package Bar;
d03bd989 25 use Moose;
26
452bac1b 27 has 'foo' => (
28 is => 'rw',
29 default => sub { Foo->new },
30 handles => { 'foo_bar' => 'bar' }
31 );
32}
33
34my $bar = Bar->new;
35isa_ok($bar, 'Bar');
36
37ok($bar->foo, '... we have something in bar->foo');
38isa_ok($bar->foo, 'Foo');
39
01cd78f8 40my $meth = Bar->meta->get_method('foo_bar');
41isa_ok($meth, 'Moose::Meta::Method::Delegation');
42is($meth->associated_attribute->name, 'foo',
43 'associated_attribute->name for this method is foo');
a05f85c1 44
452bac1b 45is($bar->foo->bar, 10, '... bar->foo->bar returned the right default');
46
47can_ok($bar, 'foo_bar');
48is($bar->foo_bar, 10, '... bar->foo_bar delegated correctly');
49
f4f3e701 50# change the value ...
51
52$bar->foo->bar(30);
53
54# and make sure the delegation picks it up
55
56is($bar->foo->bar, 30, '... bar->foo->bar returned the right (changed) value');
57is($bar->foo_bar, 30, '... bar->foo_bar delegated correctly');
58
59# change the value through the delegation ...
60
61$bar->foo_bar(50);
62
d03bd989 63# and make sure everyone sees it
f4f3e701 64
65is($bar->foo->bar, 50, '... bar->foo->bar returned the right (changed) value');
66is($bar->foo_bar, 50, '... bar->foo_bar delegated correctly');
67
68# change the object we are delegating too
69
452bac1b 70my $foo = Foo->new(bar => 25);
71isa_ok($foo, 'Foo');
72
73is($foo->bar, 25, '... got the right foo->bar');
74
75lives_ok {
76 $bar->foo($foo);
77} '... assigned the new Foo to Bar->foo';
78
79is($bar->foo, $foo, '... assigned bar->foo with the new Foo');
80
81is($bar->foo->bar, 25, '... bar->foo->bar returned the right result');
82is($bar->foo_bar, 25, '... and bar->foo_bar delegated correctly again');
83
f4f3e701 84# -------------------------------------------------------------------
d03bd989 85# ARRAY handles
f4f3e701 86# -------------------------------------------------------------------
452bac1b 87# we also support an array based format
d03bd989 88# which assumes that the name is the same
452bac1b 89# on either end
90
91{
92 package Engine;
452bac1b 93 use Moose;
94
95 sub go { 'Engine::go' }
d03bd989 96 sub stop { 'Engine::stop' }
452bac1b 97
98 package Car;
d03bd989 99 use Moose;
100
452bac1b 101 has 'engine' => (
102 is => 'rw',
103 default => sub { Engine->new },
104 handles => [ 'go', 'stop' ]
105 );
106}
107
108my $car = Car->new;
109isa_ok($car, 'Car');
110
111isa_ok($car->engine, 'Engine');
112can_ok($car->engine, 'go');
113can_ok($car->engine, 'stop');
114
115is($car->engine->go, 'Engine::go', '... got the right value from ->engine->go');
116is($car->engine->stop, 'Engine::stop', '... got the right value from ->engine->stop');
117
118can_ok($car, 'go');
119can_ok($car, 'stop');
120
121is($car->go, 'Engine::go', '... got the right value from ->go');
122is($car->stop, 'Engine::stop', '... got the right value from ->stop');
123
f4f3e701 124# -------------------------------------------------------------------
d03bd989 125# REGEXP handles
f4f3e701 126# -------------------------------------------------------------------
452bac1b 127# and we support regexp delegation
128
129{
130 package Baz;
452bac1b 131 use Moose;
132
133 sub foo { 'Baz::foo' }
d03bd989 134 sub bar { 'Baz::bar' }
135 sub boo { 'Baz::boo' }
452bac1b 136
137 package Baz::Proxy1;
d03bd989 138 use Moose;
139
452bac1b 140 has 'baz' => (
141 is => 'ro',
142 isa => 'Baz',
143 default => sub { Baz->new },
144 handles => qr/.*/
145 );
d03bd989 146
452bac1b 147 package Baz::Proxy2;
d03bd989 148 use Moose;
149
452bac1b 150 has 'baz' => (
151 is => 'ro',
152 isa => 'Baz',
153 default => sub { Baz->new },
154 handles => qr/.oo/
d03bd989 155 );
156
452bac1b 157 package Baz::Proxy3;
d03bd989 158 use Moose;
159
452bac1b 160 has 'baz' => (
161 is => 'ro',
162 isa => 'Baz',
163 default => sub { Baz->new },
164 handles => qr/b.*/
d03bd989 165 );
452bac1b 166}
167
168{
169 my $baz_proxy = Baz::Proxy1->new;
170 isa_ok($baz_proxy, 'Baz::Proxy1');
171
172 can_ok($baz_proxy, 'baz');
173 isa_ok($baz_proxy->baz, 'Baz');
174
175 can_ok($baz_proxy, 'foo');
176 can_ok($baz_proxy, 'bar');
177 can_ok($baz_proxy, 'boo');
d03bd989 178
452bac1b 179 is($baz_proxy->foo, 'Baz::foo', '... got the right proxied return value');
180 is($baz_proxy->bar, 'Baz::bar', '... got the right proxied return value');
d03bd989 181 is($baz_proxy->boo, 'Baz::boo', '... got the right proxied return value');
452bac1b 182}
183{
184 my $baz_proxy = Baz::Proxy2->new;
185 isa_ok($baz_proxy, 'Baz::Proxy2');
186
187 can_ok($baz_proxy, 'baz');
188 isa_ok($baz_proxy->baz, 'Baz');
189
190 can_ok($baz_proxy, 'foo');
191 can_ok($baz_proxy, 'boo');
d03bd989 192
452bac1b 193 is($baz_proxy->foo, 'Baz::foo', '... got the right proxied return value');
d03bd989 194 is($baz_proxy->boo, 'Baz::boo', '... got the right proxied return value');
452bac1b 195}
196{
197 my $baz_proxy = Baz::Proxy3->new;
198 isa_ok($baz_proxy, 'Baz::Proxy3');
199
200 can_ok($baz_proxy, 'baz');
201 isa_ok($baz_proxy->baz, 'Baz');
202
203 can_ok($baz_proxy, 'bar');
204 can_ok($baz_proxy, 'boo');
d03bd989 205
452bac1b 206 is($baz_proxy->bar, 'Baz::bar', '... got the right proxied return value');
d03bd989 207 is($baz_proxy->boo, 'Baz::boo', '... got the right proxied return value');
452bac1b 208}
209
f4f3e701 210# -------------------------------------------------------------------
211# ROLE handles
212# -------------------------------------------------------------------
213
c84f324f 214{
215 package Foo::Bar;
216 use Moose::Role;
d03bd989 217
c84f324f 218 requires 'foo';
219 requires 'bar';
d03bd989 220
c84f324f 221 package Foo::Baz;
222 use Moose;
d03bd989 223
c84f324f 224 sub foo { 'Foo::Baz::FOO' }
225 sub bar { 'Foo::Baz::BAR' }
d03bd989 226 sub baz { 'Foo::Baz::BAZ' }
227
c84f324f 228 package Foo::Thing;
229 use Moose;
d03bd989 230
c84f324f 231 has 'thing' => (
d03bd989 232 is => 'rw',
c84f324f 233 isa => 'Foo::Baz',
234 handles => 'Foo::Bar',
235 );
236
237}
238
239{
240 my $foo = Foo::Thing->new(thing => Foo::Baz->new);
241 isa_ok($foo, 'Foo::Thing');
242 isa_ok($foo->thing, 'Foo::Baz');
d03bd989 243
c84f324f 244 ok($foo->meta->has_method('foo'), '... we have the method we expect');
245 ok($foo->meta->has_method('bar'), '... we have the method we expect');
d03bd989 246 ok(!$foo->meta->has_method('baz'), '... we dont have the method we expect');
247
248 is($foo->foo, 'Foo::Baz::FOO', '... got the right value');
c84f324f 249 is($foo->bar, 'Foo::Baz::BAR', '... got the right value');
d03bd989 250 is($foo->thing->baz, 'Foo::Baz::BAZ', '... got the right value');
c84f324f 251}
252
e902b1a5 253# -------------------------------------------------------------------
254# AUTOLOAD & handles
255# -------------------------------------------------------------------
256
257{
258 package Foo::Autoloaded;
259 use Moose;
260
261 sub AUTOLOAD {
262 my $self = shift;
263
264 my $name = our $AUTOLOAD;
265 $name =~ s/.*://; # strip fully-qualified portion
266
267 if (@_) {
268 return $self->{$name} = shift;
269 } else {
270 return $self->{$name};
271 }
272 }
273
274 package Bar::Autoloaded;
d03bd989 275 use Moose;
276
e902b1a5 277 has 'foo' => (
278 is => 'rw',
279 default => sub { Foo::Autoloaded->new },
280 handles => { 'foo_bar' => 'bar' }
281 );
d03bd989 282
e902b1a5 283 package Baz::Autoloaded;
d03bd989 284 use Moose;
285
e902b1a5 286 has 'foo' => (
287 is => 'rw',
288 default => sub { Foo::Autoloaded->new },
289 handles => ['bar']
d03bd989 290 );
291
e902b1a5 292 package Goorch::Autoloaded;
d03bd989 293 use Moose;
294
e902b1a5 295 ::dies_ok {
296 has 'foo' => (
297 is => 'rw',
298 default => sub { Foo::Autoloaded->new },
299 handles => qr/bar/
d03bd989 300 );
e902b1a5 301 } '... you cannot delegate to AUTOLOADED class with regexp';
302}
303
304# check HASH based delegation w/ AUTOLOAD
305
306{
307 my $bar = Bar::Autoloaded->new;
308 isa_ok($bar, 'Bar::Autoloaded');
309
310 ok($bar->foo, '... we have something in bar->foo');
311 isa_ok($bar->foo, 'Foo::Autoloaded');
312
313 # change the value ...
314
315 $bar->foo->bar(30);
316
317 # and make sure the delegation picks it up
318
319 is($bar->foo->bar, 30, '... bar->foo->bar returned the right (changed) value');
320 is($bar->foo_bar, 30, '... bar->foo_bar delegated correctly');
321
322 # change the value through the delegation ...
323
324 $bar->foo_bar(50);
325
d03bd989 326 # and make sure everyone sees it
e902b1a5 327
328 is($bar->foo->bar, 50, '... bar->foo->bar returned the right (changed) value');
329 is($bar->foo_bar, 50, '... bar->foo_bar delegated correctly');
330
331 # change the object we are delegating too
332
333 my $foo = Foo::Autoloaded->new;
334 isa_ok($foo, 'Foo::Autoloaded');
335
336 $foo->bar(25);
d03bd989 337
e902b1a5 338 is($foo->bar, 25, '... got the right foo->bar');
339
340 lives_ok {
341 $bar->foo($foo);
342 } '... assigned the new Foo to Bar->foo';
343
344 is($bar->foo, $foo, '... assigned bar->foo with the new Foo');
345
346 is($bar->foo->bar, 25, '... bar->foo->bar returned the right result');
347 is($bar->foo_bar, 25, '... and bar->foo_bar delegated correctly again');
348}
349
350# check ARRAY based delegation w/ AUTOLOAD
351
352{
353 my $baz = Baz::Autoloaded->new;
354 isa_ok($baz, 'Baz::Autoloaded');
355
356 ok($baz->foo, '... we have something in baz->foo');
357 isa_ok($baz->foo, 'Foo::Autoloaded');
358
359 # change the value ...
360
361 $baz->foo->bar(30);
362
363 # and make sure the delegation picks it up
364
365 is($baz->foo->bar, 30, '... baz->foo->bar returned the right (changed) value');
366 is($baz->bar, 30, '... baz->foo_bar delegated correctly');
367
368 # change the value through the delegation ...
369
370 $baz->bar(50);
371
d03bd989 372 # and make sure everyone sees it
e902b1a5 373
374 is($baz->foo->bar, 50, '... baz->foo->bar returned the right (changed) value');
375 is($baz->bar, 50, '... baz->foo_bar delegated correctly');
376
377 # change the object we are delegating too
378
379 my $foo = Foo::Autoloaded->new;
380 isa_ok($foo, 'Foo::Autoloaded');
381
382 $foo->bar(25);
d03bd989 383
e902b1a5 384 is($foo->bar, 25, '... got the right foo->bar');
385
386 lives_ok {
387 $baz->foo($foo);
388 } '... assigned the new Foo to Baz->foo';
389
390 is($baz->foo, $foo, '... assigned baz->foo with the new Foo');
391
392 is($baz->foo->bar, 25, '... baz->foo->bar returned the right result');
393 is($baz->bar, 25, '... and baz->foo_bar delegated correctly again');
394}
e1d6f0a3 395
396# Check that removing attributes removes their handles methods also.
397{
398 {
399 package Quux;
400 use Moose;
d03bd989 401 has foo => (
402 isa => 'Foo',
e1d6f0a3 403 default => sub { Foo->new },
404 handles => { 'foo_bar' => 'bar' }
405 );
406 }
407 my $i = Quux->new;
408 ok($i->meta->has_method('foo_bar'), 'handles method foo_bar is present');
409 $i->meta->remove_attribute('foo');
410 ok(!$i->meta->has_method('foo_bar'), 'handles method foo_bar is removed');
411}
412
ad5e9d0d 413# Make sure that a useful error message is thrown when the delegation target is
414# not an object
415{
6148c167 416 my $i = Bar->new(foo => undef);
417 throws_ok { $i->foo_bar } qr/is not defined/,
418 'useful error from unblessed reference';
419
420 my $j = Bar->new(foo => []);
421 throws_ok { $j->foo_bar } qr/is not an object \(got 'ARRAY/,
ad5e9d0d 422 'useful error from unblessed reference';
423}