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