More tests
[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
983d58a5 53 our $VERSION = '0.94';
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
172sub does_role{
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
08f7a8db 183# taken from Mouse::Util (0.90)
abfdffe0 184{
185 my %cache;
186
8e64d0fa 187 sub resolve_metaclass_alias {
188 my ( $type, $metaclass_name, %options ) = @_;
189
190 my $cache_key = $type . q{ } . ( $options{trait} ? '-Trait' : '' );
191
192 return $cache{$cache_key}{$metaclass_name} ||= do{
abfdffe0 193
08f7a8db 194 my $possible_full_name = join '::',
195 'Mouse::Meta', $type, 'Custom', ($options{trait} ? 'Trait' : ()), $metaclass_name
196 ;
197
8e64d0fa 198 my $loaded_class = load_first_existing_class(
199 $possible_full_name,
200 $metaclass_name
201 );
202
203 $loaded_class->can('register_implementation')
204 ? $loaded_class->register_implementation
08f7a8db 205 : $loaded_class;
8e64d0fa 206 };
abfdffe0 207 }
208}
209
739525d0 210# Utilities from Class::MOP
211
df6dd016 212sub get_code_info;
213sub get_code_package;
739525d0 214
0ffc4183 215sub is_valid_class_name;
bf930151 216sub is_class_loaded;
abfdffe0 217
218# taken from Class/MOP.pm
219sub load_first_existing_class {
220 my @classes = @_
221 or return;
222
23264b5b 223 my %exceptions;
224 for my $class (@classes) {
abfdffe0 225 my $e = _try_load_one_class($class);
226
227 if ($e) {
228 $exceptions{$class} = $e;
229 }
230 else {
53875581 231 return $class;
abfdffe0 232 }
233 }
abfdffe0 234
53875581 235 # not found
5dd5edef 236 Carp::confess join(
abfdffe0 237 "\n",
238 map {
239 sprintf( "Could not load class (%s) because : %s",
240 $_, $exceptions{$_} )
241 } @classes
242 );
243}
244
245# taken from Class/MOP.pm
246sub _try_load_one_class {
247 my $class = shift;
248
6cfa1e5e 249 unless ( is_valid_class_name($class) ) {
250 my $display = defined($class) ? $class : 'undef';
5dd5edef 251 Carp::confess "Invalid class name ($display)";
6cfa1e5e 252 }
253
be0ba859 254 return '' if is_class_loaded($class);
abfdffe0 255
637d4f17 256 $class =~ s{::}{/}g;
257 $class .= '.pm';
abfdffe0 258
259 return do {
260 local $@;
637d4f17 261 eval { require $class };
abfdffe0 262 $@;
263 };
264}
265
6cfa1e5e 266
267sub load_class {
268 my $class = shift;
269 my $e = _try_load_one_class($class);
5dd5edef 270 Carp::confess "Could not load class ($class) because : $e" if $e;
6cfa1e5e 271
637d4f17 272 return $class;
6cfa1e5e 273}
274
6cfa1e5e 275
2e92bb89 276sub apply_all_roles {
45f22b92 277 my $consumer = Scalar::Util::blessed($_[0])
80b463bb 278 ? $_[0] # instance
279 : Mouse::Meta::Class->initialize($_[0]); # class or role name
2e92bb89 280
21498b08 281 my @roles;
f6715552 282
283 # Basis of Data::OptList
21498b08 284 my $max = scalar(@_);
80b463bb 285 for (my $i = 1; $i < $max ; $i++) {
286 my $role = $_[$i];
287 my $role_name;
288 if(ref $role) {
289 $role_name = $role->name;
290 }
291 else {
292 $role_name = $role;
293 load_class($role_name);
294 $role = get_metaclass_by_name($role_name);
21498b08 295 }
0126c27c 296
80b463bb 297 if ($i + 1 < $max && ref($_[$i + 1]) eq 'HASH') {
298 push @roles, [ $role => $_[++$i] ];
299 } else {
300 push @roles, [ $role => undef ];
301 }
302 is_a_metarole($role)
45f22b92 303 || $consumer->meta->throw_error("You can only consume roles, $role_name is not a Mouse role");
21498b08 304 }
305
21498b08 306 if ( scalar @roles == 1 ) {
80b463bb 307 my ( $role, $params ) = @{ $roles[0] };
308 $role->apply( $consumer, defined $params ? $params : () );
21498b08 309 }
310 else {
45f22b92 311 Mouse::Meta::Role->combine(@roles)->apply($consumer);
21498b08 312 }
23264b5b 313 return;
2e92bb89 314}
315
2e33bb59 316# taken from Moose::Util 0.90
317sub english_list {
8e64d0fa 318 return $_[0] if @_ == 1;
319
320 my @items = sort @_;
321
322 return "$items[0] and $items[1]" if @items == 2;
323
324 my $tail = pop @items;
325
326 return join q{, }, @items, "and $tail";
2e33bb59 327}
328
5af36247 329sub quoted_english_list {
53f661ad 330 return english_list(map { qq{'$_'} } @_);
5af36247 331}
332
53875581 333# common utilities
334
fce211ae 335sub not_supported{
336 my($feature) = @_;
337
1d76ae62 338 $feature ||= ( caller(1) )[3] . '()'; # subroutine name
fce211ae 339
1b9e472d 340 local $Carp::CarpLevel = $Carp::CarpLevel + 1;
341 Carp::confess("Mouse does not currently support $feature");
fce211ae 342}
343
fc8628e3 344# general meta() method
345sub meta :method{
152e5759 346 return Mouse::Meta::Class->initialize(ref($_[0]) || $_[0]);
53875581 347}
348
823419c5 349# general throw_error() method
350# $o->throw_error($msg, depth => $leve, longmess => $croak_or_confess)
351sub throw_error :method {
352 my($self, $message, %args) = @_;
353
354 local $Carp::CarpLevel = $Carp::CarpLevel + 1 + ($args{depth} || 0);
355 local $Carp::MaxArgNums = 20; # default is 8, usually we use named args which gets messier though
356
357 if(exists $args{longmess} && !$args{longmess}) {
358 Carp::croak($message);
359 }
360 else{
361 Carp::confess($message);
362 }
363}
364
fc8628e3 365# general dump() method
366sub dump :method {
53875581 367 my($self, $maxdepth) = @_;
368
369 require 'Data/Dumper.pm'; # we don't want to create its namespace
370 my $dd = Data::Dumper->new([$self]);
0cf6f1be 371 $dd->Maxdepth(defined($maxdepth) ? $maxdepth : 3);
53875581 372 $dd->Indent(1);
a672bf86 373 $dd->Sortkeys(1);
902174eb 374 $dd->Quotekeys(0);
53875581 375 return $dd->Dump();
376}
377
fc8628e3 378# general does() method
b64e2007 379sub does :method {
380 goto &does_role;
381}
53875581 382
4093c859 3831;
f38ce2d0 384__END__
385
386=head1 NAME
387
a672bf86 388Mouse::Util - Utilities for working with Mouse classes
f38ce2d0 389
a25ca8d6 390=head1 VERSION
391
983d58a5 392This document describes Mouse version 0.94
a25ca8d6 393
a672bf86 394=head1 SYNOPSIS
395
396 use Mouse::Util; # turns on strict and warnings
397
398=head1 DESCRIPTION
399
400This module provides a set of utility functions. Many of these
401functions are intended for use in Mouse itself or MouseX modules, but
402some of them may be useful for use in your own code.
403
f38ce2d0 404=head1 IMPLEMENTATIONS FOR
405
a672bf86 406=head2 Moose::Util functions
407
408The following functions are exportable.
409
410=head3 C<find_meta($class_or_obj)>
411
412The same as C<Mouse::Util::class_of()>.
ea249879 413
a672bf86 414=head3 C<does_role($class_or_obj, $role_or_obj)>
ea249879 415
a672bf86 416=head3 C<resolve_metaclass_alias($category, $name, %options)>
ea249879 417
a672bf86 418=head3 C<apply_all_roles($applicant, @roles)>
ea249879 419
a672bf86 420=head3 C<english_listi(@items)>
ea249879 421
a672bf86 422=head2 Class::MOP functions
ea249879 423
2af88019 424The following functions are not exportable.
ea249879 425
a672bf86 426=head3 C<< Mouse::Util::is_class_loaded($classname) -> Bool >>
ea249879 427
a672bf86 428Returns whether I<$classname> is actually loaded or not.
429It uses a heuristic which involves checking for the existence of
430C<$VERSION>, C<@ISA>, and any locally-defined method.
1820fffe 431
a672bf86 432=head3 C<< Mouse::Util::load_class($classname) -> ClassName >>
1820fffe 433
a672bf86 434This will load a given I<$classname> (or die if it is not loadable).
1820fffe 435This function can be used in place of tricks like
a672bf86 436C<eval "use $module ()"> or using C<require>.
ea249879 437
a672bf86 438=head3 C<< Mouse::Util::class_of($classname_or_object) -> MetaClass >>
739525d0 439
a672bf86 440=head3 C<< Mouse::Util::get_metaclass_by_name($classname) -> MetaClass >>
739525d0 441
a672bf86 442=head3 C<< Mouse::Util::get_all_metaclass_instances() -> (MetaClasses) >>
739525d0 443
a672bf86 444=head3 C<< Mouse::Util::get_all_metaclass_names() -> (ClassNames) >>
739525d0 445
4a5a9847 446=head2 mro (or MRO::Compat)
ea249879 447
448=head3 C<get_linear_isa>
449
450=head2 Sub::Identify
451
452=head3 C<get_code_info>
453
5482cd4c 454=head1 Mouse specific utilities
ea249879 455
bedd575c 456=head3 C<not_supported>
f38ce2d0 457
5482cd4c 458=head3 C<get_code_package>
459
460=head3 C<get_code_ref>
461
1820fffe 462=head1 SEE ALSO
463
464L<Moose::Util>
465
5164490d 466L<Class::MOP>
1820fffe 467
468L<Sub::Identify>
469
4a5a9847 470L<mro>
471
1820fffe 472L<MRO::Compat>
473
f38ce2d0 474=cut
475