Checking in changes prior to tagging of version 0.38. Changelog diff is:
[gitmo/Mouse.git] / lib / Mouse / Util.pm
CommitLineData
4093c859 1package Mouse::Util;
bc69ee88 2use Mouse::Exporter; # enables strict and warnings
6d28c5cf 3
3a63a2e7 4use Carp qw(confess);
71e7b544 5use Scalar::Util qw(blessed);
01afd8ff 6use B ();
4093c859 7
04493075 8use constant _MOUSE_VERBOSE => !!$ENV{MOUSE_VERBOSE};
9
bc69ee88 10Mouse::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
08f7a8db 17
bc69ee88 18 load_class
19 is_class_loaded
08f7a8db 20
bc69ee88 21 get_linear_isa
22 get_code_info
ea249879 23
bc69ee88 24 get_code_package
01afd8ff 25
bc69ee88 26 not_supported
53875581 27
bc69ee88 28 does meta dump
29 _MOUSE_VERBOSE
30 )],
31 groups => {
32 default => [], # export no functions by default
0126c27c 33
34 # The ':meta' group is 'use metaclass' for Mouse
bc69ee88 35 meta => [qw(does meta dump _MOUSE_VERBOSE)],
36 },
37 _export_to_main => 1,
eae80759 38);
39
739525d0 40# aliases as public APIs
deb9a0f3 41# it must be 'require', not 'use', because Mouse::Meta::Module depends on Mouse::Util
7727a2f0 42require Mouse::Meta::Module; # for the entities of metaclass cache utilities
43
44BEGIN {
739525d0 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
08f7a8db 51# Moose::Util compatible utilities
52
53sub find_meta{
739525d0 54 return class_of( $_[0] );
08f7a8db 55}
56
57sub does_role{
53875581 58 my ($class_or_obj, $role_name) = @_;
8e64d0fa 59
739525d0 60 my $meta = class_of($class_or_obj);
8e64d0fa 61
53875581 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);
08f7a8db 66}
67
42d7df00 68BEGIN {
272a1930 69 my $impl;
bcd39bf4 70 if ($] >= 5.009_005) {
388b8ebd 71 require mro;
272a1930 72 $impl = \&mro::get_linear_isa;
73 } else {
e0396a57 74 my $e = do {
75 local $@;
76 eval { require MRO::Compat };
77 $@;
f172d4e7 78 };
e0396a57 79 if (!$e) {
272a1930 80 $impl = \&mro::get_linear_isa;
81 } else {
82# VVVVV CODE TAKEN FROM MRO::COMPAT VVVVV
e0396a57 83 my $_get_linear_isa_dfs; # this recurses so it isn't pretty
84 $_get_linear_isa_dfs = sub {
272a1930 85 no strict 'refs';
86
87 my $classname = shift;
88
89 my @lin = ($classname);
90 my %stored;
91 foreach my $parent (@{"$classname\::ISA"}) {
e0396a57 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;
272a1930 97 }
98 }
99 return \@lin;
100 };
101# ^^^^^ CODE TAKEN FROM MRO::COMPAT ^^^^^
e0396a57 102 $impl = $_get_linear_isa_dfs;
eae80759 103 }
104 }
bcd39bf4 105
a81cc7b8 106
107 no warnings 'once';
108 *get_linear_isa = $impl;
eae80759 109}
110
3a63a2e7 111{ # taken from Sub::Identify
8e64d0fa 112 sub get_code_info($) {
113 my ($coderef) = @_;
114 ref($coderef) or return;
23264b5b 115
8e64d0fa 116 my $cv = B::svref_2object($coderef);
3a63a2e7 117 $cv->isa('B::CV') or return;
118
8e64d0fa 119 my $gv = $cv->GV;
120 $gv->isa('B::GV') or return;
121
122 return ($gv->STASH->NAME, $gv->NAME);
123 }
01afd8ff 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 }
3a63a2e7 136}
137
08f7a8db 138# taken from Mouse::Util (0.90)
abfdffe0 139{
140 my %cache;
141
8e64d0fa 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{
abfdffe0 148
08f7a8db 149 my $possible_full_name = join '::',
150 'Mouse::Meta', $type, 'Custom', ($options{trait} ? 'Trait' : ()), $metaclass_name
151 ;
152
8e64d0fa 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
08f7a8db 160 : $loaded_class;
8e64d0fa 161 };
abfdffe0 162 }
163}
164
739525d0 165# Utilities from Class::MOP
166
167
abfdffe0 168# taken from Class/MOP.pm
a81cc7b8 169sub is_valid_class_name {
abfdffe0 170 my $class = shift;
171
172 return 0 if ref($class);
173 return 0 unless defined($class);
abfdffe0 174
175 return 1 if $class =~ /^\w+(?:::\w+)*$/;
176
177 return 0;
178}
179
180# taken from Class/MOP.pm
181sub load_first_existing_class {
182 my @classes = @_
183 or return;
184
23264b5b 185 my %exceptions;
186 for my $class (@classes) {
abfdffe0 187 my $e = _try_load_one_class($class);
188
189 if ($e) {
190 $exceptions{$class} = $e;
191 }
192 else {
53875581 193 return $class;
abfdffe0 194 }
195 }
abfdffe0 196
53875581 197 # not found
abfdffe0 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
3aac966d 208my %is_class_loaded_cache;
abfdffe0 209sub _try_load_one_class {
210 my $class = shift;
211
6cfa1e5e 212 unless ( is_valid_class_name($class) ) {
213 my $display = defined($class) ? $class : 'undef';
214 confess "Invalid class name ($display)";
215 }
216
3aac966d 217 return undef if $is_class_loaded_cache{$class} ||= is_class_loaded($class);
abfdffe0 218
219 my $file = $class . '.pm';
220 $file =~ s{::}{/}g;
221
222 return do {
223 local $@;
224 eval { require($file) };
225 $@;
226 };
227}
228
6cfa1e5e 229
230sub 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
3aac966d 238
6cfa1e5e 239sub is_class_loaded {
240 my $class = shift;
241
242 return 0 if ref($class) || !defined($class) || !length($class);
243
6cfa1e5e 244 # walk the symbol table tree to avoid autovififying
245 # \*{${main::}{"Foo::"}} == \*main::Foo::
246
ff687069 247 my $pack = \%::;
6cfa1e5e 248 foreach my $part (split('::', $class)) {
ff687069 249 my $entry = \$pack->{$part . '::'};
250 return 0 if ref($entry) ne 'GLOB';
251 $pack = *{$entry}{HASH} or return 0;
6cfa1e5e 252 }
253
254 # check for $VERSION or @ISA
3aac966d 255 return 1 if exists $pack->{VERSION}
ff687069 256 && defined *{$pack->{VERSION}}{SCALAR} && defined ${ $pack->{VERSION} };
3aac966d 257 return 1 if exists $pack->{ISA}
ff687069 258 && defined *{$pack->{ISA}}{ARRAY} && @{ $pack->{ISA} } != 0;
6cfa1e5e 259
260 # check for any method
ff687069 261 foreach my $name( keys %{$pack} ) {
262 my $entry = \$pack->{$name};
3aac966d 263 return 1 if ref($entry) ne 'GLOB' || defined *{$entry}{CODE};
6cfa1e5e 264 }
265
266 # fail
267 return 0;
268}
269
270
2e92bb89 271sub apply_all_roles {
71e7b544 272 my $applicant = blessed($_[0]) ? shift : Mouse::Meta::Class->initialize(shift);
2e92bb89 273
21498b08 274 my @roles;
f6715552 275
276 # Basis of Data::OptList
21498b08 277 my $max = scalar(@_);
278 for (my $i = 0; $i < $max ; $i++) {
279 if ($i + 1 < $max && ref($_[$i + 1])) {
b1980b86 280 push @roles, [ $_[$i] => $_[++$i] ];
21498b08 281 } else {
b1980b86 282 push @roles, [ $_[$i] => undef ];
21498b08 283 }
ff687069 284 my $role_name = $roles[-1][0];
285 load_class($role_name);
0126c27c 286
b1980b86 287 my $metarole = get_metaclass_by_name($role_name);
288 ( $metarole && $metarole->isa('Mouse::Meta::Role') )
71e7b544 289 || $applicant->meta->throw_error("You can only consume roles, $role_name(".$role_name->meta.") is not a Mouse role");
21498b08 290 }
291
21498b08 292 if ( scalar @roles == 1 ) {
b1980b86 293 my ( $role_name, $params ) = @{ $roles[0] };
294 get_metaclass_by_name($role_name)->apply( $applicant, defined $params ? $params : () );
21498b08 295 }
296 else {
71e7b544 297 Mouse::Meta::Role->combine(@roles)->apply($applicant);
21498b08 298 }
23264b5b 299 return;
2e92bb89 300}
301
2e33bb59 302# taken from Moose::Util 0.90
303sub english_list {
8e64d0fa 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";
2e33bb59 313}
314
53875581 315
316# common utilities
317
fce211ae 318sub not_supported{
319 my($feature) = @_;
320
321 $feature ||= ( caller(1) )[3]; # subroutine name
322
1b9e472d 323 local $Carp::CarpLevel = $Carp::CarpLevel + 1;
324 Carp::confess("Mouse does not currently support $feature");
fce211ae 325}
326
fc8628e3 327# general meta() method
328sub meta :method{
152e5759 329 return Mouse::Meta::Class->initialize(ref($_[0]) || $_[0]);
53875581 330}
331
fc8628e3 332# general dump() method
333sub dump :method {
53875581 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
fc8628e3 343# general does() method
53875581 344sub does :method;
345*does = \&does_role; # alias
346
4093c859 3471;
348
f38ce2d0 349__END__
350
351=head1 NAME
352
bedd575c 353Mouse::Util - Features, with or without their dependencies
f38ce2d0 354
a25ca8d6 355=head1 VERSION
356
06a970ab 357This document describes Mouse version 0.38
a25ca8d6 358
f38ce2d0 359=head1 IMPLEMENTATIONS FOR
360
ea249879 361=head2 Moose::Util
362
363=head3 C<find_meta>
364
365=head3 C<does_role>
366
367=head3 C<resolve_metaclass_alias>
368
369=head3 C<apply_all_roles>
370
371=head3 C<english_list>
372
373=head2 Class::MOP
374
5bacd9bf 375=head3 C<< is_class_loaded(ClassName) -> Bool >>
ea249879 376
1820fffe 377Returns whether C<ClassName> is actually loaded or not. It uses a heuristic which
378involves checking for the existence of C<$VERSION>, C<@ISA>, and any
379locally-defined method.
380
381=head3 C<< load_class(ClassName) >>
382
739525d0 383This will load a given C<ClassName> (or die if it is not loadable).
1820fffe 384This function can be used in place of tricks like
385C<eval "use $module"> or using C<require>.
ea249879 386
5164490d 387=head3 C<< Mouse::Util::class_of(ClassName or Object) >>
739525d0 388
5164490d 389=head3 C<< Mouse::Util::get_metaclass_by_name(ClassName) >>
739525d0 390
5164490d 391=head3 C<< Mouse::Util::get_all_metaclass_instances() >>
739525d0 392
5164490d 393=head3 C<< Mouse::Util::get_all_metaclass_names() >>
739525d0 394
ea249879 395=head2 MRO::Compat
396
397=head3 C<get_linear_isa>
398
399=head2 Sub::Identify
400
401=head3 C<get_code_info>
402
403=head1 UTILITIES FOR MOUSE
404
bedd575c 405=head3 C<not_supported>
f38ce2d0 406
1820fffe 407=head1 SEE ALSO
408
409L<Moose::Util>
410
5164490d 411L<Class::MOP>
1820fffe 412
413L<Sub::Identify>
414
415L<MRO::Compat>
416
f38ce2d0 417=cut
418