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