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