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