7 use Params::Util qw( _STRING );
9 use Scalar::Util 'blessed';
10 use List::Util qw(first);
11 use List::MoreUtils qw(any all);
15 our $VERSION = '1.15';
16 $VERSION = eval $VERSION;
17 our $AUTHORITY = 'cpan:STEVAN';
27 get_all_attribute_values
28 resolve_metatrait_alias
29 resolve_metaclass_alias
36 Sub::Exporter::setup_exporter({
38 groups => { all => \@exports }
41 ## some utils for the utils ...
43 sub find_meta { Class::MOP::class_of(@_) }
48 my ($class_or_obj, $role) = @_;
50 my $meta = find_meta($class_or_obj);
52 return unless defined $meta;
53 return unless $meta->can('does_role');
54 return 1 if $meta->does_role($role);
58 sub search_class_by_role {
59 my ($class_or_obj, $role) = @_;
61 my $meta = find_meta($class_or_obj);
63 return unless defined $meta;
65 my $role_name = blessed $role ? $role->name : $role;
67 foreach my $class ($meta->class_precedence_list) {
69 my $_meta = find_meta($class);
71 next unless defined $_meta;
73 foreach my $role (@{ $_meta->roles || [] }) {
74 return $class if $role->name eq $role_name;
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.
84 sub ensure_all_roles {
85 my $applicant = shift;
86 _apply_all_roles($applicant, sub { !does_role($applicant, $_) }, @_);
90 my $applicant = shift;
91 _apply_all_roles($applicant, undef, @_);
94 sub _apply_all_roles {
95 my $applicant = shift;
96 my $role_filter = shift;
100 Moose->throw_error("Must specify at least one role to apply to $applicant");
103 my $roles = Data::OptList::mkopt( [@_] );
106 foreach my $role (@$roles) {
109 if ( blessed $role->[0] ) {
113 Class::MOP::load_class( $role->[0] , $role->[1] );
114 $meta = Class::MOP::class_of( $role->[0] );
117 unless ($meta && $meta->isa('Moose::Meta::Role') ) {
119 Moose->throw_error( "You can only consume roles, "
121 . " is not a Moose role" );
124 push @role_metas, [ $meta, $role->[1] ];
127 if ( defined $role_filter ) {
128 @role_metas = grep { local $_ = $_->[0]; $role_filter->() } @role_metas;
131 return unless @role_metas;
133 my $meta = ( blessed $applicant ? $applicant : find_meta($applicant) );
135 if ( scalar @role_metas == 1 ) {
136 my ( $role, $params ) = @{ $role_metas[0] };
137 $role->apply( $meta, ( defined $params ? %$params : () ) );
140 Moose::Meta::Role->combine(@role_metas)->apply($meta);
145 my ($class, @roles) = @_;
146 return $class unless @roles;
147 return Moose::Meta::Class->create_anon_class(
148 superclasses => [$class],
154 # instance deconstruction ...
156 sub get_all_attribute_values {
157 my ($class, $instance) = @_;
159 map { $_->name => $_->get_value($instance) }
160 grep { $_->has_value($instance) }
161 $class->get_all_attributes
165 sub get_all_init_args {
166 my ($class, $instance) = @_;
168 map { $_->init_arg => $_->get_value($instance) }
169 grep { $_->has_value($instance) }
170 grep { defined($_->init_arg) }
171 $class->get_all_attributes
175 sub resolve_metatrait_alias {
176 return resolve_metaclass_alias( @_, trait => 1 );
179 sub _build_alias_package_name {
180 my ($type, $name, $trait) = @_;
181 return 'Moose::Meta::'
184 . ( $trait ? 'Trait::' : '' )
191 sub resolve_metaclass_alias {
192 my ( $type, $metaclass_name, %options ) = @_;
194 my $cache_key = $type . q{ } . ( $options{trait} ? '-Trait' : '' );
195 return $cache{$cache_key}{$metaclass_name}
196 if $cache{$cache_key}{$metaclass_name};
198 my $possible_full_name = _build_alias_package_name(
199 $type, $metaclass_name, $options{trait}
202 my $loaded_class = Class::MOP::load_first_existing_class(
207 return $cache{$cache_key}{$metaclass_name}
208 = $loaded_class->can('register_implementation')
209 ? $loaded_class->register_implementation
214 sub add_method_modifier {
215 my ( $class_or_obj, $modifier_name, $args ) = @_;
217 = $class_or_obj->can('add_before_method_modifier')
219 : find_meta($class_or_obj);
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' ) {
224 my @all_methods = $meta->get_all_methods;
226 = grep { $_->name =~ @{$args}[0] } @all_methods;
227 $meta->$add_modifier_method( $_->name, $code )
228 for @matched_methods;
230 elsif ($method_modifier_type eq 'ARRAY') {
231 $meta->$add_modifier_method( $_, $code ) for @{$args->[0]};
236 "Methods passed to %s must be provided as a list, arrayref or regex, not %s",
238 $method_modifier_type,
244 $meta->$add_modifier_method( $_, $code ) for @{$args};
251 return $items[0] if @items == 1;
252 return "$items[0] and $items[1]" if @items == 2;
254 my $tail = pop @items;
255 my $list = join ', ', @items;
256 $list .= ', and ' . $tail;
262 my $level = @_ ? ($_[0] + 1) : 2;
264 @info{qw(package file line)} = caller($level);
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 }
276 sub meta_attribute_alias {
277 my ($to, $from) = @_;
279 my $meta = Class::MOP::class_of($from);
280 my $trait = $meta->isa('Moose::Meta::Role');
281 _create_alias('Attribute', $to, $trait, $from);
284 sub meta_class_alias {
285 my ($to, $from) = @_;
287 my $meta = Class::MOP::class_of($from);
288 my $trait = $meta->isa('Moose::Meta::Role');
289 _create_alias('Class', $to, $trait, $from);
292 # XXX - this should be added to Params::Util
293 sub _STRINGLIKE0 ($) {
294 return _STRING( $_[0] )
298 && overload::Method( $_[0], q{""} )
302 sub _reconcile_roles_for_metaclass {
303 my ($class_meta_name, $super_meta_name) = @_;
305 my @role_differences = _role_differences(
306 $class_meta_name, $super_meta_name,
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
313 return $super_meta_name
314 unless @role_differences;
316 return Moose::Meta::Class->create_anon_class(
317 superclasses => [$super_meta_name],
318 roles => [map { $_->name } @role_differences],
323 sub _role_differences {
324 my ($class_meta_name, $super_meta_name) = @_;
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
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
340 for my $role_meta (@role_metas) {
341 push @differences, $role_meta
342 unless any { $_->name eq $role_meta->name } @super_role_metas;
347 sub _classes_differ_by_roles_only {
348 my ( $self_meta_name, $super_meta_name ) = @_;
351 = _find_common_base( $self_meta_name, $super_meta_name );
353 return unless defined $common_base_name;
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 );
361 unless all { _is_role_only_subclass($_) }
362 @super_meta_name_ancestor_names,
363 @class_meta_name_ancestor_names;
368 sub _find_common_base {
369 my ($meta1, $meta2) = map { Class::MOP::class_of($_) } @_;
370 return unless defined $meta1 && defined $meta2;
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?
378 my %meta1_parents = map { $_ => 1 } $meta1->linearized_isa;
380 return first { $meta1_parents{$_} } $meta2->linearized_isa;
383 sub _get_ancestors_until {
384 my ($start_name, $until_name) = @_;
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;
391 return @ancestor_names;
394 sub _is_role_only_subclass {
395 my ($meta_name) = @_;
396 my $meta = Class::MOP::Class->initialize($meta_name);
397 my @parent_names = $meta->superclasses;
399 # XXX: don't feel like messing with multiple inheritance here... what would
401 return unless @parent_names == 1;
402 my ($parent_name) = @parent_names;
403 my $parent_meta = Class::MOP::Class->initialize($parent_name);
405 # only get the roles attached to this particular class, don't look at
407 my @roles = $meta->can('calculate_all_roles')
408 ? $meta->calculate_all_roles
411 # it's obviously not a role-only subclass if it doesn't do any roles
412 return unless @roles;
414 # loop over all methods that are a part of the current class
416 for my $method ( $meta->_get_local_methods ) {
418 next if $method->isa('Class::MOP::Method::Meta');
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
427 next if $method->isa('Class::MOP::Method::Wrapped')
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)
440 # loop over all attributes that are a part of the current class
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
445 for my $attr (map { $meta->get_attribute($_) } $meta->get_attribute_list) {
446 next if any { $_->has_attribute($attr->name) } @roles;
462 Moose::Util - Utilities for working with Moose classes
466 use Moose::Util qw/find_meta does_role search_class_by_role/;
468 my $meta = find_meta($object) || die "No metaclass found";
470 if (does_role($object, $role)) {
471 print "The object can do $role!\n";
474 my $class = search_class_by_role($object, 'FooRole');
475 print "Nearest class with 'FooRole' is $class\n";
479 This module provides a set of utility functions. Many of these
480 functions are intended for use in Moose itself or MooseX modules, but
481 some of them may be useful for use in your own code.
483 =head1 EXPORTED FUNCTIONS
487 =item B<find_meta($class_or_obj)>
489 This method takes a class name or object and attempts to find a
490 metaclass for the class, if one exists. It will B<not> create one if it
493 =item B<does_role($class_or_obj, $role_or_obj)>
495 Returns true if C<$class_or_obj> does the given C<$role_or_obj>. The role can
496 be provided as a name or a L<Moose::Meta::Role> object.
498 The class must already have a metaclass for this to work. If it doesn't, this
499 function simply returns false.
501 =item B<search_class_by_role($class_or_obj, $role_or_obj)>
503 Returns the first class in the class's precedence list that does
504 C<$role_or_obj>, if any. The role can be either a name or a
505 L<Moose::Meta::Role> object.
507 The class must already have a metaclass for this to work.
509 =item B<apply_all_roles($applicant, @roles)>
511 This function applies one or more roles to the given C<$applicant> The
512 applicant can be a role name, class name, or object.
514 The C<$applicant> must already have a metaclass object.
516 The list of C<@roles> should a list of names or L<Moose::Meta::Role> objects,
517 each of which can be followed by an optional hash reference of options
518 (C<-excludes> and C<-alias>).
520 =item B<ensure_all_roles($applicant, @roles)>
522 This function is similar to L</apply_all_roles>, but only applies roles that
523 C<$applicant> does not already consume.
525 =item B<with_traits($class_name, @role_names)>
527 This function creates a new class from C<$class_name> with each of
528 C<@role_names> applied. It returns the name of the new class.
530 =item B<get_all_attribute_values($meta, $instance)>
532 Returns a hash reference containing all of the C<$instance>'s
533 attributes. The keys are attribute names.
535 =item B<get_all_init_args($meta, $instance)>
537 Returns a hash reference containing all of the C<init_arg> values for
538 the instance's attributes. The values are the associated attribute
539 values. If an attribute does not have a defined C<init_arg>, it is
542 This could be useful in cloning an object.
544 =item B<resolve_metaclass_alias($category, $name, %options)>
546 =item B<resolve_metatrait_alias($category, $name, %options)>
548 Resolves a short name to a full class name. Short names are often used
549 when specifying the C<metaclass> or C<traits> option for an attribute:
555 The name resolution mechanism is covered in
556 L<Moose/Metaclass and Trait Name Resolution>.
558 =item B<meta_class_alias($to[, $from])>
560 =item B<meta_attribute_alias($to[, $from])>
562 Create an alias from the class C<$from> (or the current package, if
563 C<$from> is unspecified), so that
564 L<Moose/Metaclass and Trait Name Resolution> works properly.
566 =item B<english_list(@items)>
568 Given 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
570 make nicer error messages.
576 Here is a list of possible functions to write
580 =item discovering original method from modified method
582 =item search for origin class of a method or attribute
588 See L<Moose/BUGS> for details on reporting bugs.
592 Anders Nor Berle E<lt>debolaz@gmail.comE<gt>
594 B<with contributions from:>
596 Robert (phaylon) Sedlacek
600 =head1 COPYRIGHT AND LICENSE
602 Copyright 2007-2009 by Infinity Interactive, Inc.
604 L<http://www.iinteractive.com>
606 This library is free software; you can redistribute it and/or modify
607 it under the same terms as Perl itself.