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