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