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