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