Checking in changes prior to tagging of version 0.40_09. Changelog diff is:
[gitmo/Mouse.git] / lib / Mouse / Util.pm
CommitLineData
4093c859 1package Mouse::Util;
bc69ee88 2use Mouse::Exporter; # enables strict and warnings
6d28c5cf 3
ba153b33 4sub get_linear_isa($;$); # must be here
5
df6dd016 6BEGIN{
8aba926d 7 # This is used in Mouse::PurePerl
8 Mouse::Exporter->setup_import_methods(
9 as_is => [qw(
10 find_meta
11 does_role
12 resolve_metaclass_alias
13 apply_all_roles
14 english_list
15
16 load_class
17 is_class_loaded
18
19 get_linear_isa
20 get_code_info
21
22 get_code_package
23 get_code_ref
24
25 not_supported
26
27 does meta dump
28 )],
29 groups => {
30 default => [], # export no functions by default
31
32 # The ':meta' group is 'use metaclass' for Mouse
33 meta => [qw(does meta dump)],
34 },
35 );
36
37
df6dd016 38 # Because Mouse::Util is loaded first in all the Mouse sub-modules,
39 # XS loader is placed here, not in Mouse.pm.
40
506fb74d 41 our $VERSION = '0.40_09';
df6dd016 42
db5e4409 43 my $xs = !(exists $INC{'Mouse/PurePerl.pm'} || $ENV{MOUSE_PUREPERL});
df6dd016 44
db5e4409 45 if($xs){
34bdc46a 46 # XXX: XSLoader tries to get the object path from caller's file name
47 # $hack_mouse_file fools its mechanism
48
49 (my $hack_mouse_file = __FILE__) =~ s/.Util//; # .../Mouse/Util.pm -> .../Mouse.pm
db5e4409 50 $xs = eval sprintf("#line %d %s\n", __LINE__, $hack_mouse_file) . q{
df6dd016 51 require XSLoader;
52 XSLoader::load('Mouse', $VERSION);
923a04ba 53 Mouse::Util->import({ into => 'Mouse::Meta::Method::Constructor::XS' }, ':meta');
54 Mouse::Util->import({ into => 'Mouse::Meta::Method::Destructor::XS' }, ':meta');
3821b191 55 Mouse::Util->import({ into => 'Mouse::Meta::Method::Accessor::XS' }, ':meta');
923a04ba 56 return 1;
df6dd016 57 };
1a6d349c 58 #warn $@ if $@;
df6dd016 59 }
60
db5e4409 61 if(!$xs){
df6dd016 62 require 'Mouse/PurePerl.pm'; # we don't want to create its namespace
63 }
db5e4409 64
ecd4a125 65 *MOUSE_XS = sub(){ $xs };
df6dd016 66}
67
68
5dd5edef 69use Carp ();
70use Scalar::Util ();
4093c859 71
04493075 72use constant _MOUSE_VERBOSE => !!$ENV{MOUSE_VERBOSE};
73
739525d0 74# aliases as public APIs
deb9a0f3 75# it must be 'require', not 'use', because Mouse::Meta::Module depends on Mouse::Util
7727a2f0 76require Mouse::Meta::Module; # for the entities of metaclass cache utilities
77
78BEGIN {
542f20ad 79 *class_of = \&Mouse::Meta::Module::_class_of;
80 *get_metaclass_by_name = \&Mouse::Meta::Module::_get_metaclass_by_name;
81 *get_all_metaclass_instances = \&Mouse::Meta::Module::_get_all_metaclass_instances;
82 *get_all_metaclass_names = \&Mouse::Meta::Module::_get_all_metaclass_names;
f48920c1 83
84 # is-a predicates
e3540312 85 generate_isa_predicate_for('Mouse::Meta::TypeConstraint' => 'is_a_type_constraint');
86 generate_isa_predicate_for('Mouse::Meta::Class' => 'is_a_metaclass');
87 generate_isa_predicate_for('Mouse::Meta::Role' => 'is_a_metarole');
739525d0 88}
89
39a8df63 90our $in_global_destruction = 0;
91END{ $in_global_destruction = 1 }
f48920c1 92
08f7a8db 93# Moose::Util compatible utilities
94
95sub find_meta{
739525d0 96 return class_of( $_[0] );
08f7a8db 97}
98
99sub does_role{
53875581 100 my ($class_or_obj, $role_name) = @_;
8e64d0fa 101
739525d0 102 my $meta = class_of($class_or_obj);
8e64d0fa 103
53875581 104 (defined $role_name)
105 || ($meta || 'Mouse::Meta::Class')->throw_error("You must supply a role name to does()");
106
107 return defined($meta) && $meta->does_role($role_name);
08f7a8db 108}
109
42d7df00 110BEGIN {
f15868c3 111 my $get_linear_isa;
bcd39bf4 112 if ($] >= 5.009_005) {
388b8ebd 113 require mro;
f15868c3 114 $get_linear_isa = \&mro::get_linear_isa;
272a1930 115 } else {
272a1930 116# VVVVV CODE TAKEN FROM MRO::COMPAT VVVVV
ce5a7699 117 my $_get_linear_isa_dfs; # this recurses so it isn't pretty
118 $_get_linear_isa_dfs = sub {
119 my($classname) = @_;
120
121 my @lin = ($classname);
122 my %stored;
123
124 no strict 'refs';
125 foreach my $parent (@{"$classname\::ISA"}) {
126 my $plin = $_get_linear_isa_dfs->($parent);
127 foreach my $p(@$plin) {
128 next if exists $stored{$p};
129 push(@lin, $p);
130 $stored{$p} = 1;
272a1930 131 }
ce5a7699 132 }
133 return \@lin;
134 };
272a1930 135# ^^^^^ CODE TAKEN FROM MRO::COMPAT ^^^^^
ce5a7699 136
137 eval{ require Class::C3 };
138
139 # MRO::Compat::__get_linear_isa has no prototype, so
140 # we define a prototyped version for compatibility with core's
141 # See also MRO::Compat::__get_linear_isa.
142 $get_linear_isa = sub ($;$){
143 my($classname, $type) = @_;
144 if(!defined $type){
145 package Class::C3;
146 our %MRO;
147 $type = exists $MRO{$classname} ? 'c3' : 'dfs';
148 }
149 return $type eq 'c3'
150 ? [Class::C3::calculateMRO($classname)]
151 : $_get_linear_isa_dfs->($classname);\r
152 };
eae80759 153 }
bcd39bf4 154
f15868c3 155 *get_linear_isa = $get_linear_isa;
eae80759 156}
157
3a63a2e7 158
08f7a8db 159# taken from Mouse::Util (0.90)
abfdffe0 160{
161 my %cache;
162
8e64d0fa 163 sub resolve_metaclass_alias {
164 my ( $type, $metaclass_name, %options ) = @_;
165
166 my $cache_key = $type . q{ } . ( $options{trait} ? '-Trait' : '' );
167
168 return $cache{$cache_key}{$metaclass_name} ||= do{
abfdffe0 169
08f7a8db 170 my $possible_full_name = join '::',
171 'Mouse::Meta', $type, 'Custom', ($options{trait} ? 'Trait' : ()), $metaclass_name
172 ;
173
8e64d0fa 174 my $loaded_class = load_first_existing_class(
175 $possible_full_name,
176 $metaclass_name
177 );
178
179 $loaded_class->can('register_implementation')
180 ? $loaded_class->register_implementation
08f7a8db 181 : $loaded_class;
8e64d0fa 182 };
abfdffe0 183 }
184}
185
739525d0 186# Utilities from Class::MOP
187
df6dd016 188sub get_code_info;
189sub get_code_package;
739525d0 190
abfdffe0 191# taken from Class/MOP.pm
a81cc7b8 192sub is_valid_class_name {
abfdffe0 193 my $class = shift;
194
195 return 0 if ref($class);
196 return 0 unless defined($class);
abfdffe0 197
ba153b33 198 return 1 if $class =~ /\A \w+ (?: :: \w+ )* \z/xms;
abfdffe0 199
200 return 0;
201}
202
203# taken from Class/MOP.pm
204sub load_first_existing_class {
205 my @classes = @_
206 or return;
207
23264b5b 208 my %exceptions;
209 for my $class (@classes) {
abfdffe0 210 my $e = _try_load_one_class($class);
211
212 if ($e) {
213 $exceptions{$class} = $e;
214 }
215 else {
53875581 216 return $class;
abfdffe0 217 }
218 }
abfdffe0 219
53875581 220 # not found
5dd5edef 221 Carp::confess join(
abfdffe0 222 "\n",
223 map {
224 sprintf( "Could not load class (%s) because : %s",
225 $_, $exceptions{$_} )
226 } @classes
227 );
228}
229
230# taken from Class/MOP.pm
3aac966d 231my %is_class_loaded_cache;
abfdffe0 232sub _try_load_one_class {
233 my $class = shift;
234
6cfa1e5e 235 unless ( is_valid_class_name($class) ) {
236 my $display = defined($class) ? $class : 'undef';
5dd5edef 237 Carp::confess "Invalid class name ($display)";
6cfa1e5e 238 }
239
3aac966d 240 return undef if $is_class_loaded_cache{$class} ||= is_class_loaded($class);
abfdffe0 241
242 my $file = $class . '.pm';
243 $file =~ s{::}{/}g;
244
245 return do {
246 local $@;
247 eval { require($file) };
248 $@;
249 };
250}
251
6cfa1e5e 252
253sub load_class {
254 my $class = shift;
255 my $e = _try_load_one_class($class);
5dd5edef 256 Carp::confess "Could not load class ($class) because : $e" if $e;
6cfa1e5e 257
258 return 1;
259}
260
df6dd016 261sub is_class_loaded;
6cfa1e5e 262
263
2e92bb89 264sub apply_all_roles {
5dd5edef 265 my $applicant = Scalar::Util::blessed($_[0])
266 ? shift # instance
267 : Mouse::Meta::Class->initialize(shift); # class or role name
2e92bb89 268
21498b08 269 my @roles;
f6715552 270
271 # Basis of Data::OptList
21498b08 272 my $max = scalar(@_);
273 for (my $i = 0; $i < $max ; $i++) {
274 if ($i + 1 < $max && ref($_[$i + 1])) {
b1980b86 275 push @roles, [ $_[$i] => $_[++$i] ];
21498b08 276 } else {
b1980b86 277 push @roles, [ $_[$i] => undef ];
21498b08 278 }
ff687069 279 my $role_name = $roles[-1][0];
280 load_class($role_name);
0126c27c 281
f48920c1 282 is_a_metarole( get_metaclass_by_name($role_name) )
9ad58623 283 || $applicant->meta->throw_error("You can only consume roles, $role_name is not a Mouse role");
21498b08 284 }
285
21498b08 286 if ( scalar @roles == 1 ) {
b1980b86 287 my ( $role_name, $params ) = @{ $roles[0] };
288 get_metaclass_by_name($role_name)->apply( $applicant, defined $params ? $params : () );
21498b08 289 }
290 else {
71e7b544 291 Mouse::Meta::Role->combine(@roles)->apply($applicant);
21498b08 292 }
23264b5b 293 return;
2e92bb89 294}
295
2e33bb59 296# taken from Moose::Util 0.90
297sub english_list {
8e64d0fa 298 return $_[0] if @_ == 1;
299
300 my @items = sort @_;
301
302 return "$items[0] and $items[1]" if @items == 2;
303
304 my $tail = pop @items;
305
306 return join q{, }, @items, "and $tail";
2e33bb59 307}
308
53875581 309
310# common utilities
311
fce211ae 312sub not_supported{
313 my($feature) = @_;
314
315 $feature ||= ( caller(1) )[3]; # subroutine name
316
1b9e472d 317 local $Carp::CarpLevel = $Carp::CarpLevel + 1;
318 Carp::confess("Mouse does not currently support $feature");
fce211ae 319}
320
fc8628e3 321# general meta() method
322sub meta :method{
152e5759 323 return Mouse::Meta::Class->initialize(ref($_[0]) || $_[0]);
53875581 324}
325
fc8628e3 326# general dump() method
327sub dump :method {
53875581 328 my($self, $maxdepth) = @_;
329
330 require 'Data/Dumper.pm'; # we don't want to create its namespace
331 my $dd = Data::Dumper->new([$self]);
332 $dd->Maxdepth(defined($maxdepth) ? $maxdepth : 2);
333 $dd->Indent(1);
334 return $dd->Dump();
335}
336
fc8628e3 337# general does() method
53875581 338sub does :method;
339*does = \&does_role; # alias
340
4093c859 3411;
342
f38ce2d0 343__END__
344
345=head1 NAME
346
bedd575c 347Mouse::Util - Features, with or without their dependencies
f38ce2d0 348
a25ca8d6 349=head1 VERSION
350
506fb74d 351This document describes Mouse version 0.40_09
a25ca8d6 352
f38ce2d0 353=head1 IMPLEMENTATIONS FOR
354
ea249879 355=head2 Moose::Util
356
357=head3 C<find_meta>
358
359=head3 C<does_role>
360
361=head3 C<resolve_metaclass_alias>
362
363=head3 C<apply_all_roles>
364
365=head3 C<english_list>
366
367=head2 Class::MOP
368
5bacd9bf 369=head3 C<< is_class_loaded(ClassName) -> Bool >>
ea249879 370
1820fffe 371Returns whether C<ClassName> is actually loaded or not. It uses a heuristic which
372involves checking for the existence of C<$VERSION>, C<@ISA>, and any
373locally-defined method.
374
375=head3 C<< load_class(ClassName) >>
376
739525d0 377This will load a given C<ClassName> (or die if it is not loadable).
1820fffe 378This function can be used in place of tricks like
379C<eval "use $module"> or using C<require>.
ea249879 380
5164490d 381=head3 C<< Mouse::Util::class_of(ClassName or Object) >>
739525d0 382
5164490d 383=head3 C<< Mouse::Util::get_metaclass_by_name(ClassName) >>
739525d0 384
5164490d 385=head3 C<< Mouse::Util::get_all_metaclass_instances() >>
739525d0 386
5164490d 387=head3 C<< Mouse::Util::get_all_metaclass_names() >>
739525d0 388
ea249879 389=head2 MRO::Compat
390
391=head3 C<get_linear_isa>
392
393=head2 Sub::Identify
394
395=head3 C<get_code_info>
396
397=head1 UTILITIES FOR MOUSE
398
bedd575c 399=head3 C<not_supported>
f38ce2d0 400
1820fffe 401=head1 SEE ALSO
402
403L<Moose::Util>
404
5164490d 405L<Class::MOP>
1820fffe 406
407L<Sub::Identify>
408
409L<MRO::Compat>
410
f38ce2d0 411=cut
412