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