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