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