Beginning of dzilization
[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
7125b244 15our $AUTHORITY = 'cpan:STEVAN';
9a641848 16
7125b244 17my @exports = qw[
d03bd989 18 find_meta
6532ca5a 19 does_role
d03bd989 20 search_class_by_role
b099a649 21 ensure_all_roles
d7d8a8c7 22 apply_all_roles
d26f5671 23 with_traits
ab76842e 24 get_all_init_args
25 get_all_attribute_values
a5e883ae 26 resolve_metatrait_alias
27 resolve_metaclass_alias
5f71050b 28 add_method_modifier
d939e016 29 english_list
27f2f43f 30 meta_attribute_alias
31 meta_class_alias
7125b244 32];
9a641848 33
7125b244 34Sub::Exporter::setup_exporter({
35 exports => \@exports,
11065d1f 36 groups => { all => \@exports }
7125b244 37});
38
39## some utils for the utils ...
40
56ea1a11 41sub find_meta { Class::MOP::class_of(@_) }
9a641848 42
7125b244 43## the functions ...
adf82331 44
7125b244 45sub does_role {
46 my ($class_or_obj, $role) = @_;
adf82331 47
6532ca5a 48 my $meta = find_meta($class_or_obj);
d03bd989 49
7125b244 50 return unless defined $meta;
10a745f5 51 return unless $meta->can('does_role');
7125b244 52 return 1 if $meta->does_role($role);
53 return;
9a641848 54}
55
1631b53f 56sub search_class_by_role {
560c498d 57 my ($class_or_obj, $role) = @_;
d03bd989 58
6532ca5a 59 my $meta = find_meta($class_or_obj);
7125b244 60
61 return unless defined $meta;
62
560c498d 63 my $role_name = blessed $role ? $role->name : $role;
64
7125b244 65 foreach my $class ($meta->class_precedence_list) {
d03bd989 66
67 my $_meta = find_meta($class);
1631b53f 68
7125b244 69 next unless defined $_meta;
70
71 foreach my $role (@{ $_meta->roles || [] }) {
1631b53f 72 return $class if $role->name eq $role_name;
73 }
74 }
75
7125b244 76 return;
1631b53f 77}
78
b099a649 79# this can possibly behave in unexpected ways because the roles being composed
80# before being applied could differ from call to call; I'm not sure if or how
81# to document this possible quirk.
82sub ensure_all_roles {
83 my $applicant = shift;
84 _apply_all_roles($applicant, sub { !does_role($applicant, $_) }, @_);
85}
86
d7d8a8c7 87sub apply_all_roles {
88 my $applicant = shift;
8d4d1cdc 89 _apply_all_roles($applicant, undef, @_);
b099a649 90}
91
92sub _apply_all_roles {
93 my $applicant = shift;
94 my $role_filter = shift;
e606ae5f 95
70ea9161 96 unless (@_) {
97 require Moose;
98 Moose->throw_error("Must specify at least one role to apply to $applicant");
99 }
e606ae5f 100
101 my $roles = Data::OptList::mkopt( [@_] );
102
560c498d 103 my @role_metas;
70ea9161 104 foreach my $role (@$roles) {
560c498d 105 my $meta;
106
107 if ( blessed $role->[0] ) {
108 $meta = $role->[0];
109 }
110 else {
2e7f6cf4 111 Class::MOP::load_class( $role->[0] , $role->[1] );
560c498d 112 $meta = Class::MOP::class_of( $role->[0] );
113 }
70ea9161 114
c8d9f1e2 115 unless ($meta && $meta->isa('Moose::Meta::Role') ) {
70ea9161 116 require Moose;
117 Moose->throw_error( "You can only consume roles, "
118 . $role->[0]
119 . " is not a Moose role" );
120 }
560c498d 121
122 push @role_metas, [ $meta, $role->[1] ];
70ea9161 123 }
e606ae5f 124
82a24871 125 if ( defined $role_filter ) {
560c498d 126 @role_metas = grep { local $_ = $_->[0]; $role_filter->() } @role_metas;
8d4d1cdc 127 }
b099a649 128
560c498d 129 return unless @role_metas;
b099a649 130
6fab0b75 131 my $meta = ( blessed $applicant ? $applicant : find_meta($applicant) );
132
560c498d 133 if ( scalar @role_metas == 1 ) {
134 my ( $role, $params ) = @{ $role_metas[0] };
135 $role->apply( $meta, ( defined $params ? %$params : () ) );
d7d8a8c7 136 }
137 else {
560c498d 138 Moose::Meta::Role->combine(@role_metas)->apply($meta);
e606ae5f 139 }
d7d8a8c7 140}
141
d26f5671 142sub with_traits {
143 my ($class, @roles) = @_;
144 return $class unless @roles;
145 return Moose::Meta::Class->create_anon_class(
146 superclasses => [$class],
147 roles => \@roles,
148 cache => 1,
149 )->name;
150}
151
ab76842e 152# instance deconstruction ...
153
154sub get_all_attribute_values {
155 my ($class, $instance) = @_;
156 return +{
157 map { $_->name => $_->get_value($instance) }
158 grep { $_->has_value($instance) }
b2df9268 159 $class->get_all_attributes
ab76842e 160 };
161}
162
163sub get_all_init_args {
164 my ($class, $instance) = @_;
165 return +{
166 map { $_->init_arg => $_->get_value($instance) }
167 grep { $_->has_value($instance) }
d03bd989 168 grep { defined($_->init_arg) }
b2df9268 169 $class->get_all_attributes
ab76842e 170 };
171}
172
50fbbf3d 173sub resolve_metatrait_alias {
50fbbf3d 174 return resolve_metaclass_alias( @_, trait => 1 );
a3738e5b 175}
176
27f2f43f 177sub _build_alias_package_name {
178 my ($type, $name, $trait) = @_;
179 return 'Moose::Meta::'
180 . $type
181 . '::Custom::'
182 . ( $trait ? 'Trait::' : '' )
183 . $name;
184}
185
50fbbf3d 186{
187 my %cache;
a3738e5b 188
50fbbf3d 189 sub resolve_metaclass_alias {
190 my ( $type, $metaclass_name, %options ) = @_;
a3738e5b 191
50fbbf3d 192 my $cache_key = $type . q{ } . ( $options{trait} ? '-Trait' : '' );
193 return $cache{$cache_key}{$metaclass_name}
194 if $cache{$cache_key}{$metaclass_name};
195
27f2f43f 196 my $possible_full_name = _build_alias_package_name(
197 $type, $metaclass_name, $options{trait}
198 );
50fbbf3d 199
200 my $loaded_class = Class::MOP::load_first_existing_class(
201 $possible_full_name,
202 $metaclass_name
203 );
204
205 return $cache{$cache_key}{$metaclass_name}
206 = $loaded_class->can('register_implementation')
207 ? $loaded_class->register_implementation
208 : $loaded_class;
209 }
a3738e5b 210}
211
5f71050b 212sub add_method_modifier {
213 my ( $class_or_obj, $modifier_name, $args ) = @_;
d5447d26 214 my $meta
215 = $class_or_obj->can('add_before_method_modifier')
216 ? $class_or_obj
217 : find_meta($class_or_obj);
5f71050b 218 my $code = pop @{$args};
219 my $add_modifier_method = 'add_' . $modifier_name . '_method_modifier';
220 if ( my $method_modifier_type = ref( @{$args}[0] ) ) {
221 if ( $method_modifier_type eq 'Regexp' ) {
e606ae5f 222 my @all_methods = $meta->get_all_methods;
5f71050b 223 my @matched_methods
e606ae5f 224 = grep { $_->name =~ @{$args}[0] } @all_methods;
225 $meta->$add_modifier_method( $_->name, $code )
5f71050b 226 for @matched_methods;
227 }
775666aa 228 elsif ($method_modifier_type eq 'ARRAY') {
229 $meta->$add_modifier_method( $_, $code ) for @{$args->[0]};
230 }
231 else {
232 $meta->throw_error(
233 sprintf(
234 "Methods passed to %s must be provided as a list, arrayref or regex, not %s",
235 $modifier_name,
236 $method_modifier_type,
237 )
238 );
239 }
5f71050b 240 }
241 else {
242 $meta->$add_modifier_method( $_, $code ) for @{$args};
243 }
244}
d9bb6c63 245
d939e016 246sub english_list {
247 my @items = sort @_;
248
249 return $items[0] if @items == 1;
250 return "$items[0] and $items[1]" if @items == 2;
251
252 my $tail = pop @items;
253 my $list = join ', ', @items;
254 $list .= ', and ' . $tail;
255
256 return $list;
257}
258
833b56a7 259sub _caller_info {
260 my $level = @_ ? ($_[0] + 1) : 2;
261 my %info;
262 @info{qw(package file line)} = caller($level);
263 return \%info;
264}
265
27f2f43f 266sub _create_alias {
267 my ($type, $name, $trait, $for) = @_;
268 my $package = _build_alias_package_name($type, $name, $trait);
269 Class::MOP::Class->initialize($package)->add_method(
270 register_implementation => sub { $for }
271 );
272}
273
274sub meta_attribute_alias {
275 my ($to, $from) = @_;
276 $from ||= caller;
277 my $meta = Class::MOP::class_of($from);
278 my $trait = $meta->isa('Moose::Meta::Role');
279 _create_alias('Attribute', $to, $trait, $from);
280}
281
282sub meta_class_alias {
283 my ($to, $from) = @_;
284 $from ||= caller;
285 my $meta = Class::MOP::class_of($from);
286 my $trait = $meta->isa('Moose::Meta::Role');
287 _create_alias('Class', $to, $trait, $from);
288}
289
88e88a7b 290# XXX - this should be added to Params::Util
5394a1c7 291sub _STRINGLIKE0 ($) {
88e88a7b 292 return _STRING( $_[0] )
5394a1c7 293 || ( defined $_[0]
294 && $_[0] eq q{} )
88e88a7b 295 || ( blessed $_[0]
296 && overload::Method( $_[0], q{""} )
297 && length "$_[0]" );
298}
299
61907a02 300sub _reconcile_roles_for_metaclass {
301 my ($class_meta_name, $super_meta_name) = @_;
302
303 my @role_differences = _role_differences(
304 $class_meta_name, $super_meta_name,
305 );
306
307 # handle the case where we need to fix compatibility between a class and
308 # its parent, but all roles in the class are already also done by the
309 # parent
310 # see t/050/054.t
311 return $super_meta_name
312 unless @role_differences;
313
314 return Moose::Meta::Class->create_anon_class(
315 superclasses => [$super_meta_name],
316 roles => [map { $_->name } @role_differences],
317 cache => 1,
318 )->name;
319}
320
321sub _role_differences {
322 my ($class_meta_name, $super_meta_name) = @_;
6e15f532 323 my @super_role_metas
324 = grep { !$_->isa('Moose::Meta::Role::Composite') }
325 $super_meta_name->meta->can('calculate_all_roles_with_inheritance')
326 ? $super_meta_name->meta->calculate_all_roles_with_inheritance
327 : $super_meta_name->meta->can('calculate_all_roles')
328 ? $super_meta_name->meta->calculate_all_roles
329 : ();
330 my @role_metas
331 = grep { !$_->isa('Moose::Meta::Role::Composite') }
332 $class_meta_name->meta->can('calculate_all_roles_with_inheritance')
333 ? $class_meta_name->meta->calculate_all_roles_with_inheritance
334 : $class_meta_name->meta->can('calculate_all_roles')
335 ? $class_meta_name->meta->calculate_all_roles
336 : ();
61907a02 337 my @differences;
338 for my $role_meta (@role_metas) {
339 push @differences, $role_meta
340 unless any { $_->name eq $role_meta->name } @super_role_metas;
341 }
342 return @differences;
343}
344
d2782813 345sub _classes_differ_by_roles_only {
346 my ( $self_meta_name, $super_meta_name ) = @_;
347
348 my $common_base_name
349 = _find_common_base( $self_meta_name, $super_meta_name );
350
351 return unless defined $common_base_name;
352
353 my @super_meta_name_ancestor_names
354 = _get_ancestors_until( $super_meta_name, $common_base_name );
355 my @class_meta_name_ancestor_names
356 = _get_ancestors_until( $self_meta_name, $common_base_name );
357
358 return
359 unless all { _is_role_only_subclass($_) }
360 @super_meta_name_ancestor_names,
361 @class_meta_name_ancestor_names;
362
363 return 1;
364}
365
366sub _find_common_base {
367 my ($meta1, $meta2) = map { Class::MOP::class_of($_) } @_;
368 return unless defined $meta1 && defined $meta2;
369
370 # FIXME? This doesn't account for multiple inheritance (not sure
371 # if it needs to though). For example, if somewhere in $meta1's
372 # history it inherits from both ClassA and ClassB, and $meta2
373 # inherits from ClassB & ClassA, does it matter? And what crazy
374 # fool would do that anyway?
375
376 my %meta1_parents = map { $_ => 1 } $meta1->linearized_isa;
377
378 return first { $meta1_parents{$_} } $meta2->linearized_isa;
379}
380
381sub _get_ancestors_until {
382 my ($start_name, $until_name) = @_;
383
384 my @ancestor_names;
385 for my $ancestor_name (Class::MOP::class_of($start_name)->linearized_isa) {
386 last if $ancestor_name eq $until_name;
387 push @ancestor_names, $ancestor_name;
388 }
389 return @ancestor_names;
390}
391
392sub _is_role_only_subclass {
393 my ($meta_name) = @_;
394 my $meta = Class::MOP::Class->initialize($meta_name);
395 my @parent_names = $meta->superclasses;
396
397 # XXX: don't feel like messing with multiple inheritance here... what would
398 # that even do?
399 return unless @parent_names == 1;
400 my ($parent_name) = @parent_names;
401 my $parent_meta = Class::MOP::Class->initialize($parent_name);
402
403 # only get the roles attached to this particular class, don't look at
404 # superclasses
405 my @roles = $meta->can('calculate_all_roles')
406 ? $meta->calculate_all_roles
407 : ();
408
409 # it's obviously not a role-only subclass if it doesn't do any roles
410 return unless @roles;
411
412 # loop over all methods that are a part of the current class
413 # (not inherited)
414 for my $method ( $meta->_get_local_methods ) {
415 # always ignore meta
ba7d613d 416 next if $method->isa('Class::MOP::Method::Meta');
d2782813 417 # we'll deal with attributes below
418 next if $method->can('associated_attribute');
419 # if the method comes from a role we consumed, ignore it
420 next if $meta->can('does_role')
421 && $meta->does_role($method->original_package_name);
422 # FIXME - this really isn't right. Just because a modifier is
423 # defined in a role doesn't mean it isn't _also_ defined in the
424 # subclass.
425 next if $method->isa('Class::MOP::Method::Wrapped')
426 && (
427 (!scalar($method->around_modifiers)
428 || any { $_->has_around_method_modifiers($method->name) } @roles)
429 && (!scalar($method->before_modifiers)
430 || any { $_->has_before_method_modifiers($method->name) } @roles)
431 && (!scalar($method->after_modifiers)
432 || any { $_->has_after_method_modifiers($method->name) } @roles)
433 );
434
435 return 0;
436 }
437
438 # loop over all attributes that are a part of the current class
439 # (not inherited)
440 # FIXME - this really isn't right. Just because an attribute is
441 # defined in a role doesn't mean it isn't _also_ defined in the
442 # subclass.
443 for my $attr (map { $meta->get_attribute($_) } $meta->get_attribute_list) {
444 next if any { $_->has_attribute($attr->name) } @roles;
445
446 return 0;
447 }
448
449 return 1;
450}
451
9a641848 4521;
453
ad46f524 454# ABSTRACT: Utilities for working with Moose classes
455
9a641848 456__END__
457
458=pod
459
9a641848 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
9a641848 586=cut
587