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