Add a test for a constant with an undef value
[gitmo/Class-MOP.git] / t / 003_methods.t
CommitLineData
0882828e 1use strict;
2use warnings;
3
86a4d873 4use Test::More;
0882828e 5use Test::Exception;
6
16e960bd 7use Scalar::Util qw/reftype/;
2e13df84 8use Sub::Name;
16e960bd 9
7d682ca2 10use Class::MOP;
11use Class::MOP::Class;
12use Class::MOP::Method;
0882828e 13
49ca2e97 14{
15 # This package tries to test &has_method as exhaustively as
16 # possible. More corner cases are welcome :)
0882828e 17 package Foo;
49ca2e97 18
0882828e 19 # import a sub
49ca2e97 20 use Scalar::Util 'blessed';
21
823a5d31 22 sub pie;
23 sub cake ();
24
bfe4d0fc 25 use constant FOO_CONSTANT => 'Foo-CONSTANT';
49ca2e97 26
0882828e 27 # define a sub in package
49ca2e97 28 sub bar {'Foo::bar'}
60d90bbc 29 *baz = \&bar;
49ca2e97 30
46b23b44 31 # create something with the typeglob inside the package
49ca2e97 32 *baaz = sub {'Foo::baaz'};
bfe4d0fc 33
49ca2e97 34 { # method named with Sub::Name inside the package scope
bfe4d0fc 35 no strict 'refs';
49ca2e97 36 *{'Foo::floob'} = Sub::Name::subname 'floob' => sub {'!floob!'};
bfe4d0fc 37 }
38
60d90bbc 39 # We hateses the "used only once" warnings
49ca2e97 40 {
46b23b44 41 my $temp1 = \&Foo::baz;
49ca2e97 42 my $temp2 = \&Foo::baaz;
46b23b44 43 }
49ca2e97 44
16e960bd 45 package OinkyBoinky;
46 our @ISA = "Foo";
49ca2e97 47
48 sub elk {'OinkyBoinky::elk'}
60d90bbc 49
50 package main;
49ca2e97 51
60d90bbc 52 sub Foo::blah { $_[0]->Foo::baz() }
49ca2e97 53
60d90bbc 54 {
55 no strict 'refs';
49ca2e97 56 *{'Foo::bling'} = sub {'$$Bling$$'};
57 *{'Foo::bang'} = Sub::Name::subname 'Foo::bang' => sub {'!BANG!'};
58 *{'Foo::boom'} = Sub::Name::subname 'boom' => sub {'!BOOM!'};
59
60 eval "package Foo; sub evaled_foo { 'Foo::evaled_foo' }";
60d90bbc 61 }
0882828e 62}
63
bfe4d0fc 64my $Foo = Class::MOP::Class->initialize('Foo');
0882828e 65
49ca2e97 66ok( $Foo->has_method('pie'), '... got the method stub pie' );
67ok( $Foo->has_method('cake'), '... got the constant method stub cake' );
823a5d31 68
49ca2e97 69my $foo = sub {'Foo::foo'};
0882828e 70
49ca2e97 71ok( !UNIVERSAL::isa( $foo, 'Class::MOP::Method' ),
72 '... our method is not yet blessed' );
de19f115 73
0882828e 74lives_ok {
49ca2e97 75 $Foo->add_method( 'foo' => $foo );
76}
77'... we added the method successfully';
0882828e 78
7855ddba 79my $foo_method = $Foo->get_method('foo');
de19f115 80
49ca2e97 81isa_ok( $foo_method, 'Class::MOP::Method' );
7855ddba 82
49ca2e97 83is( $foo_method->name, 'foo', '... got the right name for the method' );
84is( $foo_method->package_name, 'Foo',
85 '... got the right package name for the method' );
de19f115 86
49ca2e97 87ok( $Foo->has_method('foo'),
88 '... Foo->has_method(foo) (defined with Sub::Name)' );
bfe4d0fc 89
49ca2e97 90is( $Foo->get_method('foo')->body, $foo,
91 '... Foo->get_method(foo) == \&foo' );
92is( $Foo->get_method('foo')->execute, 'Foo::foo',
93 '... _method_foo->execute returns "Foo::foo"' );
94is( Foo->foo(), 'Foo::foo', '... Foo->foo() returns "Foo::foo"' );
bfe4d0fc 95
96# now check all our other items ...
97
49ca2e97 98ok( $Foo->has_method('FOO_CONSTANT'),
99 '... not Foo->has_method(FOO_CONSTANT) (defined w/ use constant)' );
100ok( !$Foo->has_method('bling'),
101 '... not Foo->has_method(bling) (defined in main:: using symbol tables (no Sub::Name))'
102);
46b23b44 103
49ca2e97 104ok( $Foo->has_method('bar'), '... Foo->has_method(bar) (defined in Foo)' );
105ok( $Foo->has_method('baz'),
106 '... Foo->has_method(baz) (typeglob aliased within Foo)' );
107ok( $Foo->has_method('baaz'),
108 '... Foo->has_method(baaz) (typeglob aliased within Foo)' );
109ok( $Foo->has_method('floob'),
110 '... Foo->has_method(floob) (defined in Foo:: using symbol tables and Sub::Name w/out package name)'
111);
112ok( $Foo->has_method('blah'),
113 '... Foo->has_method(blah) (defined in main:: using fully qualified package name)'
114);
115ok( $Foo->has_method('bang'),
116 '... Foo->has_method(bang) (defined in main:: using symbol tables and Sub::Name)'
117);
118ok( $Foo->has_method('evaled_foo'),
119 '... Foo->has_method(evaled_foo) (evaled in main::)' );
0882828e 120
16e960bd 121my $OinkyBoinky = Class::MOP::Class->initialize('OinkyBoinky');
122
49ca2e97 123ok( $OinkyBoinky->has_method('elk'),
124 "the method 'elk' is defined in OinkyBoinky" );
16e960bd 125
49ca2e97 126ok( !$OinkyBoinky->has_method('bar'),
127 "the method 'bar' is not defined in OinkyBoinky" );
16e960bd 128
49ca2e97 129ok( my $bar = $OinkyBoinky->find_method_by_name('bar'),
130 "but if you look in the inheritence chain then 'bar' does exist" );
16e960bd 131
49ca2e97 132is( reftype( $bar->body ), "CODE", "the returned value is a code ref" );
16e960bd 133
de19f115 134# calling get_method blessed them all
49ca2e97 135for my $method_name (
136 qw/baaz
137 bar
138 baz
139 floob
140 blah
141 bang
142 evaled_foo
143 FOO_CONSTANT/
144 ) {
145 isa_ok( $Foo->get_method($method_name), 'Class::MOP::Method' );
7855ddba 146 {
147 no strict 'refs';
49ca2e97 148 is( $Foo->get_method($method_name)->body,
149 \&{ 'Foo::' . $method_name },
150 '... body matches CODE ref in package for ' . $method_name );
7855ddba 151 }
152}
de19f115 153
49ca2e97 154for my $method_name (
155 qw/
156 bling
157 /
158 ) {
159 is( ref( $Foo->get_package_symbol( '&' . $method_name ) ), 'CODE',
160 '... got the __ANON__ methods' );
46b23b44 161 {
162 no strict 'refs';
49ca2e97 163 is( $Foo->get_package_symbol( '&' . $method_name ),
164 \&{ 'Foo::' . $method_name },
165 '... symbol matches CODE ref in package for ' . $method_name );
46b23b44 166 }
167}
168
49ca2e97 169ok( !$Foo->has_method('blessed'),
170 '... !Foo->has_method(blessed) (imported into Foo)' );
171ok( !$Foo->has_method('boom'),
172 '... !Foo->has_method(boom) (defined in main:: using symbol tables and Sub::Name w/out package name)'
173);
0882828e 174
49ca2e97 175ok( !$Foo->has_method('not_a_real_method'),
176 '... !Foo->has_method(not_a_real_method) (does not exist)' );
177is( $Foo->get_method('not_a_real_method'), undef,
178 '... Foo->get_method(not_a_real_method) == undef' );
bfe4d0fc 179
c9b8b7f9 180is_deeply(
181 [ sort $Foo->get_method_list ],
49ca2e97 182 [qw(FOO_CONSTANT baaz bang bar baz blah cake evaled_foo floob foo pie)],
183 '... got the right method list for Foo'
184);
c9b8b7f9 185
a5eca695 186is_deeply(
77ff94df 187 [ sort { $a->name cmp $b->name } $Foo->get_all_methods() ],
a5eca695 188 [
49ca2e97 189 map { $Foo->get_method($_) }
190 qw(
d7d3f3cb 191 FOO_CONSTANT
49ca2e97 192 baaz
193 bang
194 bar
195 baz
196 blah
c3b8d5ad 197 cake
49ca2e97 198 evaled_foo
199 floob
a5eca695 200 foo
c3b8d5ad 201 pie
49ca2e97 202 )
a5eca695 203 ],
49ca2e97 204 '... got the right list of applicable methods for Foo'
205);
a5eca695 206
49ca2e97 207is( $Foo->remove_method('foo')->body, $foo, '... removed the foo method' );
208ok( !$Foo->has_method('foo'),
209 '... !Foo->has_method(foo) we just removed it' );
c9b8b7f9 210dies_ok { Foo->foo } '... cannot call Foo->foo because it is not there';
211
212is_deeply(
213 [ sort $Foo->get_method_list ],
49ca2e97 214 [qw(FOO_CONSTANT baaz bang bar baz blah cake evaled_foo floob pie)],
215 '... got the right method list for Foo'
216);
c9b8b7f9 217
49ca2e97 218# ... test our class creator
bfe4d0fc 219
220my $Bar = Class::MOP::Class->create(
99b84658 221 package => 'Bar',
49ca2e97 222 superclasses => ['Foo'],
3976fb78 223 methods => {
49ca2e97 224 foo => sub {'Bar::foo'},
225 bar => sub {'Bar::bar'},
3976fb78 226 }
227);
49ca2e97 228isa_ok( $Bar, 'Class::MOP::Class' );
bfe4d0fc 229
49ca2e97 230ok( $Bar->has_method('foo'), '... Bar->has_method(foo)' );
231ok( $Bar->has_method('bar'), '... Bar->has_method(bar)' );
bfe4d0fc 232
49ca2e97 233is( Bar->foo, 'Bar::foo', '... Bar->foo == Bar::foo' );
234is( Bar->bar, 'Bar::bar', '... Bar->bar == Bar::bar' );
bfe4d0fc 235
236lives_ok {
49ca2e97 237 $Bar->add_method( 'foo' => sub {'Bar::foo v2'} );
238}
239'... overwriting a method is fine';
bfe4d0fc 240
49ca2e97 241is_deeply( [ Class::MOP::get_code_info( $Bar->get_method('foo')->body ) ],
242 [ "Bar", "foo" ], "subname applied to anonymous method" );
bfa0d9f8 243
49ca2e97 244ok( $Bar->has_method('foo'), '... Bar-> (still) has_method(foo)' );
245is( Bar->foo, 'Bar::foo v2', '... Bar->foo == "Bar::foo v2"' );
c9b8b7f9 246
247is_deeply(
248 [ sort $Bar->get_method_list ],
49ca2e97 249 [qw(bar foo meta)],
250 '... got the right method list for Bar'
251);
252
a5eca695 253is_deeply(
77ff94df 254 [ sort { $a->name cmp $b->name } $Bar->get_all_methods() ],
a5eca695 255 [
77ff94df 256 $Foo->get_method('FOO_CONSTANT'),
257 $Foo->get_method('baaz'),
258 $Foo->get_method('bang'),
259 $Bar->get_method('bar'),
49ca2e97 260 (
261 map { $Foo->get_method($_) }
262 qw(
263 baz
264 blah
265 cake
266 evaled_foo
267 floob
268 )
269 ),
77ff94df 270 $Bar->get_method('foo'),
271 $Bar->get_method('meta'),
c3b8d5ad 272 $Foo->get_method('pie'),
a5eca695 273 ],
49ca2e97 274 '... got the right list of applicable methods for Bar'
275);
a5eca695 276
7d682ca2 277my $method = Class::MOP::Method->wrap(
278 name => 'objecty',
279 package_name => 'Whatever',
280 body => sub {q{I am an object, and I feel an object's pain}},
281);
282
283Bar->meta->add_method( $method->name, $method );
284
285my $new_method = Bar->meta->get_method('objecty');
a5eca695 286
49ca2e97 287isnt( $method, $new_method,
288 'add_method clones method objects as they are added' );
289is( $new_method->original_method, $method,
55039f82 290 '... the cloned method has the correct original method' )
291 or diag $new_method->dump;
c41df5a9 292
293{
c41df5a9 294 package CustomAccessor;
295
296 use Class::MOP;
297
298 my $meta = Class::MOP::Class->initialize(__PACKAGE__);
299
300 $meta->add_attribute(
301 foo => (
302 accessor => 'foo',
303 )
304 );
305
306 {
f675e129 307 no warnings 'redefine', 'once';
c41df5a9 308 *foo = sub {
309 my $self = shift;
310 $self->{custom_store} = $_[0];
311 };
312 }
313
314 $meta->add_around_method_modifier(
315 'foo',
316 sub {
317 my $orig = shift;
318 $orig->(@_);
319 }
320 );
321
5327fc78 322 sub new {
323 return bless {}, shift;
324 }
c41df5a9 325}
326
327{
328 my $o = CustomAccessor->new;
329 my $str = 'string';
330
331 $o->foo($str);
332
49ca2e97 333 is(
334 $o->{custom_store}, $str,
5327fc78 335 'Custom glob-assignment-created accessor still has method modifier'
49ca2e97 336 );
c41df5a9 337}
5327fc78 338
339{
340 # Since the sub reference below is not a closure, Perl caches it and uses
341 # the same reference each time through the loop. See RT #48985 for the
342 # bug.
343 foreach my $ns ( qw( Foo2 Bar2 Baz2 ) ) {
344 my $meta = Class::MOP::Class->create($ns);
345
346 my $sub = sub { };
347
348 $meta->add_method( 'foo', $sub );
349
350 my $method = $meta->get_method('foo');
351 ok( $method, 'Got the foo method back' );
352 }
353}
86a4d873 354
7f9ef61e 355{
356 package HasConstants;
357
adb2de7d 358 use constant FOO => 1;
359 use constant BAR => [];
360 use constant BAZ => {};
361 use constant UNDEF => undef;
7f9ef61e 362
363 sub quux {1}
364 sub thing {1}
365}
366
367my $HC = Class::MOP::Class->initialize('HasConstants');
368
369is_deeply(
370 [ sort $HC->get_method_list ],
adb2de7d 371 [qw( BAR BAZ FOO UNDEF quux thing )],
7f9ef61e 372 'get_method_list handles constants properly'
373);
374
375is_deeply(
376 [ sort map { $_->name } $HC->_get_local_methods ],
adb2de7d 377 [qw( BAR BAZ FOO UNDEF quux thing )],
7f9ef61e 378 '_get_local_methods handles constants properly'
379);
380
381
86a4d873 382done_testing;