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