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