7 use Params::Util qw( _STRING );
9 use Scalar::Util 'blessed';
10 use List::Util qw(first);
11 use List::MoreUtils qw(any all);
23 get_all_attribute_values
24 resolve_metatrait_alias
25 resolve_metaclass_alias
32 Sub::Exporter::setup_exporter({
34 groups => { all => \@exports }
37 ## some utils for the utils ...
39 sub find_meta { Class::MOP::class_of(@_) }
44 my ($class_or_obj, $role) = @_;
46 my $meta = find_meta($class_or_obj);
48 return unless defined $meta;
49 return unless $meta->can('does_role');
50 return 1 if $meta->does_role($role);
54 sub search_class_by_role {
55 my ($class_or_obj, $role) = @_;
57 my $meta = find_meta($class_or_obj);
59 return unless defined $meta;
61 my $role_name = blessed $role ? $role->name : $role;
63 foreach my $class ($meta->class_precedence_list) {
65 my $_meta = find_meta($class);
67 next unless defined $_meta;
69 foreach my $role (@{ $_meta->roles || [] }) {
70 return $class if $role->name eq $role_name;
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.
80 sub ensure_all_roles {
81 my $applicant = shift;
82 _apply_all_roles($applicant, sub { !does_role($applicant, $_) }, @_);
86 my $applicant = shift;
87 _apply_all_roles($applicant, undef, @_);
90 sub _apply_all_roles {
91 my $applicant = shift;
92 my $role_filter = shift;
96 Moose->throw_error("Must specify at least one role to apply to $applicant");
99 # If @_ contains role meta objects, mkopt will think that they're values,
100 # because they're references. In other words (roleobj1, roleobj2,
101 # roleobj3) will become [ [ roleobj1, roleobj2 ], [ roleobj3, undef ] ]
102 # -- this is no good. We'll preprocess @_ first to eliminate the potential
104 # -- rjbs, 2011-04-08
105 my $roles = Data::OptList::mkopt( [@_], {
108 ! ref $_[0] or blessed($_[0]) && $_[0]->isa('Moose::Meta::Role')
113 foreach my $role (@$roles) {
116 if ( blessed $role->[0] ) {
120 Class::MOP::load_class( $role->[0] , $role->[1] );
121 $meta = Class::MOP::class_of( $role->[0] );
124 unless ($meta && $meta->isa('Moose::Meta::Role') ) {
126 Moose->throw_error( "You can only consume roles, "
128 . " is not a Moose role" );
131 push @role_metas, [ $meta, $role->[1] ];
134 if ( defined $role_filter ) {
135 @role_metas = grep { local $_ = $_->[0]; $role_filter->() } @role_metas;
138 return unless @role_metas;
140 my $meta = ( blessed $applicant ? $applicant : find_meta($applicant) );
142 if ( scalar @role_metas == 1 ) {
143 my ( $role, $params ) = @{ $role_metas[0] };
144 $role->apply( $meta, ( defined $params ? %$params : () ) );
147 Moose::Meta::Role->combine(@role_metas)->apply($meta);
152 my ($class, @roles) = @_;
153 return $class unless @roles;
154 return Moose::Meta::Class->create_anon_class(
155 superclasses => [$class],
161 # instance deconstruction ...
163 sub get_all_attribute_values {
164 my ($class, $instance) = @_;
166 map { $_->name => $_->get_value($instance) }
167 grep { $_->has_value($instance) }
168 $class->get_all_attributes
172 sub get_all_init_args {
173 my ($class, $instance) = @_;
175 map { $_->init_arg => $_->get_value($instance) }
176 grep { $_->has_value($instance) }
177 grep { defined($_->init_arg) }
178 $class->get_all_attributes
182 sub resolve_metatrait_alias {
183 return resolve_metaclass_alias( @_, trait => 1 );
186 sub _build_alias_package_name {
187 my ($type, $name, $trait) = @_;
188 return 'Moose::Meta::'
191 . ( $trait ? 'Trait::' : '' )
198 sub resolve_metaclass_alias {
199 my ( $type, $metaclass_name, %options ) = @_;
201 my $cache_key = $type . q{ } . ( $options{trait} ? '-Trait' : '' );
202 return $cache{$cache_key}{$metaclass_name}
203 if $cache{$cache_key}{$metaclass_name};
205 my $possible_full_name = _build_alias_package_name(
206 $type, $metaclass_name, $options{trait}
209 my $loaded_class = Class::MOP::load_first_existing_class(
214 return $cache{$cache_key}{$metaclass_name}
215 = $loaded_class->can('register_implementation')
216 ? $loaded_class->register_implementation
221 sub add_method_modifier {
222 my ( $class_or_obj, $modifier_name, $args ) = @_;
224 = $class_or_obj->can('add_before_method_modifier')
226 : find_meta($class_or_obj);
227 my $code = pop @{$args};
228 my $add_modifier_method = 'add_' . $modifier_name . '_method_modifier';
229 if ( my $method_modifier_type = ref( @{$args}[0] ) ) {
230 if ( $method_modifier_type eq 'Regexp' ) {
231 my @all_methods = $meta->get_all_methods;
233 = grep { $_->name =~ @{$args}[0] } @all_methods;
234 $meta->$add_modifier_method( $_->name, $code )
235 for @matched_methods;
237 elsif ($method_modifier_type eq 'ARRAY') {
238 $meta->$add_modifier_method( $_, $code ) for @{$args->[0]};
243 "Methods passed to %s must be provided as a list, arrayref or regex, not %s",
245 $method_modifier_type,
251 $meta->$add_modifier_method( $_, $code ) for @{$args};
258 return $items[0] if @items == 1;
259 return "$items[0] and $items[1]" if @items == 2;
261 my $tail = pop @items;
262 my $list = join ', ', @items;
263 $list .= ', and ' . $tail;
269 my $level = @_ ? ($_[0] + 1) : 2;
271 @info{qw(package file line)} = caller($level);
276 my ($type, $name, $trait, $for) = @_;
277 my $package = _build_alias_package_name($type, $name, $trait);
278 Class::MOP::Class->initialize($package)->add_method(
279 register_implementation => sub { $for }
283 sub meta_attribute_alias {
284 my ($to, $from) = @_;
286 my $meta = Class::MOP::class_of($from);
287 my $trait = $meta->isa('Moose::Meta::Role');
288 _create_alias('Attribute', $to, $trait, $from);
291 sub meta_class_alias {
292 my ($to, $from) = @_;
294 my $meta = Class::MOP::class_of($from);
295 my $trait = $meta->isa('Moose::Meta::Role');
296 _create_alias('Class', $to, $trait, $from);
299 # XXX - this should be added to Params::Util
300 sub _STRINGLIKE0 ($) {
301 return _STRING( $_[0] )
305 && overload::Method( $_[0], q{""} )
309 sub _reconcile_roles_for_metaclass {
310 my ($class_meta_name, $super_meta_name) = @_;
312 my @role_differences = _role_differences(
313 $class_meta_name, $super_meta_name,
316 # handle the case where we need to fix compatibility between a class and
317 # its parent, but all roles in the class are already also done by the
319 # see t/metaclasses/metaclass_compat_no_fixing_bug.t
320 return $super_meta_name
321 unless @role_differences;
323 return Moose::Meta::Class->create_anon_class(
324 superclasses => [$super_meta_name],
325 roles => [map { $_->name } @role_differences],
330 sub _role_differences {
331 my ($class_meta_name, $super_meta_name) = @_;
333 = grep { !$_->isa('Moose::Meta::Role::Composite') }
334 $super_meta_name->meta->can('calculate_all_roles_with_inheritance')
335 ? $super_meta_name->meta->calculate_all_roles_with_inheritance
336 : $super_meta_name->meta->can('calculate_all_roles')
337 ? $super_meta_name->meta->calculate_all_roles
340 = grep { !$_->isa('Moose::Meta::Role::Composite') }
341 $class_meta_name->meta->can('calculate_all_roles_with_inheritance')
342 ? $class_meta_name->meta->calculate_all_roles_with_inheritance
343 : $class_meta_name->meta->can('calculate_all_roles')
344 ? $class_meta_name->meta->calculate_all_roles
347 for my $role_meta (@role_metas) {
348 push @differences, $role_meta
349 unless any { $_->name eq $role_meta->name } @super_role_metas;
354 sub _classes_differ_by_roles_only {
355 my ( $self_meta_name, $super_meta_name ) = @_;
358 = _find_common_base( $self_meta_name, $super_meta_name );
360 return unless defined $common_base_name;
362 my @super_meta_name_ancestor_names
363 = _get_ancestors_until( $super_meta_name, $common_base_name );
364 my @class_meta_name_ancestor_names
365 = _get_ancestors_until( $self_meta_name, $common_base_name );
368 unless all { _is_role_only_subclass($_) }
369 @super_meta_name_ancestor_names,
370 @class_meta_name_ancestor_names;
375 sub _find_common_base {
376 my ($meta1, $meta2) = map { Class::MOP::class_of($_) } @_;
377 return unless defined $meta1 && defined $meta2;
379 # FIXME? This doesn't account for multiple inheritance (not sure
380 # if it needs to though). For example, if somewhere in $meta1's
381 # history it inherits from both ClassA and ClassB, and $meta2
382 # inherits from ClassB & ClassA, does it matter? And what crazy
383 # fool would do that anyway?
385 my %meta1_parents = map { $_ => 1 } $meta1->linearized_isa;
387 return first { $meta1_parents{$_} } $meta2->linearized_isa;
390 sub _get_ancestors_until {
391 my ($start_name, $until_name) = @_;
394 for my $ancestor_name (Class::MOP::class_of($start_name)->linearized_isa) {
395 last if $ancestor_name eq $until_name;
396 push @ancestor_names, $ancestor_name;
398 return @ancestor_names;
401 sub _is_role_only_subclass {
402 my ($meta_name) = @_;
403 my $meta = Class::MOP::Class->initialize($meta_name);
404 my @parent_names = $meta->superclasses;
406 # XXX: don't feel like messing with multiple inheritance here... what would
408 return unless @parent_names == 1;
409 my ($parent_name) = @parent_names;
410 my $parent_meta = Class::MOP::Class->initialize($parent_name);
412 # only get the roles attached to this particular class, don't look at
414 my @roles = $meta->can('calculate_all_roles')
415 ? $meta->calculate_all_roles
418 # it's obviously not a role-only subclass if it doesn't do any roles
419 return unless @roles;
421 # loop over all methods that are a part of the current class
423 for my $method ( $meta->_get_local_methods ) {
425 next if $method->isa('Class::MOP::Method::Meta');
426 # we'll deal with attributes below
427 next if $method->can('associated_attribute');
428 # if the method comes from a role we consumed, ignore it
429 next if $meta->can('does_role')
430 && $meta->does_role($method->original_package_name);
431 # FIXME - this really isn't right. Just because a modifier is
432 # defined in a role doesn't mean it isn't _also_ defined in the
434 next if $method->isa('Class::MOP::Method::Wrapped')
436 (!scalar($method->around_modifiers)
437 || any { $_->has_around_method_modifiers($method->name) } @roles)
438 && (!scalar($method->before_modifiers)
439 || any { $_->has_before_method_modifiers($method->name) } @roles)
440 && (!scalar($method->after_modifiers)
441 || any { $_->has_after_method_modifiers($method->name) } @roles)
447 # loop over all attributes that are a part of the current class
449 # FIXME - this really isn't right. Just because an attribute is
450 # defined in a role doesn't mean it isn't _also_ defined in the
452 for my $attr (map { $meta->get_attribute($_) } $meta->get_attribute_list) {
453 next if any { $_->has_attribute($attr->name) } @roles;
463 # ABSTRACT: Utilities for working with Moose classes
471 use Moose::Util qw/find_meta does_role search_class_by_role/;
473 my $meta = find_meta($object) || die "No metaclass found";
475 if (does_role($object, $role)) {
476 print "The object can do $role!\n";
479 my $class = search_class_by_role($object, 'FooRole');
480 print "Nearest class with 'FooRole' is $class\n";
484 This module provides a set of utility functions. Many of these
485 functions are intended for use in Moose itself or MooseX modules, but
486 some of them may be useful for use in your own code.
488 =head1 EXPORTED FUNCTIONS
492 =item B<find_meta($class_or_obj)>
494 This method takes a class name or object and attempts to find a
495 metaclass for the class, if one exists. It will B<not> create one if it
498 =item B<does_role($class_or_obj, $role_or_obj)>
500 Returns true if C<$class_or_obj> does the given C<$role_or_obj>. The role can
501 be provided as a name or a L<Moose::Meta::Role> object.
503 The class must already have a metaclass for this to work. If it doesn't, this
504 function simply returns false.
506 =item B<search_class_by_role($class_or_obj, $role_or_obj)>
508 Returns the first class in the class's precedence list that does
509 C<$role_or_obj>, if any. The role can be either a name or a
510 L<Moose::Meta::Role> object.
512 The class must already have a metaclass for this to work.
514 =item B<apply_all_roles($applicant, @roles)>
516 This function applies one or more roles to the given C<$applicant> The
517 applicant can be a role name, class name, or object.
519 The C<$applicant> must already have a metaclass object.
521 The list of C<@roles> should a list of names or L<Moose::Meta::Role> objects,
522 each of which can be followed by an optional hash reference of options
523 (C<-excludes> and C<-alias>).
525 =item B<ensure_all_roles($applicant, @roles)>
527 This function is similar to L</apply_all_roles>, but only applies roles that
528 C<$applicant> does not already consume.
530 =item B<with_traits($class_name, @role_names)>
532 This function creates a new class from C<$class_name> with each of
533 C<@role_names> applied. It returns the name of the new class.
535 =item B<get_all_attribute_values($meta, $instance)>
537 Returns a hash reference containing all of the C<$instance>'s
538 attributes. The keys are attribute names.
540 =item B<get_all_init_args($meta, $instance)>
542 Returns a hash reference containing all of the C<init_arg> values for
543 the instance's attributes. The values are the associated attribute
544 values. If an attribute does not have a defined C<init_arg>, it is
547 This could be useful in cloning an object.
549 =item B<resolve_metaclass_alias($category, $name, %options)>
551 =item B<resolve_metatrait_alias($category, $name, %options)>
553 Resolves a short name to a full class name. Short names are often used
554 when specifying the C<metaclass> or C<traits> option for an attribute:
560 The name resolution mechanism is covered in
561 L<Moose/Metaclass and Trait Name Resolution>.
563 =item B<meta_class_alias($to[, $from])>
565 =item B<meta_attribute_alias($to[, $from])>
567 Create an alias from the class C<$from> (or the current package, if
568 C<$from> is unspecified), so that
569 L<Moose/Metaclass and Trait Name Resolution> works properly.
571 =item B<english_list(@items)>
573 Given a list of scalars, turns them into a proper list in English
574 ("one and two", "one, two, three, and four"). This is used to help us
575 make nicer error messages.
581 Here is a list of possible functions to write
585 =item discovering original method from modified method
587 =item search for origin class of a method or attribute
593 See L<Moose/BUGS> for details on reporting bugs.