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