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