Switch to unshifting message if needed, like most of Moose
[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 {
1924a2bb 475 if (@_ % 2) {
476 unshift @_, 'message';
883c60be 477 }
478
1924a2bb 479 my %args = @_;
480
03d53c1c 481 my $superclass = delete($args{superclass}) || 'Throwable::Error';
883c60be 482 my $roles = delete($args{roles});
483
03d53c1c 484 my $metaclass;
485
fe21fa25 486 load_class($superclass);
487
03d53c1c 488 if ($roles) {
489 $metaclass = Moose::Meta::Class->create_anon_class(
490 superclasses => [$superclass],
491 roles => $roles,
492 );
493 }
494 else {
495 $metaclass = Moose::Meta::Class->initialize($superclass);
496 }
883c60be 497
498 $metaclass->name->throw(\%args);
499}
500
501
9a641848 5021;
503
ad46f524 504# ABSTRACT: Utilities for working with Moose classes
505
9a641848 506__END__
507
508=pod
509
9a641848 510=head1 SYNOPSIS
511
6532ca5a 512 use Moose::Util qw/find_meta does_role search_class_by_role/;
513
514 my $meta = find_meta($object) || die "No metaclass found";
9a641848 515
adf82331 516 if (does_role($object, $role)) {
517 print "The object can do $role!\n";
9a641848 518 }
519
1631b53f 520 my $class = search_class_by_role($object, 'FooRole');
521 print "Nearest class with 'FooRole' is $class\n";
522
7125b244 523=head1 DESCRIPTION
524
2c3bf4e7 525This module provides a set of utility functions. Many of these
526functions are intended for use in Moose itself or MooseX modules, but
527some of them may be useful for use in your own code.
7125b244 528
529=head1 EXPORTED FUNCTIONS
9a641848 530
531=over 4
532
2c3bf4e7 533=item B<find_meta($class_or_obj)>
534
535This method takes a class name or object and attempts to find a
3ff98e47 536metaclass for the class, if one exists. It will B<not> create one if it
2c3bf4e7 537does not yet exist.
538
560c498d 539=item B<does_role($class_or_obj, $role_or_obj)>
2c3bf4e7 540
560c498d 541Returns true if C<$class_or_obj> does the given C<$role_or_obj>. The role can
542be provided as a name or a L<Moose::Meta::Role> object.
6532ca5a 543
560c498d 544The class must already have a metaclass for this to work. If it doesn't, this
545function simply returns false.
6532ca5a 546
560c498d 547=item B<search_class_by_role($class_or_obj, $role_or_obj)>
7125b244 548
2c3bf4e7 549Returns the first class in the class's precedence list that does
560c498d 550C<$role_or_obj>, if any. The role can be either a name or a
551L<Moose::Meta::Role> object.
7125b244 552
2c3bf4e7 553The class must already have a metaclass for this to work.
7125b244 554
2c3bf4e7 555=item B<apply_all_roles($applicant, @roles)>
7125b244 556
2c3bf4e7 557This function applies one or more roles to the given C<$applicant> The
558applicant can be a role name, class name, or object.
d7d8a8c7 559
2c3bf4e7 560The C<$applicant> must already have a metaclass object.
561
560c498d 562The list of C<@roles> should a list of names or L<Moose::Meta::Role> objects,
563each of which can be followed by an optional hash reference of options
564(C<-excludes> and C<-alias>).
d7d8a8c7 565
b099a649 566=item B<ensure_all_roles($applicant, @roles)>
567
6f970df6 568This function is similar to C<apply_all_roles>, but only applies roles that
b099a649 569C<$applicant> does not already consume.
570
d26f5671 571=item B<with_traits($class_name, @role_names)>
572
573This function creates a new class from C<$class_name> with each of
574C<@role_names> applied. It returns the name of the new class.
575
ab76842e 576=item B<get_all_attribute_values($meta, $instance)>
577
2c3bf4e7 578Returns a hash reference containing all of the C<$instance>'s
579attributes. The keys are attribute names.
ab76842e 580
581=item B<get_all_init_args($meta, $instance)>
582
2c3bf4e7 583Returns a hash reference containing all of the C<init_arg> values for
584the instance's attributes. The values are the associated attribute
585values. If an attribute does not have a defined C<init_arg>, it is
586skipped.
587
588This could be useful in cloning an object.
ab76842e 589
a3738e5b 590=item B<resolve_metaclass_alias($category, $name, %options)>
591
592=item B<resolve_metatrait_alias($category, $name, %options)>
593
2c3bf4e7 594Resolves a short name to a full class name. Short names are often used
595when specifying the C<metaclass> or C<traits> option for an attribute:
a3738e5b 596
597 has foo => (
598 metaclass => "Bar",
599 );
600
8a8856de 601The name resolution mechanism is covered in
602L<Moose/Metaclass and Trait Name Resolution>.
5f71050b 603
27f2f43f 604=item B<meta_class_alias($to[, $from])>
605
606=item B<meta_attribute_alias($to[, $from])>
607
608Create an alias from the class C<$from> (or the current package, if
609C<$from> is unspecified), so that
610L<Moose/Metaclass and Trait Name Resolution> works properly.
611
57385c0d 612=item B<english_list(@items)>
613
614Given a list of scalars, turns them into a proper list in English
615("one and two", "one, two, three, and four"). This is used to help us
616make nicer error messages.
617
7125b244 618=back
9a641848 619
7125b244 620=head1 TODO
9a641848 621
7125b244 622Here is a list of possible functions to write
9a641848 623
7125b244 624=over 4
1631b53f 625
7125b244 626=item discovering original method from modified method
1631b53f 627
7125b244 628=item search for origin class of a method or attribute
1631b53f 629
9a641848 630=back
631
632=head1 BUGS
633
d4048ef3 634See L<Moose/BUGS> for details on reporting bugs.
9a641848 635
9a641848 636=cut
637