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