bump version to 0.87
[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
92d82041 11our $VERSION = '0.87';
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
833b56a7 210sub _caller_info {
211 my $level = @_ ? ($_[0] + 1) : 2;
212 my %info;
213 @info{qw(package file line)} = caller($level);
214 return \%info;
215}
216
9a641848 2171;
218
219__END__
220
221=pod
222
223=head1 NAME
224
7125b244 225Moose::Util - Utilities for working with Moose classes
9a641848 226
227=head1 SYNOPSIS
228
6532ca5a 229 use Moose::Util qw/find_meta does_role search_class_by_role/;
230
231 my $meta = find_meta($object) || die "No metaclass found";
9a641848 232
adf82331 233 if (does_role($object, $role)) {
234 print "The object can do $role!\n";
9a641848 235 }
236
1631b53f 237 my $class = search_class_by_role($object, 'FooRole');
238 print "Nearest class with 'FooRole' is $class\n";
239
7125b244 240=head1 DESCRIPTION
241
2c3bf4e7 242This module provides a set of utility functions. Many of these
243functions are intended for use in Moose itself or MooseX modules, but
244some of them may be useful for use in your own code.
7125b244 245
246=head1 EXPORTED FUNCTIONS
9a641848 247
248=over 4
249
2c3bf4e7 250=item B<find_meta($class_or_obj)>
251
252This method takes a class name or object and attempts to find a
3ff98e47 253metaclass for the class, if one exists. It will B<not> create one if it
2c3bf4e7 254does not yet exist.
255
256=item B<does_role($class_or_obj, $role_name)>
257
258Returns true if C<$class_or_obj> does the given C<$role_name>.
6532ca5a 259
2c3bf4e7 260The class must already have a metaclass for this to work.
6532ca5a 261
2c3bf4e7 262=item B<search_class_by_role($class_or_obj, $role_name)>
7125b244 263
2c3bf4e7 264Returns the first class in the class's precedence list that does
265C<$role_name>, if any.
7125b244 266
2c3bf4e7 267The class must already have a metaclass for this to work.
7125b244 268
2c3bf4e7 269=item B<apply_all_roles($applicant, @roles)>
7125b244 270
2c3bf4e7 271This function applies one or more roles to the given C<$applicant> The
272applicant can be a role name, class name, or object.
d7d8a8c7 273
2c3bf4e7 274The C<$applicant> must already have a metaclass object.
275
276The list of C<@roles> should be a list of names, each of which can be
277followed by an optional hash reference of options (C<exclude> and
278C<alias>).
d7d8a8c7 279
b099a649 280=item B<ensure_all_roles($applicant, @roles)>
281
282This function is similar to L</apply_all_roles>, but only applies roles that
283C<$applicant> does not already consume.
284
ab76842e 285=item B<get_all_attribute_values($meta, $instance)>
286
2c3bf4e7 287Returns a hash reference containing all of the C<$instance>'s
288attributes. The keys are attribute names.
ab76842e 289
290=item B<get_all_init_args($meta, $instance)>
291
2c3bf4e7 292Returns a hash reference containing all of the C<init_arg> values for
293the instance's attributes. The values are the associated attribute
294values. If an attribute does not have a defined C<init_arg>, it is
295skipped.
296
297This could be useful in cloning an object.
ab76842e 298
a3738e5b 299=item B<resolve_metaclass_alias($category, $name, %options)>
300
301=item B<resolve_metatrait_alias($category, $name, %options)>
302
2c3bf4e7 303Resolves a short name to a full class name. Short names are often used
304when specifying the C<metaclass> or C<traits> option for an attribute:
a3738e5b 305
306 has foo => (
307 metaclass => "Bar",
308 );
309
2c3bf4e7 310The name resolution mechanism is covered in L<Moose/Trait Name
311Resolution>.
5f71050b 312
d939e016 313=item B<english_list(@items)>
314
315Given a list of scalars, turns them into a proper list in English
316("one and two", "one, two, three, and four"). This is used to help us
317make nicer error messages.
318
7125b244 319=back
9a641848 320
7125b244 321=head1 TODO
9a641848 322
7125b244 323Here is a list of possible functions to write
9a641848 324
7125b244 325=over 4
1631b53f 326
7125b244 327=item discovering original method from modified method
1631b53f 328
7125b244 329=item search for origin class of a method or attribute
1631b53f 330
9a641848 331=back
332
333=head1 BUGS
334
d03bd989 335All complex software has bugs lurking in it, and this module is no
9a641848 336exception. If you find a bug please either email me, or add the bug
337to cpan-RT.
338
339=head1 AUTHOR
340
341Anders Nor Berle E<lt>debolaz@gmail.comE<gt>
342
7125b244 343B<with contributions from:>
344
345Robert (phaylon) Sedlacek
346
347Stevan Little
348
9a641848 349=head1 COPYRIGHT AND LICENSE
350
2840a3b2 351Copyright 2007-2009 by Infinity Interactive, Inc.
9a641848 352
353L<http://www.iinteractive.com>
354
355This library is free software; you can redistribute it and/or modify
b60c9fa0 356it under the same terms as Perl itself.
9a641848 357
358=cut
359