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