Mouse::Util::does_role() respects $thing->does() method
[gitmo/Mouse.git] / lib / Mouse / Util.pm
CommitLineData
4093c859 1package Mouse::Util;
bc69ee88 2use Mouse::Exporter; # enables strict and warnings
6d28c5cf 3
bf930151 4# Note that those which don't exist here are defined in XS or Mouse::PurePerl
5
a4b15169 6# must be here because it will be refered by other modules loaded
7sub get_linear_isa($;$); ## no critic
ba153b33 8
a4b15169 9# must be here because it will called in Mouse::Exporter
10sub install_subroutines {
1194aede 11 my $into = shift;
12
13 while(my($name, $code) = splice @_, 0, 2){
14 no strict 'refs';
15 no warnings 'once', 'redefine';
16 use warnings FATAL => 'uninitialized';
17 *{$into . '::' . $name} = \&{$code};
18 }
19 return;
20}
21
df6dd016 22BEGIN{
8aba926d 23 # This is used in Mouse::PurePerl
24 Mouse::Exporter->setup_import_methods(
25 as_is => [qw(
26 find_meta
27 does_role
28 resolve_metaclass_alias
29 apply_all_roles
30 english_list
31
32 load_class
33 is_class_loaded
34
35 get_linear_isa
36 get_code_info
37
38 get_code_package
39 get_code_ref
40
41 not_supported
42
823419c5 43 does meta throw_error dump
8aba926d 44 )],
45 groups => {
46 default => [], # export no functions by default
47
48 # The ':meta' group is 'use metaclass' for Mouse
823419c5 49 meta => [qw(does meta dump throw_error)],
8aba926d 50 },
51 );
52
14cf9b5a 53 our $VERSION = '0.95';
df6dd016 54
bdef60b4 55 my $xs = !(defined(&is_valid_class_name) || $ENV{MOUSE_PUREPERL} || $ENV{PERL_ONLY});
df6dd016 56
4bef84ef 57 # Because Mouse::Util is loaded first in all the Mouse sub-modules,
58 # XSLoader must be placed here, not in Mouse.pm.
db5e4409 59 if($xs){
34bdc46a 60 # XXX: XSLoader tries to get the object path from caller's file name
61 # $hack_mouse_file fools its mechanism
34bdc46a 62 (my $hack_mouse_file = __FILE__) =~ s/.Util//; # .../Mouse/Util.pm -> .../Mouse.pm
db5e4409 63 $xs = eval sprintf("#line %d %s\n", __LINE__, $hack_mouse_file) . q{
4bef84ef 64 local $^W = 0; # workaround 'redefine' warning to &install_subroutines
df6dd016 65 require XSLoader;
66 XSLoader::load('Mouse', $VERSION);
923a04ba 67 Mouse::Util->import({ into => 'Mouse::Meta::Method::Constructor::XS' }, ':meta');
68 Mouse::Util->import({ into => 'Mouse::Meta::Method::Destructor::XS' }, ':meta');
3821b191 69 Mouse::Util->import({ into => 'Mouse::Meta::Method::Accessor::XS' }, ':meta');
923a04ba 70 return 1;
029463f4 71 } || 0;
823419c5 72 warn $@ if $@ && $ENV{MOUSE_XS};
df6dd016 73 }
74
db5e4409 75 if(!$xs){
df6dd016 76 require 'Mouse/PurePerl.pm'; # we don't want to create its namespace
77 }
db5e4409 78
ecd4a125 79 *MOUSE_XS = sub(){ $xs };
08f7a8db 80
bf930151 81 # definition of mro::get_linear_isa()
f15868c3 82 my $get_linear_isa;
b2571a4b 83 if ($] >= 5.010_000) {
84 require mro;
f15868c3 85 $get_linear_isa = \&mro::get_linear_isa;
4a5a9847 86 }
87 else {
74690b26 88 # this code is based on MRO::Compat::__get_linear_isa
ce5a7699 89 my $_get_linear_isa_dfs; # this recurses so it isn't pretty
90 $_get_linear_isa_dfs = sub {
91 my($classname) = @_;
92
93 my @lin = ($classname);
94 my %stored;
95
96 no strict 'refs';
97 foreach my $parent (@{"$classname\::ISA"}) {
74690b26 98 foreach my $p(@{ $_get_linear_isa_dfs->($parent) }) {
ce5a7699 99 next if exists $stored{$p};
100 push(@lin, $p);
101 $stored{$p} = 1;
272a1930 102 }
ce5a7699 103 }
104 return \@lin;
105 };
ce5a7699 106
74690b26 107 {
108 package # hide from PAUSE
109 Class::C3;
4bef84ef 110 our %MRO; # avoid 'once' warnings
74690b26 111 }
ce5a7699 112
113 # MRO::Compat::__get_linear_isa has no prototype, so
114 # we define a prototyped version for compatibility with core's
115 # See also MRO::Compat::__get_linear_isa.
116 $get_linear_isa = sub ($;$){
117 my($classname, $type) = @_;
74690b26 118
ce5a7699 119 if(!defined $type){
74690b26 120 $type = exists $Class::C3::MRO{$classname} ? 'c3' : 'dfs';
121 }
122 if($type eq 'c3'){
123 require Class::C3;
124 return [Class::C3::calculateMRO($classname)];
125 }
126 else{
127 return $_get_linear_isa_dfs->($classname);
ce5a7699 128 }
ce5a7699 129 };
eae80759 130 }
bcd39bf4 131
f15868c3 132 *get_linear_isa = $get_linear_isa;
eae80759 133}
134
bf930151 135use Carp ();
136use Scalar::Util ();
137
138# aliases as public APIs
139# it must be 'require', not 'use', because Mouse::Meta::Module depends on Mouse::Util
140require Mouse::Meta::Module; # for the entities of metaclass cache utilities
141
142# aliases
143{
144 *class_of = \&Mouse::Meta::Module::_class_of;
145 *get_metaclass_by_name = \&Mouse::Meta::Module::_get_metaclass_by_name;
146 *get_all_metaclass_instances = \&Mouse::Meta::Module::_get_all_metaclass_instances;
147 *get_all_metaclass_names = \&Mouse::Meta::Module::_get_all_metaclass_names;
148
149 *Mouse::load_class = \&load_class;
150 *Mouse::is_class_loaded = \&is_class_loaded;
151
152 # is-a predicates
153 #generate_isa_predicate_for('Mouse::Meta::TypeConstraint' => 'is_a_type_constraint');
154 #generate_isa_predicate_for('Mouse::Meta::Class' => 'is_a_metaclass');
155 #generate_isa_predicate_for('Mouse::Meta::Role' => 'is_a_metarole');
156
157 # duck type predicates
158 generate_can_predicate_for(['_compiled_type_constraint'] => 'is_a_type_constraint');
159 generate_can_predicate_for(['create_anon_class'] => 'is_a_metaclass');
160 generate_can_predicate_for(['create_anon_role'] => 'is_a_metarole');
161}
162
163our $in_global_destruction = 0;
164END{ $in_global_destruction = 1 }
165
166# Moose::Util compatible utilities
167
168sub find_meta{
169 return class_of( $_[0] );
170}
171
31aa6299 172sub _does_role_impl {
bf930151 173 my ($class_or_obj, $role_name) = @_;
174
175 my $meta = class_of($class_or_obj);
176
177 (defined $role_name)
178 || ($meta || 'Mouse::Meta::Class')->throw_error("You must supply a role name to does()");
179
180 return defined($meta) && $meta->does_role($role_name);
181}
3a63a2e7 182
31aa6299 183sub does_role {
184 my($thing, $role_name) = @_;
185
186 if( (Scalar::Util::blessed($thing) || is_class_loaded($thing))
187 && $thing->can('does')) {
188 return $thing->does($role_name);
189 }
190 goto &_does_role_impl;
191}
192
08f7a8db 193# taken from Mouse::Util (0.90)
abfdffe0 194{
195 my %cache;
196
8e64d0fa 197 sub resolve_metaclass_alias {
198 my ( $type, $metaclass_name, %options ) = @_;
199
200 my $cache_key = $type . q{ } . ( $options{trait} ? '-Trait' : '' );
201
202 return $cache{$cache_key}{$metaclass_name} ||= do{
abfdffe0 203
08f7a8db 204 my $possible_full_name = join '::',
205 'Mouse::Meta', $type, 'Custom', ($options{trait} ? 'Trait' : ()), $metaclass_name
206 ;
207
8e64d0fa 208 my $loaded_class = load_first_existing_class(
209 $possible_full_name,
210 $metaclass_name
211 );
212
213 $loaded_class->can('register_implementation')
214 ? $loaded_class->register_implementation
08f7a8db 215 : $loaded_class;
8e64d0fa 216 };
abfdffe0 217 }
218}
219
739525d0 220# Utilities from Class::MOP
221
df6dd016 222sub get_code_info;
223sub get_code_package;
739525d0 224
0ffc4183 225sub is_valid_class_name;
bf930151 226sub is_class_loaded;
abfdffe0 227
228# taken from Class/MOP.pm
229sub load_first_existing_class {
230 my @classes = @_
231 or return;
232
23264b5b 233 my %exceptions;
234 for my $class (@classes) {
abfdffe0 235 my $e = _try_load_one_class($class);
236
237 if ($e) {
238 $exceptions{$class} = $e;
239 }
240 else {
53875581 241 return $class;
abfdffe0 242 }
243 }
abfdffe0 244
53875581 245 # not found
5dd5edef 246 Carp::confess join(
abfdffe0 247 "\n",
248 map {
249 sprintf( "Could not load class (%s) because : %s",
250 $_, $exceptions{$_} )
251 } @classes
252 );
253}
254
255# taken from Class/MOP.pm
256sub _try_load_one_class {
257 my $class = shift;
258
6cfa1e5e 259 unless ( is_valid_class_name($class) ) {
260 my $display = defined($class) ? $class : 'undef';
5dd5edef 261 Carp::confess "Invalid class name ($display)";
6cfa1e5e 262 }
263
be0ba859 264 return '' if is_class_loaded($class);
abfdffe0 265
637d4f17 266 $class =~ s{::}{/}g;
267 $class .= '.pm';
abfdffe0 268
269 return do {
270 local $@;
637d4f17 271 eval { require $class };
abfdffe0 272 $@;
273 };
274}
275
6cfa1e5e 276
277sub load_class {
278 my $class = shift;
279 my $e = _try_load_one_class($class);
5dd5edef 280 Carp::confess "Could not load class ($class) because : $e" if $e;
6cfa1e5e 281
637d4f17 282 return $class;
6cfa1e5e 283}
284
6cfa1e5e 285
2e92bb89 286sub apply_all_roles {
45f22b92 287 my $consumer = Scalar::Util::blessed($_[0])
80b463bb 288 ? $_[0] # instance
289 : Mouse::Meta::Class->initialize($_[0]); # class or role name
2e92bb89 290
21498b08 291 my @roles;
f6715552 292
293 # Basis of Data::OptList
21498b08 294 my $max = scalar(@_);
80b463bb 295 for (my $i = 1; $i < $max ; $i++) {
296 my $role = $_[$i];
297 my $role_name;
298 if(ref $role) {
299 $role_name = $role->name;
300 }
301 else {
302 $role_name = $role;
303 load_class($role_name);
304 $role = get_metaclass_by_name($role_name);
21498b08 305 }
0126c27c 306
80b463bb 307 if ($i + 1 < $max && ref($_[$i + 1]) eq 'HASH') {
308 push @roles, [ $role => $_[++$i] ];
309 } else {
310 push @roles, [ $role => undef ];
311 }
312 is_a_metarole($role)
45f22b92 313 || $consumer->meta->throw_error("You can only consume roles, $role_name is not a Mouse role");
21498b08 314 }
315
21498b08 316 if ( scalar @roles == 1 ) {
80b463bb 317 my ( $role, $params ) = @{ $roles[0] };
318 $role->apply( $consumer, defined $params ? $params : () );
21498b08 319 }
320 else {
45f22b92 321 Mouse::Meta::Role->combine(@roles)->apply($consumer);
21498b08 322 }
23264b5b 323 return;
2e92bb89 324}
325
2e33bb59 326# taken from Moose::Util 0.90
327sub english_list {
8e64d0fa 328 return $_[0] if @_ == 1;
329
330 my @items = sort @_;
331
332 return "$items[0] and $items[1]" if @items == 2;
333
334 my $tail = pop @items;
335
336 return join q{, }, @items, "and $tail";
2e33bb59 337}
338
5af36247 339sub quoted_english_list {
53f661ad 340 return english_list(map { qq{'$_'} } @_);
5af36247 341}
342
53875581 343# common utilities
344
fce211ae 345sub not_supported{
346 my($feature) = @_;
347
1d76ae62 348 $feature ||= ( caller(1) )[3] . '()'; # subroutine name
fce211ae 349
1b9e472d 350 local $Carp::CarpLevel = $Carp::CarpLevel + 1;
351 Carp::confess("Mouse does not currently support $feature");
fce211ae 352}
353
fc8628e3 354# general meta() method
355sub meta :method{
152e5759 356 return Mouse::Meta::Class->initialize(ref($_[0]) || $_[0]);
53875581 357}
358
823419c5 359# general throw_error() method
360# $o->throw_error($msg, depth => $leve, longmess => $croak_or_confess)
361sub throw_error :method {
362 my($self, $message, %args) = @_;
363
364 local $Carp::CarpLevel = $Carp::CarpLevel + 1 + ($args{depth} || 0);
365 local $Carp::MaxArgNums = 20; # default is 8, usually we use named args which gets messier though
366
367 if(exists $args{longmess} && !$args{longmess}) {
368 Carp::croak($message);
369 }
370 else{
371 Carp::confess($message);
372 }
373}
374
fc8628e3 375# general dump() method
376sub dump :method {
53875581 377 my($self, $maxdepth) = @_;
378
379 require 'Data/Dumper.pm'; # we don't want to create its namespace
380 my $dd = Data::Dumper->new([$self]);
0cf6f1be 381 $dd->Maxdepth(defined($maxdepth) ? $maxdepth : 3);
53875581 382 $dd->Indent(1);
a672bf86 383 $dd->Sortkeys(1);
902174eb 384 $dd->Quotekeys(0);
53875581 385 return $dd->Dump();
386}
387
fc8628e3 388# general does() method
b64e2007 389sub does :method {
31aa6299 390 goto &_does_role_impl;
b64e2007 391}
53875581 392
4093c859 3931;
f38ce2d0 394__END__
395
396=head1 NAME
397
a672bf86 398Mouse::Util - Utilities for working with Mouse classes
f38ce2d0 399
a25ca8d6 400=head1 VERSION
401
14cf9b5a 402This document describes Mouse version 0.95
a25ca8d6 403
a672bf86 404=head1 SYNOPSIS
405
406 use Mouse::Util; # turns on strict and warnings
407
408=head1 DESCRIPTION
409
410This module provides a set of utility functions. Many of these
411functions are intended for use in Mouse itself or MouseX modules, but
412some of them may be useful for use in your own code.
413
f38ce2d0 414=head1 IMPLEMENTATIONS FOR
415
a672bf86 416=head2 Moose::Util functions
417
418The following functions are exportable.
419
420=head3 C<find_meta($class_or_obj)>
421
422The same as C<Mouse::Util::class_of()>.
ea249879 423
a672bf86 424=head3 C<does_role($class_or_obj, $role_or_obj)>
ea249879 425
a672bf86 426=head3 C<resolve_metaclass_alias($category, $name, %options)>
ea249879 427
a672bf86 428=head3 C<apply_all_roles($applicant, @roles)>
ea249879 429
a672bf86 430=head3 C<english_listi(@items)>
ea249879 431
a672bf86 432=head2 Class::MOP functions
ea249879 433
2af88019 434The following functions are not exportable.
ea249879 435
a672bf86 436=head3 C<< Mouse::Util::is_class_loaded($classname) -> Bool >>
ea249879 437
a672bf86 438Returns whether I<$classname> is actually loaded or not.
439It uses a heuristic which involves checking for the existence of
440C<$VERSION>, C<@ISA>, and any locally-defined method.
1820fffe 441
a672bf86 442=head3 C<< Mouse::Util::load_class($classname) -> ClassName >>
1820fffe 443
a672bf86 444This will load a given I<$classname> (or die if it is not loadable).
1820fffe 445This function can be used in place of tricks like
a672bf86 446C<eval "use $module ()"> or using C<require>.
ea249879 447
a672bf86 448=head3 C<< Mouse::Util::class_of($classname_or_object) -> MetaClass >>
739525d0 449
a672bf86 450=head3 C<< Mouse::Util::get_metaclass_by_name($classname) -> MetaClass >>
739525d0 451
a672bf86 452=head3 C<< Mouse::Util::get_all_metaclass_instances() -> (MetaClasses) >>
739525d0 453
a672bf86 454=head3 C<< Mouse::Util::get_all_metaclass_names() -> (ClassNames) >>
739525d0 455
4a5a9847 456=head2 mro (or MRO::Compat)
ea249879 457
458=head3 C<get_linear_isa>
459
460=head2 Sub::Identify
461
462=head3 C<get_code_info>
463
5482cd4c 464=head1 Mouse specific utilities
ea249879 465
bedd575c 466=head3 C<not_supported>
f38ce2d0 467
5482cd4c 468=head3 C<get_code_package>
469
470=head3 C<get_code_ref>
471
1820fffe 472=head1 SEE ALSO
473
474L<Moose::Util>
475
5164490d 476L<Class::MOP>
1820fffe 477
478L<Sub::Identify>
479
4a5a9847 480L<mro>
481
1820fffe 482L<MRO::Compat>
483
f38ce2d0 484=cut
485