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