For class_types, explicitly state that the value is not an instance of
[gitmo/Moose.git] / t / 600_todo_tests / 002_various_role_shit.t
CommitLineData
bbc9f1c6 1#!/usr/bin/perl
2
3use strict;
4use warnings;
5
d443cad0 6use Test::More tests => 40;
bbc9f1c6 7use Test::Exception;
d443cad0 8use Test::Output;
bbc9f1c6 9
10sub req_or_has ($$) {
11 my ( $role, $method ) = @_;
12 local $Test::Builder::Level = $Test::Builder::Level + 1;
13 if ( $role ) {
68b6146c 14 ok(
15 $role->has_method($method) || $role->requires_method($method),
16 $role->name . " has or requires method $method"
17 );
bbc9f1c6 18 } else {
19 fail("role has or requires method $method");
20 }
21}
22
1344fd47 23{
24 package Bar;
25 use Moose::Role;
bbc9f1c6 26
27 # this role eventually adds three methods, qw(foo bar xxy), but only one is
28 # known when it's still a role
29
1344fd47 30 has foo => ( is => "rw" );
bbc9f1c6 31
32 has gorch => ( reader => "bar" );
33
34 sub xxy { "BAAAD" }
35
36 package Gorch;
37 use Moose::Role;
38
39 # similarly this role gives attr and gorch_method
40
41 has attr => ( is => "rw" );
42
43 sub gorch_method { "gorch method" }
44
45 around dandy => sub { shift->(@_) . "bar" };
46
47 package Quxx;
48 use Moose;
49
50 sub dandy { "foo" }
51
52 # this object will be used in an attr of Foo to test that Foo can do the
53 # Gorch interface
54
55 with qw(Gorch);
56
57 package Dancer;
58 use Moose::Role;
59
60 requires "twist";
61
62 package Dancer::Ballerina;
63 use Moose;
64
65 with qw(Dancer);
66
67 sub twist { }
68
69 sub pirouette { }
70
71 package Dancer::Robot;
72 use Moose::Role;
73
74 # this doesn't fail but it produces a requires in the role
75 # the order doesn't matter
76 has twist => ( is => "rw" );
77 ::lives_ok { with qw(Dancer) };
78
79 package Dancer::Something;
80 use Moose;
81
82 # this fail even though the method already exists
83
84 has twist => ( is => "rw" );
85
86 {
bbc9f1c6 87 ::lives_ok { with qw(Dancer) };
88 }
89
90 package Dancer::80s;
91 use Moose;
92
93 # this should pass because ::Robot has the attribute to fill in the requires
94 # but due to the deferrence logic that doesn't actually work
95 {
96 local our $TODO = "attribute accessor in role doesn't satisfy role requires";
97 ::lives_ok { with qw(Dancer::Robot) };
98 }
99
1344fd47 100 package Foo;
101 use Moose;
bbc9f1c6 102
d443cad0 103 ::stderr_like {
104 with qw(Bar);
105 } qr/The Foo class has implicitly overridden the method \(xxy\) from role Bar\./;
bbc9f1c6 106
107 has oink => (
108 is => "rw",
109 handles => "Gorch", # should handles take the same arguments as 'with'? Meta::Role::Application::Delegation?
110 default => sub { Quxx->new },
111 );
112
113 has dancer => (
114 is => "rw",
115 does => "Dancer",
116 handles => "Dancer",
117 default => sub { Dancer::Ballerina->new },
118 );
119
1344fd47 120 sub foo { 42 }
bbc9f1c6 121
122 sub bar { 33 }
123
124 sub xxy { 7 }
e953aaf5 125
126 package Tree;
127 use Moose::Role;
128
129 has bark => ( is => "rw" );
130
131 package Dog;
132 use Moose::Role;
133
134 sub bark { warn "woof!" };
135
136 package EntPuppy;
137 use Moose;
138
139 {
140 local our $TODO = "attrs and methods from a role should clash";
d443cad0 141 local $SIG{__WARN__} = sub { 'Ignore!' };
e953aaf5 142 ::dies_ok { with qw(Tree Dog) }
143 }
bbc9f1c6 144}
145
146# these fail because of the deferral logic winning over actual methods
147# this might be tricky to fix due to the 'sub foo {}; has foo => ( )' hack
148# we've been doing for a long while, though I doubt people relied on it for
149# anything other than fulfilling 'requires'
150{
151 local $TODO = "attributes from role overwrite class methods";
152 is( Foo->new->foo, 42, "attr did not zap overriding method" );
153 is( Foo->new->bar, 33, "attr did not zap overriding method" );
154}
155is( Foo->new->xxy, 7, "method did not zap overriding method" ); # duh
156
157# these pass, simple delegate
158# mostly they are here to contrast the next blck
159can_ok( Foo->new->oink, "dandy" );
160can_ok( Foo->new->oink, "attr" );
161can_ok( Foo->new->oink, "gorch_method" );
162
163ok( Foo->new->oink->does("Gorch"), "Quxx does Gorch" );
164
165
166# these are broken because 'attr' is not technically part of the interface
167can_ok( Foo->new, "gorch_method" );
168{
169 local $TODO = "accessor methods from a role are omitted in handles role";
170 can_ok( Foo->new, "attr" );
171}
172
173{
174 local $TODO = "handles role doesn't add the role to the ->does of the delegate's parent class";
175 ok( Foo->new->does("Gorch"), "Foo does Gorch" );
176}
177
178
179# these work
180can_ok( Foo->new->dancer, "pirouette" );
181can_ok( Foo->new->dancer, "twist" );
182
183can_ok( Foo->new, "twist" );
184ok( !Foo->new->can("pirouette"), "can't pirouette, not part of the iface" );
185
186{
187 local $TODO = "handles role doesn't add the role to the ->does of the delegate's parent class";
188 ok( Foo->new->does("Dancer") );
189}
190
191
192
193
194my $gorch = Gorch->meta;
195
196isa_ok( $gorch, "Moose::Meta::Role" );
197
198ok( $gorch->has_attribute("attr"), "has attribute 'attr'" );
199
200{
201 local $TODO = "role attribute isn't a meta attribute yet";
202 isa_ok( $gorch->get_attribute("attr"), "Moose::Meta::Attribute" );
203}
204
205req_or_has($gorch, "gorch_method");
206ok( $gorch->has_method("gorch_method"), "has_method gorch_method" );
207ok( !$gorch->requires_method("gorch_method"), "requires gorch method" );
9cde8a2f 208isa_ok( $gorch->get_method("gorch_method"), "Moose::Meta::Method" );
bbc9f1c6 209
210{
211 local $TODO = "method modifier doesn't yet create a method requirement or meta object";
212 req_or_has($gorch, "dandy" );
213
214 # this specific test is maybe not backwards compat, but in theory it *does*
215 # require that method to exist
216 ok( $gorch->requires_method("dandy"), "requires the dandy method for the modifier" );
217}
218
219{
220 local $TODO = "attribute related methods are not yet known by the role";
221 # we want this to be a part of the interface, somehow
222 req_or_has($gorch, "attr");
223 ok( $gorch->has_method("attr"), "has_method attr" );
224 isa_ok( $gorch->get_method("attr"), "Moose::Meta::Method" );
225 isa_ok( $gorch->get_method("attr"), "Moose::Meta::Method::Accessor" );
226}
227
228my $robot = Dancer::Robot->meta;
229
230isa_ok( $robot, "Moose::Meta::Role" );
231
232ok( $robot->has_attribute("twist"), "has attr 'twist'" );
233
234{
235 local $TODO = "role attribute isn't a meta attribute yet";
236 isa_ok( $robot->get_attribute("twist"), "Moose::Meta::Attribute" );
237}
238
239{
bbc9f1c6 240 req_or_has($robot, "twist");
a632beb5 241
242 local $TODO = "attribute related methods are not yet known by the role";
bbc9f1c6 243 ok( $robot->has_method("twist"), "has twist method" );
244 isa_ok( $robot->get_method("twist"), "Moose::Meta::Method" );
245 isa_ok( $robot->get_method("twist"), "Moose::Meta::Method::Accessor" );
246}
247
248__END__
249
250I think Attribute needs to be refactored in some way to better support roles.
251
252There are several possible ways to do this, all of them seem plausible to me.
253
254The first approach would be to change the attribute class to allow it to be
255queried about the methods it would install.
256
257Then we instantiate the attribute in the role, and instead of deferring the
258arguments, we just make an C<unpack>ish method.
259
260Then we can interrogate the attr when adding it to the role, and generate stub
261methods for all the methods it would produce.
262
263A second approach is kinda like the Immutable hack: wrap the attr in an
264anonmyous class that disables part of its interface.
265
266A third method would be to create an Attribute::Partial object that would
267provide a more role-ish behavior, and to do this independently of the actual
268Attribute class.
269
270Something similar can be done for method modifiers, but I think that's even simpler.
271
272
273
274The benefits of doing this are:
275
276* Much better introspection of roles
277
278* More correctness in many cases (in my opinion anyway)
279
280* More roles are more usable as interface declarations, without having to split
281 them into two pieces (one for the interface with a bunch of requires(), and
282 another for the actual impl with the problematic attrs (and stub methods to
283 fix the accessors) and method modifiers (dunno if this can even work at all)
284
285