Oops, wrong ticket #
[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
efa728b4 15our $VERSION = '1.15';
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) = @_;
325 my @super_role_metas = $super_meta_name->meta->can('calculate_all_roles_with_inheritance')
326 ? $super_meta_name->meta->calculate_all_roles_with_inheritance
cfa43121 327 : $super_meta_name->meta->can('calculate_all_roles')
328 ? $super_meta_name->meta->calculate_all_roles
61907a02 329 : ();
330 my @role_metas = $class_meta_name->meta->can('calculate_all_roles_with_inheritance')
331 ? $class_meta_name->meta->calculate_all_roles_with_inheritance
cfa43121 332 : $class_meta_name->meta->can('calculate_all_roles')
333 ? $class_meta_name->meta->calculate_all_roles
61907a02 334 : ();
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
452__END__
453
454=pod
455
456=head1 NAME
457
7125b244 458Moose::Util - Utilities for working with Moose classes
9a641848 459
460=head1 SYNOPSIS
461
6532ca5a 462 use Moose::Util qw/find_meta does_role search_class_by_role/;
463
464 my $meta = find_meta($object) || die "No metaclass found";
9a641848 465
adf82331 466 if (does_role($object, $role)) {
467 print "The object can do $role!\n";
9a641848 468 }
469
1631b53f 470 my $class = search_class_by_role($object, 'FooRole');
471 print "Nearest class with 'FooRole' is $class\n";
472
7125b244 473=head1 DESCRIPTION
474
2c3bf4e7 475This module provides a set of utility functions. Many of these
476functions are intended for use in Moose itself or MooseX modules, but
477some of them may be useful for use in your own code.
7125b244 478
479=head1 EXPORTED FUNCTIONS
9a641848 480
481=over 4
482
2c3bf4e7 483=item B<find_meta($class_or_obj)>
484
485This method takes a class name or object and attempts to find a
3ff98e47 486metaclass for the class, if one exists. It will B<not> create one if it
2c3bf4e7 487does not yet exist.
488
560c498d 489=item B<does_role($class_or_obj, $role_or_obj)>
2c3bf4e7 490
560c498d 491Returns true if C<$class_or_obj> does the given C<$role_or_obj>. The role can
492be provided as a name or a L<Moose::Meta::Role> object.
6532ca5a 493
560c498d 494The class must already have a metaclass for this to work. If it doesn't, this
495function simply returns false.
6532ca5a 496
560c498d 497=item B<search_class_by_role($class_or_obj, $role_or_obj)>
7125b244 498
2c3bf4e7 499Returns the first class in the class's precedence list that does
560c498d 500C<$role_or_obj>, if any. The role can be either a name or a
501L<Moose::Meta::Role> object.
7125b244 502
2c3bf4e7 503The class must already have a metaclass for this to work.
7125b244 504
2c3bf4e7 505=item B<apply_all_roles($applicant, @roles)>
7125b244 506
2c3bf4e7 507This function applies one or more roles to the given C<$applicant> The
508applicant can be a role name, class name, or object.
d7d8a8c7 509
2c3bf4e7 510The C<$applicant> must already have a metaclass object.
511
560c498d 512The list of C<@roles> should a list of names or L<Moose::Meta::Role> objects,
513each of which can be followed by an optional hash reference of options
514(C<-excludes> and C<-alias>).
d7d8a8c7 515
b099a649 516=item B<ensure_all_roles($applicant, @roles)>
517
518This function is similar to L</apply_all_roles>, but only applies roles that
519C<$applicant> does not already consume.
520
d26f5671 521=item B<with_traits($class_name, @role_names)>
522
523This function creates a new class from C<$class_name> with each of
524C<@role_names> applied. It returns the name of the new class.
525
ab76842e 526=item B<get_all_attribute_values($meta, $instance)>
527
2c3bf4e7 528Returns a hash reference containing all of the C<$instance>'s
529attributes. The keys are attribute names.
ab76842e 530
531=item B<get_all_init_args($meta, $instance)>
532
2c3bf4e7 533Returns a hash reference containing all of the C<init_arg> values for
534the instance's attributes. The values are the associated attribute
535values. If an attribute does not have a defined C<init_arg>, it is
536skipped.
537
538This could be useful in cloning an object.
ab76842e 539
a3738e5b 540=item B<resolve_metaclass_alias($category, $name, %options)>
541
542=item B<resolve_metatrait_alias($category, $name, %options)>
543
2c3bf4e7 544Resolves a short name to a full class name. Short names are often used
545when specifying the C<metaclass> or C<traits> option for an attribute:
a3738e5b 546
547 has foo => (
548 metaclass => "Bar",
549 );
550
8a8856de 551The name resolution mechanism is covered in
552L<Moose/Metaclass and Trait Name Resolution>.
5f71050b 553
27f2f43f 554=item B<meta_class_alias($to[, $from])>
555
556=item B<meta_attribute_alias($to[, $from])>
557
558Create an alias from the class C<$from> (or the current package, if
559C<$from> is unspecified), so that
560L<Moose/Metaclass and Trait Name Resolution> works properly.
561
57385c0d 562=item B<english_list(@items)>
563
564Given a list of scalars, turns them into a proper list in English
565("one and two", "one, two, three, and four"). This is used to help us
566make nicer error messages.
567
7125b244 568=back
9a641848 569
7125b244 570=head1 TODO
9a641848 571
7125b244 572Here is a list of possible functions to write
9a641848 573
7125b244 574=over 4
1631b53f 575
7125b244 576=item discovering original method from modified method
1631b53f 577
7125b244 578=item search for origin class of a method or attribute
1631b53f 579
9a641848 580=back
581
582=head1 BUGS
583
d4048ef3 584See L<Moose/BUGS> for details on reporting bugs.
9a641848 585
586=head1 AUTHOR
587
588Anders Nor Berle E<lt>debolaz@gmail.comE<gt>
589
7125b244 590B<with contributions from:>
591
592Robert (phaylon) Sedlacek
593
594Stevan Little
595
9a641848 596=head1 COPYRIGHT AND LICENSE
597
2840a3b2 598Copyright 2007-2009 by Infinity Interactive, Inc.
9a641848 599
600L<http://www.iinteractive.com>
601
602This library is free software; you can redistribute it and/or modify
b60c9fa0 603it under the same terms as Perl itself.
9a641848 604
605=cut
606