bump version
[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
9409e92e 10our $VERSION = '0.72';
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) {
90 unless ( $role->[0]->can('meta')
91 && $role->[0]->meta->isa('Moose::Meta::Role') ) {
92
93 require Moose;
94 Moose->throw_error( "You can only consume roles, "
95 . $role->[0]
96 . " is not a Moose role" );
97 }
98 }
e606ae5f 99
100 if ( scalar @$roles == 1 ) {
101 my ( $role, $params ) = @{ $roles->[0] };
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
004222dc 220This is a set of utility functions to help working with Moose classes, and
221is used internally by Moose itself. The goal is to provide useful functions
222that for both Moose users and Moose extenders (MooseX:: authors).
223
6549b0d1 224This is a relatively new addition to the Moose tool chest, so ideas,
004222dc 225suggestions and contributions to this collection are most welcome.
226See the L<TODO> section below for a list of ideas for possible functions
227to write.
7125b244 228
229=head1 EXPORTED FUNCTIONS
9a641848 230
231=over 4
232
6532ca5a 233=item B<find_meta ($class_or_obj)>
234
235This will attempt to locate a metaclass for the given C<$class_or_obj>
236and return it.
237
7125b244 238=item B<does_role ($class_or_obj, $role_name)>
239
240Returns true if C<$class_or_obj> can do the role C<$role_name>.
241
242=item B<search_class_by_role ($class_or_obj, $role_name)>
243
244Returns first class in precedence list that consumed C<$role_name>.
245
d7d8a8c7 246=item B<apply_all_roles ($applicant, @roles)>
247
248Given an C<$applicant> (which can somehow be turned into either a
249metaclass or a metarole) and a list of C<@roles> this will do the
250right thing to apply the C<@roles> to the C<$applicant>. This is
3bb22459 251actually used internally by both L<Moose> and L<Moose::Role>, and the
6549b0d1 252C<@roles> will be preprocessed through L<Data::OptList::mkopt>
3bb22459 253to allow for the additional arguments to be passed.
d7d8a8c7 254
ab76842e 255=item B<get_all_attribute_values($meta, $instance)>
256
257Returns the values of the C<$instance>'s fields keyed by the attribute names.
258
259=item B<get_all_init_args($meta, $instance)>
260
261Returns a hash reference where the keys are all the attributes' C<init_arg>s
262and the values are the instance's fields. Attributes without an C<init_arg>
263will be skipped.
264
a3738e5b 265=item B<resolve_metaclass_alias($category, $name, %options)>
266
267=item B<resolve_metatrait_alias($category, $name, %options)>
268
269Resolve a short name like in e.g.
270
271 has foo => (
272 metaclass => "Bar",
273 );
274
275to a full class name.
276
5f71050b 277=item B<add_method_modifier ($class_or_obj, $modifier_name, $args)>
278
d939e016 279=item B<english_list(@items)>
280
281Given a list of scalars, turns them into a proper list in English
282("one and two", "one, two, three, and four"). This is used to help us
283make nicer error messages.
284
7125b244 285=back
9a641848 286
7125b244 287=head1 TODO
9a641848 288
7125b244 289Here is a list of possible functions to write
9a641848 290
7125b244 291=over 4
1631b53f 292
7125b244 293=item discovering original method from modified method
1631b53f 294
7125b244 295=item search for origin class of a method or attribute
1631b53f 296
9a641848 297=back
298
299=head1 BUGS
300
301All complex software has bugs lurking in it, and this module is no
302exception. If you find a bug please either email me, or add the bug
303to cpan-RT.
304
305=head1 AUTHOR
306
307Anders Nor Berle E<lt>debolaz@gmail.comE<gt>
308
7125b244 309B<with contributions from:>
310
311Robert (phaylon) Sedlacek
312
313Stevan Little
314
9a641848 315=head1 COPYRIGHT AND LICENSE
316
2840a3b2 317Copyright 2007-2009 by Infinity Interactive, Inc.
9a641848 318
319L<http://www.iinteractive.com>
320
321This library is free software; you can redistribute it and/or modify
322it under the same terms as Perl itself.
323
324=cut
325