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