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