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