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