Don't call an unnecessary role filter
[gitmo/Moose.git] / lib / Moose / Util.pm
CommitLineData
9a641848 1package Moose::Util;
2
9a641848 3use strict;
4use warnings;
5
9ac41ce6 6use Data::OptList;
7125b244 7use Sub::Exporter;
d7d8a8c7 8use Scalar::Util 'blessed';
f5bc97e5 9use Class::MOP 0.60;
9a641848 10
92d82041 11our $VERSION = '0.87';
e606ae5f 12$VERSION = eval $VERSION;
7125b244 13our $AUTHORITY = 'cpan:STEVAN';
9a641848 14
7125b244 15my @exports = qw[
d03bd989 16 find_meta
6532ca5a 17 does_role
d03bd989 18 search_class_by_role
b099a649 19 ensure_all_roles
d7d8a8c7 20 apply_all_roles
ab76842e 21 get_all_init_args
22 get_all_attribute_values
a5e883ae 23 resolve_metatrait_alias
24 resolve_metaclass_alias
5f71050b 25 add_method_modifier
d939e016 26 english_list
7125b244 27];
9a641848 28
7125b244 29Sub::Exporter::setup_exporter({
30 exports => \@exports,
11065d1f 31 groups => { all => \@exports }
7125b244 32});
33
34## some utils for the utils ...
35
56ea1a11 36sub find_meta { Class::MOP::class_of(@_) }
9a641848 37
7125b244 38## the functions ...
adf82331 39
7125b244 40sub does_role {
41 my ($class_or_obj, $role) = @_;
adf82331 42
6532ca5a 43 my $meta = find_meta($class_or_obj);
d03bd989 44
7125b244 45 return unless defined $meta;
10a745f5 46 return unless $meta->can('does_role');
7125b244 47 return 1 if $meta->does_role($role);
48 return;
9a641848 49}
50
1631b53f 51sub search_class_by_role {
7125b244 52 my ($class_or_obj, $role_name) = @_;
d03bd989 53
6532ca5a 54 my $meta = find_meta($class_or_obj);
7125b244 55
56 return unless defined $meta;
57
58 foreach my $class ($meta->class_precedence_list) {
d03bd989 59
60 my $_meta = find_meta($class);
1631b53f 61
7125b244 62 next unless defined $_meta;
63
64 foreach my $role (@{ $_meta->roles || [] }) {
1631b53f 65 return $class if $role->name eq $role_name;
66 }
67 }
68
7125b244 69 return;
1631b53f 70}
71
b099a649 72# this can possibly behave in unexpected ways because the roles being composed
73# before being applied could differ from call to call; I'm not sure if or how
74# to document this possible quirk.
75sub ensure_all_roles {
76 my $applicant = shift;
77 _apply_all_roles($applicant, sub { !does_role($applicant, $_) }, @_);
78}
79
d7d8a8c7 80sub apply_all_roles {
81 my $applicant = shift;
8d4d1cdc 82 _apply_all_roles($applicant, undef, @_);
b099a649 83}
84
85sub _apply_all_roles {
86 my $applicant = shift;
87 my $role_filter = shift;
e606ae5f 88
70ea9161 89 unless (@_) {
90 require Moose;
91 Moose->throw_error("Must specify at least one role to apply to $applicant");
92 }
e606ae5f 93
94 my $roles = Data::OptList::mkopt( [@_] );
95
70ea9161 96 foreach my $role (@$roles) {
c8d9f1e2 97 Class::MOP::load_class( $role->[0] );
98 my $meta = Class::MOP::class_of( $role->[0] );
70ea9161 99
c8d9f1e2 100 unless ($meta && $meta->isa('Moose::Meta::Role') ) {
70ea9161 101 require Moose;
102 Moose->throw_error( "You can only consume roles, "
103 . $role->[0]
104 . " is not a Moose role" );
105 }
106 }
e606ae5f 107
8d4d1cdc 108 if(defined $role_filter){
109 @$roles = grep { local $_ = $_->[0]; $role_filter->() } @$roles;
110 }
b099a649 111
112 return unless @$roles;
113
6fab0b75 114 my $meta = ( blessed $applicant ? $applicant : find_meta($applicant) );
115
e606ae5f 116 if ( scalar @$roles == 1 ) {
117 my ( $role, $params ) = @{ $roles->[0] };
7d27ee95 118 my $role_meta = Class::MOP::class_of($role);
119 $role_meta->apply( $meta, ( defined $params ? %$params : () ) );
d7d8a8c7 120 }
121 else {
e606ae5f 122 Moose::Meta::Role->combine( @$roles )->apply($meta);
123 }
d7d8a8c7 124}
125
ab76842e 126# instance deconstruction ...
127
128sub get_all_attribute_values {
129 my ($class, $instance) = @_;
130 return +{
131 map { $_->name => $_->get_value($instance) }
132 grep { $_->has_value($instance) }
b2df9268 133 $class->get_all_attributes
ab76842e 134 };
135}
136
137sub get_all_init_args {
138 my ($class, $instance) = @_;
139 return +{
140 map { $_->init_arg => $_->get_value($instance) }
141 grep { $_->has_value($instance) }
d03bd989 142 grep { defined($_->init_arg) }
b2df9268 143 $class->get_all_attributes
ab76842e 144 };
145}
146
50fbbf3d 147sub resolve_metatrait_alias {
50fbbf3d 148 return resolve_metaclass_alias( @_, trait => 1 );
a3738e5b 149}
150
50fbbf3d 151{
152 my %cache;
a3738e5b 153
50fbbf3d 154 sub resolve_metaclass_alias {
155 my ( $type, $metaclass_name, %options ) = @_;
a3738e5b 156
50fbbf3d 157 my $cache_key = $type . q{ } . ( $options{trait} ? '-Trait' : '' );
158 return $cache{$cache_key}{$metaclass_name}
159 if $cache{$cache_key}{$metaclass_name};
160
161 my $possible_full_name
d03bd989 162 = 'Moose::Meta::'
50fbbf3d 163 . $type
164 . '::Custom::'
165 . ( $options{trait} ? "Trait::" : "" )
166 . $metaclass_name;
167
168 my $loaded_class = Class::MOP::load_first_existing_class(
169 $possible_full_name,
170 $metaclass_name
171 );
172
173 return $cache{$cache_key}{$metaclass_name}
174 = $loaded_class->can('register_implementation')
175 ? $loaded_class->register_implementation
176 : $loaded_class;
177 }
a3738e5b 178}
179
5f71050b 180sub add_method_modifier {
181 my ( $class_or_obj, $modifier_name, $args ) = @_;
182 my $meta = find_meta($class_or_obj);
183 my $code = pop @{$args};
184 my $add_modifier_method = 'add_' . $modifier_name . '_method_modifier';
185 if ( my $method_modifier_type = ref( @{$args}[0] ) ) {
186 if ( $method_modifier_type eq 'Regexp' ) {
e606ae5f 187 my @all_methods = $meta->get_all_methods;
5f71050b 188 my @matched_methods
e606ae5f 189 = grep { $_->name =~ @{$args}[0] } @all_methods;
190 $meta->$add_modifier_method( $_->name, $code )
5f71050b 191 for @matched_methods;
192 }
193 }
194 else {
195 $meta->$add_modifier_method( $_, $code ) for @{$args};
196 }
197}
d9bb6c63 198
d939e016 199sub english_list {
200 my @items = sort @_;
201
202 return $items[0] if @items == 1;
203 return "$items[0] and $items[1]" if @items == 2;
204
205 my $tail = pop @items;
206 my $list = join ', ', @items;
207 $list .= ', and ' . $tail;
208
209 return $list;
210}
211
833b56a7 212sub _caller_info {
213 my $level = @_ ? ($_[0] + 1) : 2;
214 my %info;
215 @info{qw(package file line)} = caller($level);
216 return \%info;
217}
218
9a641848 2191;
220
221__END__
222
223=pod
224
225=head1 NAME
226
7125b244 227Moose::Util - Utilities for working with Moose classes
9a641848 228
229=head1 SYNOPSIS
230
6532ca5a 231 use Moose::Util qw/find_meta does_role search_class_by_role/;
232
233 my $meta = find_meta($object) || die "No metaclass found";
9a641848 234
adf82331 235 if (does_role($object, $role)) {
236 print "The object can do $role!\n";
9a641848 237 }
238
1631b53f 239 my $class = search_class_by_role($object, 'FooRole');
240 print "Nearest class with 'FooRole' is $class\n";
241
7125b244 242=head1 DESCRIPTION
243
2c3bf4e7 244This module provides a set of utility functions. Many of these
245functions are intended for use in Moose itself or MooseX modules, but
246some of them may be useful for use in your own code.
7125b244 247
248=head1 EXPORTED FUNCTIONS
9a641848 249
250=over 4
251
2c3bf4e7 252=item B<find_meta($class_or_obj)>
253
254This method takes a class name or object and attempts to find a
3ff98e47 255metaclass for the class, if one exists. It will B<not> create one if it
2c3bf4e7 256does not yet exist.
257
258=item B<does_role($class_or_obj, $role_name)>
259
260Returns true if C<$class_or_obj> does the given C<$role_name>.
6532ca5a 261
2c3bf4e7 262The class must already have a metaclass for this to work.
6532ca5a 263
2c3bf4e7 264=item B<search_class_by_role($class_or_obj, $role_name)>
7125b244 265
2c3bf4e7 266Returns the first class in the class's precedence list that does
267C<$role_name>, if any.
7125b244 268
2c3bf4e7 269The class must already have a metaclass for this to work.
7125b244 270
2c3bf4e7 271=item B<apply_all_roles($applicant, @roles)>
7125b244 272
2c3bf4e7 273This function applies one or more roles to the given C<$applicant> The
274applicant can be a role name, class name, or object.
d7d8a8c7 275
2c3bf4e7 276The C<$applicant> must already have a metaclass object.
277
278The list of C<@roles> should be a list of names, each of which can be
ffb800c0 279followed by an optional hash reference of options (C<excludes> and
2c3bf4e7 280C<alias>).
d7d8a8c7 281
b099a649 282=item B<ensure_all_roles($applicant, @roles)>
283
284This function is similar to L</apply_all_roles>, but only applies roles that
285C<$applicant> does not already consume.
286
ab76842e 287=item B<get_all_attribute_values($meta, $instance)>
288
2c3bf4e7 289Returns a hash reference containing all of the C<$instance>'s
290attributes. The keys are attribute names.
ab76842e 291
292=item B<get_all_init_args($meta, $instance)>
293
2c3bf4e7 294Returns a hash reference containing all of the C<init_arg> values for
295the instance's attributes. The values are the associated attribute
296values. If an attribute does not have a defined C<init_arg>, it is
297skipped.
298
299This could be useful in cloning an object.
ab76842e 300
a3738e5b 301=item B<resolve_metaclass_alias($category, $name, %options)>
302
303=item B<resolve_metatrait_alias($category, $name, %options)>
304
2c3bf4e7 305Resolves a short name to a full class name. Short names are often used
306when specifying the C<metaclass> or C<traits> option for an attribute:
a3738e5b 307
308 has foo => (
309 metaclass => "Bar",
310 );
311
2c3bf4e7 312The name resolution mechanism is covered in L<Moose/Trait Name
313Resolution>.
5f71050b 314
d939e016 315=item B<english_list(@items)>
316
317Given a list of scalars, turns them into a proper list in English
318("one and two", "one, two, three, and four"). This is used to help us
319make nicer error messages.
320
7125b244 321=back
9a641848 322
7125b244 323=head1 TODO
9a641848 324
7125b244 325Here is a list of possible functions to write
9a641848 326
7125b244 327=over 4
1631b53f 328
7125b244 329=item discovering original method from modified method
1631b53f 330
7125b244 331=item search for origin class of a method or attribute
1631b53f 332
9a641848 333=back
334
335=head1 BUGS
336
d03bd989 337All complex software has bugs lurking in it, and this module is no
9a641848 338exception. If you find a bug please either email me, or add the bug
339to cpan-RT.
340
341=head1 AUTHOR
342
343Anders Nor Berle E<lt>debolaz@gmail.comE<gt>
344
7125b244 345B<with contributions from:>
346
347Robert (phaylon) Sedlacek
348
349Stevan Little
350
9a641848 351=head1 COPYRIGHT AND LICENSE
352
2840a3b2 353Copyright 2007-2009 by Infinity Interactive, Inc.
9a641848 354
355L<http://www.iinteractive.com>
356
357This library is free software; you can redistribute it and/or modify
b60c9fa0 358it under the same terms as Perl itself.
9a641848 359
360=cut
361