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