Speedups in does for MooseX::Storage, should also make protocol buffers suck less...
[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';
2351f08e 8use Class::MOP 0.59;
9a641848 9
2351f08e 10our $VERSION = '0.59';
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
7125b244 24];
9a641848 25
7125b244 26Sub::Exporter::setup_exporter({
27 exports => \@exports,
11065d1f 28 groups => { all => \@exports }
7125b244 29});
30
31## some utils for the utils ...
32
6532ca5a 33sub find_meta {
7125b244 34 return unless $_[0];
d7d8a8c7 35 return Class::MOP::get_metaclass_by_name(blessed($_[0]) || $_[0]);
7125b244 36}
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);
7125b244 44
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) = @_;
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) {
59
6532ca5a 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
d7d8a8c7 72sub apply_all_roles {
73 my $applicant = shift;
e606ae5f 74
c245d69b 75 Moose->throw_error("Must specify at least one role to apply to $applicant") unless @_;
e606ae5f 76
77 my $roles = Data::OptList::mkopt( [@_] );
78
79 my $meta = ( blessed $applicant ? $applicant : find_meta($applicant) );
80
9c10b5ad 81 foreach my $role_spec (@$roles) {
e606ae5f 82 Class::MOP::load_class( $role_spec->[0] );
9c10b5ad 83 }
d7d8a8c7 84
e606ae5f 85 ( $_->[0]->can('meta') && $_->[0]->meta->isa('Moose::Meta::Role') )
c245d69b 86 || Moose->throw_error("You can only consume roles, "
e606ae5f 87 . $_->[0]
4c0b3599 88 . " is not a Moose role")
e606ae5f 89 foreach @$roles;
90
91 if ( scalar @$roles == 1 ) {
92 my ( $role, $params ) = @{ $roles->[0] };
93 $role->meta->apply( $meta, ( defined $params ? %$params : () ) );
d7d8a8c7 94 }
95 else {
e606ae5f 96 Moose::Meta::Role->combine( @$roles )->apply($meta);
97 }
d7d8a8c7 98}
99
ab76842e 100# instance deconstruction ...
101
102sub get_all_attribute_values {
103 my ($class, $instance) = @_;
104 return +{
105 map { $_->name => $_->get_value($instance) }
106 grep { $_->has_value($instance) }
107 $class->compute_all_applicable_attributes
108 };
109}
110
111sub get_all_init_args {
112 my ($class, $instance) = @_;
113 return +{
114 map { $_->init_arg => $_->get_value($instance) }
115 grep { $_->has_value($instance) }
116 grep { defined($_->init_arg) }
117 $class->compute_all_applicable_attributes
118 };
119}
120
b8e4461e 121{ my %cache;
122 sub resolve_metatrait_alias {
123 my ( $type, $metaclass_name ) = @_;
124
125 return $cache{$type}{$metaclass_name} if $cache{$type}{$metaclass_name};
126
127 my $class = resolve_metaclass_alias( @_, trait => 1 );
128 $cache{$type}{$metaclass_name} = $class if $class;
129
130 return $class;
131 }
a3738e5b 132}
133
134sub resolve_metaclass_alias {
135 my ( $type, $metaclass_name, %options ) = @_;
136
b8e4461e 137 my $possible_full_name = 'Moose::Meta::' . $type . '::Custom::' . ( $options{trait} ? "Trait::" : "" ) . $metaclass_name;
138 my $loaded_class = Class::MOP::load_first_existing_class($possible_full_name, $metaclass_name);
a3738e5b 139
b8e4461e 140 $loaded_class->can('register_implementation')
141 ? $loaded_class->register_implementation
142 : $loaded_class;
a3738e5b 143}
144
5f71050b 145sub add_method_modifier {
146 my ( $class_or_obj, $modifier_name, $args ) = @_;
147 my $meta = find_meta($class_or_obj);
148 my $code = pop @{$args};
149 my $add_modifier_method = 'add_' . $modifier_name . '_method_modifier';
150 if ( my $method_modifier_type = ref( @{$args}[0] ) ) {
151 if ( $method_modifier_type eq 'Regexp' ) {
e606ae5f 152 my @all_methods = $meta->get_all_methods;
5f71050b 153 my @matched_methods
e606ae5f 154 = grep { $_->name =~ @{$args}[0] } @all_methods;
155 $meta->$add_modifier_method( $_->name, $code )
5f71050b 156 for @matched_methods;
157 }
158 }
159 else {
160 $meta->$add_modifier_method( $_, $code ) for @{$args};
161 }
162}
d9bb6c63 163
9a641848 1641;
165
166__END__
167
168=pod
169
170=head1 NAME
171
7125b244 172Moose::Util - Utilities for working with Moose classes
9a641848 173
174=head1 SYNOPSIS
175
6532ca5a 176 use Moose::Util qw/find_meta does_role search_class_by_role/;
177
178 my $meta = find_meta($object) || die "No metaclass found";
9a641848 179
adf82331 180 if (does_role($object, $role)) {
181 print "The object can do $role!\n";
9a641848 182 }
183
1631b53f 184 my $class = search_class_by_role($object, 'FooRole');
185 print "Nearest class with 'FooRole' is $class\n";
186
7125b244 187=head1 DESCRIPTION
188
004222dc 189This is a set of utility functions to help working with Moose classes, and
190is used internally by Moose itself. The goal is to provide useful functions
191that for both Moose users and Moose extenders (MooseX:: authors).
192
193This is a relatively new addition to the Moose toolchest, so ideas,
194suggestions and contributions to this collection are most welcome.
195See the L<TODO> section below for a list of ideas for possible functions
196to write.
7125b244 197
198=head1 EXPORTED FUNCTIONS
9a641848 199
200=over 4
201
6532ca5a 202=item B<find_meta ($class_or_obj)>
203
204This will attempt to locate a metaclass for the given C<$class_or_obj>
205and return it.
206
7125b244 207=item B<does_role ($class_or_obj, $role_name)>
208
209Returns true if C<$class_or_obj> can do the role C<$role_name>.
210
211=item B<search_class_by_role ($class_or_obj, $role_name)>
212
213Returns first class in precedence list that consumed C<$role_name>.
214
d7d8a8c7 215=item B<apply_all_roles ($applicant, @roles)>
216
217Given an C<$applicant> (which can somehow be turned into either a
218metaclass or a metarole) and a list of C<@roles> this will do the
219right thing to apply the C<@roles> to the C<$applicant>. This is
3bb22459 220actually used internally by both L<Moose> and L<Moose::Role>, and the
221C<@roles> will be pre-processed through L<Data::OptList::mkopt>
222to allow for the additional arguments to be passed.
d7d8a8c7 223
ab76842e 224=item B<get_all_attribute_values($meta, $instance)>
225
226Returns the values of the C<$instance>'s fields keyed by the attribute names.
227
228=item B<get_all_init_args($meta, $instance)>
229
230Returns a hash reference where the keys are all the attributes' C<init_arg>s
231and the values are the instance's fields. Attributes without an C<init_arg>
232will be skipped.
233
a3738e5b 234=item B<resolve_metaclass_alias($category, $name, %options)>
235
236=item B<resolve_metatrait_alias($category, $name, %options)>
237
238Resolve a short name like in e.g.
239
240 has foo => (
241 metaclass => "Bar",
242 );
243
244to a full class name.
245
5f71050b 246=item B<add_method_modifier ($class_or_obj, $modifier_name, $args)>
247
7125b244 248=back
9a641848 249
7125b244 250=head1 TODO
9a641848 251
7125b244 252Here is a list of possible functions to write
9a641848 253
7125b244 254=over 4
1631b53f 255
7125b244 256=item discovering original method from modified method
1631b53f 257
7125b244 258=item search for origin class of a method or attribute
1631b53f 259
9a641848 260=back
261
262=head1 BUGS
263
264All complex software has bugs lurking in it, and this module is no
265exception. If you find a bug please either email me, or add the bug
266to cpan-RT.
267
268=head1 AUTHOR
269
270Anders Nor Berle E<lt>debolaz@gmail.comE<gt>
271
7125b244 272B<with contributions from:>
273
274Robert (phaylon) Sedlacek
275
276Stevan Little
277
9a641848 278=head1 COPYRIGHT AND LICENSE
279
778db3ac 280Copyright 2007-2008 by Infinity Interactive, Inc.
9a641848 281
282L<http://www.iinteractive.com>
283
284This library is free software; you can redistribute it and/or modify
285it under the same terms as Perl itself.
286
287=cut
288