Load superclass explicitly
[gitmo/Moose.git] / lib / Moose / Util.pm
CommitLineData
9a641848 1package Moose::Util;
2
9a641848 3use strict;
4use warnings;
5
a9f50e21 6use Class::Load 0.07 qw(load_class load_first_existing_class);
c01f48b7 7use Data::OptList;
88e88a7b 8use Params::Util qw( _STRING );
7125b244 9use Sub::Exporter;
d7d8a8c7 10use Scalar::Util 'blessed';
d2782813 11use List::Util qw(first);
12use List::MoreUtils qw(any all);
5394a1c7 13use overload ();
094b6ec9 14use Try::Tiny;
38bf2a25 15use Class::MOP;
9a641848 16
7125b244 17my @exports = qw[
d03bd989 18 find_meta
6532ca5a 19 does_role
d03bd989 20 search_class_by_role
b099a649 21 ensure_all_roles
d7d8a8c7 22 apply_all_roles
d26f5671 23 with_traits
ab76842e 24 get_all_init_args
25 get_all_attribute_values
a5e883ae 26 resolve_metatrait_alias
27 resolve_metaclass_alias
5f71050b 28 add_method_modifier
d939e016 29 english_list
27f2f43f 30 meta_attribute_alias
31 meta_class_alias
883c60be 32 throw
7125b244 33];
9a641848 34
7125b244 35Sub::Exporter::setup_exporter({
36 exports => \@exports,
11065d1f 37 groups => { all => \@exports }
7125b244 38});
39
40## some utils for the utils ...
41
56ea1a11 42sub find_meta { Class::MOP::class_of(@_) }
9a641848 43
7125b244 44## the functions ...
adf82331 45
7125b244 46sub does_role {
47 my ($class_or_obj, $role) = @_;
adf82331 48
43781cc3 49 if (try { $class_or_obj->isa('Moose::Object') }) {
094b6ec9 50 return $class_or_obj->does($role);
51 }
52
6532ca5a 53 my $meta = find_meta($class_or_obj);
d03bd989 54
7125b244 55 return unless defined $meta;
10a745f5 56 return unless $meta->can('does_role');
7125b244 57 return 1 if $meta->does_role($role);
58 return;
9a641848 59}
60
1631b53f 61sub search_class_by_role {
560c498d 62 my ($class_or_obj, $role) = @_;
d03bd989 63
6532ca5a 64 my $meta = find_meta($class_or_obj);
7125b244 65
66 return unless defined $meta;
67
560c498d 68 my $role_name = blessed $role ? $role->name : $role;
69
7125b244 70 foreach my $class ($meta->class_precedence_list) {
d03bd989 71
72 my $_meta = find_meta($class);
1631b53f 73
7125b244 74 next unless defined $_meta;
75
76 foreach my $role (@{ $_meta->roles || [] }) {
1631b53f 77 return $class if $role->name eq $role_name;
78 }
79 }
80
7125b244 81 return;
1631b53f 82}
83
b099a649 84# this can possibly behave in unexpected ways because the roles being composed
85# before being applied could differ from call to call; I'm not sure if or how
86# to document this possible quirk.
87sub ensure_all_roles {
88 my $applicant = shift;
89 _apply_all_roles($applicant, sub { !does_role($applicant, $_) }, @_);
90}
91
d7d8a8c7 92sub apply_all_roles {
93 my $applicant = shift;
8d4d1cdc 94 _apply_all_roles($applicant, undef, @_);
b099a649 95}
96
97sub _apply_all_roles {
98 my $applicant = shift;
99 my $role_filter = shift;
e606ae5f 100
70ea9161 101 unless (@_) {
102 require Moose;
103 Moose->throw_error("Must specify at least one role to apply to $applicant");
104 }
e606ae5f 105
8e590ae4 106 # If @_ contains role meta objects, mkopt will think that they're values,
107 # because they're references. In other words (roleobj1, roleobj2,
108 # roleobj3) will become [ [ roleobj1, roleobj2 ], [ roleobj3, undef ] ]
109 # -- this is no good. We'll preprocess @_ first to eliminate the potential
110 # bug.
111 # -- rjbs, 2011-04-08
112 my $roles = Data::OptList::mkopt( [@_], {
113 moniker => 'role',
114 name_test => sub {
115 ! ref $_[0] or blessed($_[0]) && $_[0]->isa('Moose::Meta::Role')
116 }
117 });
e606ae5f 118
560c498d 119 my @role_metas;
70ea9161 120 foreach my $role (@$roles) {
560c498d 121 my $meta;
122
123 if ( blessed $role->[0] ) {
124 $meta = $role->[0];
125 }
126 else {
b5ae7c00 127 load_class( $role->[0] , $role->[1] );
eea9eb4d 128 $meta = find_meta( $role->[0] );
560c498d 129 }
70ea9161 130
c8d9f1e2 131 unless ($meta && $meta->isa('Moose::Meta::Role') ) {
70ea9161 132 require Moose;
133 Moose->throw_error( "You can only consume roles, "
134 . $role->[0]
135 . " is not a Moose role" );
136 }
560c498d 137
138 push @role_metas, [ $meta, $role->[1] ];
70ea9161 139 }
e606ae5f 140
82a24871 141 if ( defined $role_filter ) {
560c498d 142 @role_metas = grep { local $_ = $_->[0]; $role_filter->() } @role_metas;
8d4d1cdc 143 }
b099a649 144
560c498d 145 return unless @role_metas;
b099a649 146
b5ae7c00 147 load_class($applicant)
23360da2 148 unless blessed($applicant)
149 || Class::MOP::class_of($applicant);
150
eea9eb4d 151 my $meta = ( blessed $applicant ? $applicant : Moose::Meta::Class->initialize($applicant) );
6fab0b75 152
560c498d 153 if ( scalar @role_metas == 1 ) {
154 my ( $role, $params ) = @{ $role_metas[0] };
155 $role->apply( $meta, ( defined $params ? %$params : () ) );
d7d8a8c7 156 }
157 else {
560c498d 158 Moose::Meta::Role->combine(@role_metas)->apply($meta);
e606ae5f 159 }
d7d8a8c7 160}
161
d26f5671 162sub with_traits {
163 my ($class, @roles) = @_;
164 return $class unless @roles;
165 return Moose::Meta::Class->create_anon_class(
166 superclasses => [$class],
167 roles => \@roles,
168 cache => 1,
169 )->name;
170}
171
ab76842e 172# instance deconstruction ...
173
174sub get_all_attribute_values {
175 my ($class, $instance) = @_;
176 return +{
177 map { $_->name => $_->get_value($instance) }
178 grep { $_->has_value($instance) }
b2df9268 179 $class->get_all_attributes
ab76842e 180 };
181}
182
183sub get_all_init_args {
184 my ($class, $instance) = @_;
185 return +{
186 map { $_->init_arg => $_->get_value($instance) }
187 grep { $_->has_value($instance) }
d03bd989 188 grep { defined($_->init_arg) }
b2df9268 189 $class->get_all_attributes
ab76842e 190 };
191}
192
50fbbf3d 193sub resolve_metatrait_alias {
50fbbf3d 194 return resolve_metaclass_alias( @_, trait => 1 );
a3738e5b 195}
196
27f2f43f 197sub _build_alias_package_name {
198 my ($type, $name, $trait) = @_;
199 return 'Moose::Meta::'
200 . $type
201 . '::Custom::'
202 . ( $trait ? 'Trait::' : '' )
203 . $name;
204}
205
50fbbf3d 206{
207 my %cache;
a3738e5b 208
50fbbf3d 209 sub resolve_metaclass_alias {
210 my ( $type, $metaclass_name, %options ) = @_;
a3738e5b 211
50fbbf3d 212 my $cache_key = $type . q{ } . ( $options{trait} ? '-Trait' : '' );
213 return $cache{$cache_key}{$metaclass_name}
214 if $cache{$cache_key}{$metaclass_name};
215
27f2f43f 216 my $possible_full_name = _build_alias_package_name(
217 $type, $metaclass_name, $options{trait}
218 );
50fbbf3d 219
b5ae7c00 220 my $loaded_class = load_first_existing_class(
50fbbf3d 221 $possible_full_name,
222 $metaclass_name
223 );
224
225 return $cache{$cache_key}{$metaclass_name}
226 = $loaded_class->can('register_implementation')
227 ? $loaded_class->register_implementation
228 : $loaded_class;
229 }
a3738e5b 230}
231
5f71050b 232sub add_method_modifier {
233 my ( $class_or_obj, $modifier_name, $args ) = @_;
d5447d26 234 my $meta
235 = $class_or_obj->can('add_before_method_modifier')
236 ? $class_or_obj
237 : find_meta($class_or_obj);
5f71050b 238 my $code = pop @{$args};
239 my $add_modifier_method = 'add_' . $modifier_name . '_method_modifier';
240 if ( my $method_modifier_type = ref( @{$args}[0] ) ) {
241 if ( $method_modifier_type eq 'Regexp' ) {
e606ae5f 242 my @all_methods = $meta->get_all_methods;
5f71050b 243 my @matched_methods
e606ae5f 244 = grep { $_->name =~ @{$args}[0] } @all_methods;
245 $meta->$add_modifier_method( $_->name, $code )
5f71050b 246 for @matched_methods;
247 }
775666aa 248 elsif ($method_modifier_type eq 'ARRAY') {
249 $meta->$add_modifier_method( $_, $code ) for @{$args->[0]};
250 }
251 else {
252 $meta->throw_error(
253 sprintf(
254 "Methods passed to %s must be provided as a list, arrayref or regex, not %s",
255 $modifier_name,
256 $method_modifier_type,
257 )
258 );
259 }
5f71050b 260 }
261 else {
262 $meta->$add_modifier_method( $_, $code ) for @{$args};
263 }
264}
d9bb6c63 265
d939e016 266sub english_list {
267 my @items = sort @_;
268
269 return $items[0] if @items == 1;
270 return "$items[0] and $items[1]" if @items == 2;
271
272 my $tail = pop @items;
273 my $list = join ', ', @items;
274 $list .= ', and ' . $tail;
275
276 return $list;
277}
278
833b56a7 279sub _caller_info {
280 my $level = @_ ? ($_[0] + 1) : 2;
281 my %info;
282 @info{qw(package file line)} = caller($level);
283 return \%info;
284}
285
27f2f43f 286sub _create_alias {
287 my ($type, $name, $trait, $for) = @_;
288 my $package = _build_alias_package_name($type, $name, $trait);
289 Class::MOP::Class->initialize($package)->add_method(
290 register_implementation => sub { $for }
291 );
292}
293
294sub meta_attribute_alias {
295 my ($to, $from) = @_;
296 $from ||= caller;
297 my $meta = Class::MOP::class_of($from);
298 my $trait = $meta->isa('Moose::Meta::Role');
299 _create_alias('Attribute', $to, $trait, $from);
300}
301
302sub meta_class_alias {
303 my ($to, $from) = @_;
304 $from ||= caller;
305 my $meta = Class::MOP::class_of($from);
306 my $trait = $meta->isa('Moose::Meta::Role');
307 _create_alias('Class', $to, $trait, $from);
308}
309
88e88a7b 310# XXX - this should be added to Params::Util
5394a1c7 311sub _STRINGLIKE0 ($) {
dcba17d1 312 return 1 if _STRING( $_[0] );
313 if ( blessed $_[0] ) {
314 return overload::Method( $_[0], q{""} );
315 }
316
317 return 1 if defined $_[0] && $_[0] eq q{};
318
319 return 0;
88e88a7b 320}
321
61907a02 322sub _reconcile_roles_for_metaclass {
323 my ($class_meta_name, $super_meta_name) = @_;
324
325 my @role_differences = _role_differences(
326 $class_meta_name, $super_meta_name,
327 );
328
329 # handle the case where we need to fix compatibility between a class and
330 # its parent, but all roles in the class are already also done by the
331 # parent
2c739d1a 332 # see t/metaclasses/metaclass_compat_no_fixing_bug.t
61907a02 333 return $super_meta_name
334 unless @role_differences;
335
336 return Moose::Meta::Class->create_anon_class(
337 superclasses => [$super_meta_name],
338 roles => [map { $_->name } @role_differences],
339 cache => 1,
340 )->name;
341}
342
343sub _role_differences {
344 my ($class_meta_name, $super_meta_name) = @_;
6e15f532 345 my @super_role_metas
346 = grep { !$_->isa('Moose::Meta::Role::Composite') }
347 $super_meta_name->meta->can('calculate_all_roles_with_inheritance')
348 ? $super_meta_name->meta->calculate_all_roles_with_inheritance
349 : $super_meta_name->meta->can('calculate_all_roles')
350 ? $super_meta_name->meta->calculate_all_roles
351 : ();
352 my @role_metas
353 = grep { !$_->isa('Moose::Meta::Role::Composite') }
354 $class_meta_name->meta->can('calculate_all_roles_with_inheritance')
355 ? $class_meta_name->meta->calculate_all_roles_with_inheritance
356 : $class_meta_name->meta->can('calculate_all_roles')
357 ? $class_meta_name->meta->calculate_all_roles
358 : ();
61907a02 359 my @differences;
360 for my $role_meta (@role_metas) {
361 push @differences, $role_meta
362 unless any { $_->name eq $role_meta->name } @super_role_metas;
363 }
364 return @differences;
365}
366
d2782813 367sub _classes_differ_by_roles_only {
368 my ( $self_meta_name, $super_meta_name ) = @_;
369
370 my $common_base_name
371 = _find_common_base( $self_meta_name, $super_meta_name );
372
373 return unless defined $common_base_name;
374
375 my @super_meta_name_ancestor_names
376 = _get_ancestors_until( $super_meta_name, $common_base_name );
377 my @class_meta_name_ancestor_names
378 = _get_ancestors_until( $self_meta_name, $common_base_name );
379
380 return
381 unless all { _is_role_only_subclass($_) }
382 @super_meta_name_ancestor_names,
383 @class_meta_name_ancestor_names;
384
385 return 1;
386}
387
388sub _find_common_base {
389 my ($meta1, $meta2) = map { Class::MOP::class_of($_) } @_;
390 return unless defined $meta1 && defined $meta2;
391
392 # FIXME? This doesn't account for multiple inheritance (not sure
393 # if it needs to though). For example, if somewhere in $meta1's
394 # history it inherits from both ClassA and ClassB, and $meta2
395 # inherits from ClassB & ClassA, does it matter? And what crazy
396 # fool would do that anyway?
397
398 my %meta1_parents = map { $_ => 1 } $meta1->linearized_isa;
399
400 return first { $meta1_parents{$_} } $meta2->linearized_isa;
401}
402
403sub _get_ancestors_until {
404 my ($start_name, $until_name) = @_;
405
406 my @ancestor_names;
407 for my $ancestor_name (Class::MOP::class_of($start_name)->linearized_isa) {
408 last if $ancestor_name eq $until_name;
409 push @ancestor_names, $ancestor_name;
410 }
411 return @ancestor_names;
412}
413
414sub _is_role_only_subclass {
415 my ($meta_name) = @_;
416 my $meta = Class::MOP::Class->initialize($meta_name);
417 my @parent_names = $meta->superclasses;
418
419 # XXX: don't feel like messing with multiple inheritance here... what would
420 # that even do?
421 return unless @parent_names == 1;
422 my ($parent_name) = @parent_names;
423 my $parent_meta = Class::MOP::Class->initialize($parent_name);
424
425 # only get the roles attached to this particular class, don't look at
426 # superclasses
427 my @roles = $meta->can('calculate_all_roles')
428 ? $meta->calculate_all_roles
429 : ();
430
431 # it's obviously not a role-only subclass if it doesn't do any roles
432 return unless @roles;
433
434 # loop over all methods that are a part of the current class
435 # (not inherited)
436 for my $method ( $meta->_get_local_methods ) {
437 # always ignore meta
ba7d613d 438 next if $method->isa('Class::MOP::Method::Meta');
d2782813 439 # we'll deal with attributes below
440 next if $method->can('associated_attribute');
441 # if the method comes from a role we consumed, ignore it
442 next if $meta->can('does_role')
443 && $meta->does_role($method->original_package_name);
444 # FIXME - this really isn't right. Just because a modifier is
445 # defined in a role doesn't mean it isn't _also_ defined in the
446 # subclass.
447 next if $method->isa('Class::MOP::Method::Wrapped')
448 && (
449 (!scalar($method->around_modifiers)
450 || any { $_->has_around_method_modifiers($method->name) } @roles)
451 && (!scalar($method->before_modifiers)
452 || any { $_->has_before_method_modifiers($method->name) } @roles)
453 && (!scalar($method->after_modifiers)
454 || any { $_->has_after_method_modifiers($method->name) } @roles)
455 );
456
457 return 0;
458 }
459
460 # loop over all attributes that are a part of the current class
461 # (not inherited)
462 # FIXME - this really isn't right. Just because an attribute is
463 # defined in a role doesn't mean it isn't _also_ defined in the
464 # subclass.
465 for my $attr (map { $meta->get_attribute($_) } $meta->get_attribute_list) {
466 next if any { $_->has_attribute($attr->name) } @roles;
467
468 return 0;
469 }
470
471 return 1;
472}
473
883c60be 474sub throw {
475 my %args;
476 if (@_ == 1) {
477 $args{message} = shift;
478 }
479 else {
480 %args = @_;
481 }
482
03d53c1c 483 my $superclass = delete($args{superclass}) || 'Throwable::Error';
883c60be 484 my $roles = delete($args{roles});
485
03d53c1c 486 my $metaclass;
487
fe21fa25 488 load_class($superclass);
489
03d53c1c 490 if ($roles) {
491 $metaclass = Moose::Meta::Class->create_anon_class(
492 superclasses => [$superclass],
493 roles => $roles,
494 );
495 }
496 else {
497 $metaclass = Moose::Meta::Class->initialize($superclass);
498 }
883c60be 499
500 $metaclass->name->throw(\%args);
501}
502
503
9a641848 5041;
505
ad46f524 506# ABSTRACT: Utilities for working with Moose classes
507
9a641848 508__END__
509
510=pod
511
9a641848 512=head1 SYNOPSIS
513
6532ca5a 514 use Moose::Util qw/find_meta does_role search_class_by_role/;
515
516 my $meta = find_meta($object) || die "No metaclass found";
9a641848 517
adf82331 518 if (does_role($object, $role)) {
519 print "The object can do $role!\n";
9a641848 520 }
521
1631b53f 522 my $class = search_class_by_role($object, 'FooRole');
523 print "Nearest class with 'FooRole' is $class\n";
524
7125b244 525=head1 DESCRIPTION
526
2c3bf4e7 527This module provides a set of utility functions. Many of these
528functions are intended for use in Moose itself or MooseX modules, but
529some of them may be useful for use in your own code.
7125b244 530
531=head1 EXPORTED FUNCTIONS
9a641848 532
533=over 4
534
2c3bf4e7 535=item B<find_meta($class_or_obj)>
536
537This method takes a class name or object and attempts to find a
3ff98e47 538metaclass for the class, if one exists. It will B<not> create one if it
2c3bf4e7 539does not yet exist.
540
560c498d 541=item B<does_role($class_or_obj, $role_or_obj)>
2c3bf4e7 542
560c498d 543Returns true if C<$class_or_obj> does the given C<$role_or_obj>. The role can
544be provided as a name or a L<Moose::Meta::Role> object.
6532ca5a 545
560c498d 546The class must already have a metaclass for this to work. If it doesn't, this
547function simply returns false.
6532ca5a 548
560c498d 549=item B<search_class_by_role($class_or_obj, $role_or_obj)>
7125b244 550
2c3bf4e7 551Returns the first class in the class's precedence list that does
560c498d 552C<$role_or_obj>, if any. The role can be either a name or a
553L<Moose::Meta::Role> object.
7125b244 554
2c3bf4e7 555The class must already have a metaclass for this to work.
7125b244 556
2c3bf4e7 557=item B<apply_all_roles($applicant, @roles)>
7125b244 558
2c3bf4e7 559This function applies one or more roles to the given C<$applicant> The
560applicant can be a role name, class name, or object.
d7d8a8c7 561
2c3bf4e7 562The C<$applicant> must already have a metaclass object.
563
560c498d 564The list of C<@roles> should a list of names or L<Moose::Meta::Role> objects,
565each of which can be followed by an optional hash reference of options
566(C<-excludes> and C<-alias>).
d7d8a8c7 567
b099a649 568=item B<ensure_all_roles($applicant, @roles)>
569
6f970df6 570This function is similar to C<apply_all_roles>, but only applies roles that
b099a649 571C<$applicant> does not already consume.
572
d26f5671 573=item B<with_traits($class_name, @role_names)>
574
575This function creates a new class from C<$class_name> with each of
576C<@role_names> applied. It returns the name of the new class.
577
ab76842e 578=item B<get_all_attribute_values($meta, $instance)>
579
2c3bf4e7 580Returns a hash reference containing all of the C<$instance>'s
581attributes. The keys are attribute names.
ab76842e 582
583=item B<get_all_init_args($meta, $instance)>
584
2c3bf4e7 585Returns a hash reference containing all of the C<init_arg> values for
586the instance's attributes. The values are the associated attribute
587values. If an attribute does not have a defined C<init_arg>, it is
588skipped.
589
590This could be useful in cloning an object.
ab76842e 591
a3738e5b 592=item B<resolve_metaclass_alias($category, $name, %options)>
593
594=item B<resolve_metatrait_alias($category, $name, %options)>
595
2c3bf4e7 596Resolves a short name to a full class name. Short names are often used
597when specifying the C<metaclass> or C<traits> option for an attribute:
a3738e5b 598
599 has foo => (
600 metaclass => "Bar",
601 );
602
8a8856de 603The name resolution mechanism is covered in
604L<Moose/Metaclass and Trait Name Resolution>.
5f71050b 605
27f2f43f 606=item B<meta_class_alias($to[, $from])>
607
608=item B<meta_attribute_alias($to[, $from])>
609
610Create an alias from the class C<$from> (or the current package, if
611C<$from> is unspecified), so that
612L<Moose/Metaclass and Trait Name Resolution> works properly.
613
57385c0d 614=item B<english_list(@items)>
615
616Given a list of scalars, turns them into a proper list in English
617("one and two", "one, two, three, and four"). This is used to help us
618make nicer error messages.
619
7125b244 620=back
9a641848 621
7125b244 622=head1 TODO
9a641848 623
7125b244 624Here is a list of possible functions to write
9a641848 625
7125b244 626=over 4
1631b53f 627
7125b244 628=item discovering original method from modified method
1631b53f 629
7125b244 630=item search for origin class of a method or attribute
1631b53f 631
9a641848 632=back
633
634=head1 BUGS
635
d4048ef3 636See L<Moose/BUGS> for details on reporting bugs.
9a641848 637
9a641848 638=cut
639