just some more cleanup
[gitmo/Moose.git] / lib / Moose / Util.pm
1 package Moose::Util;
2
3 use strict;
4 use warnings;
5
6 use Sub::Exporter;
7 use Scalar::Util 'blessed';
8 use Carp         'confess';
9 use Class::MOP   ();
10
11 our $VERSION   = '0.02';
12 our $AUTHORITY = 'cpan:STEVAN';
13
14 my @exports = qw[
15     find_meta 
16     does_role
17     search_class_by_role   
18     apply_all_roles
19 ];
20
21 Sub::Exporter::setup_exporter({
22     exports => \@exports,
23     groups  => { all => \@exports }
24 });
25
26 ## some utils for the utils ...
27
28 sub find_meta { 
29     return unless $_[0];
30     return Class::MOP::get_metaclass_by_name(blessed($_[0]) || $_[0]);
31 }
32
33 ## the functions ...
34
35 sub does_role {
36     my ($class_or_obj, $role) = @_;
37
38     my $meta = find_meta($class_or_obj);
39     
40     return unless defined $meta;
41
42     return 1 if $meta->does_role($role);
43     return;
44 }
45
46 sub search_class_by_role {
47     my ($class_or_obj, $role_name) = @_;
48     
49     my $meta = find_meta($class_or_obj);
50
51     return unless defined $meta;
52
53     foreach my $class ($meta->class_precedence_list) {
54         
55         my $_meta = find_meta($class);        
56
57         next unless defined $_meta;
58
59         foreach my $role (@{ $_meta->roles || [] }) {
60             return $class if $role->name eq $role_name;
61         }
62     }
63
64     return;
65 }
66
67 sub apply_all_roles {
68     my $applicant = shift;
69     
70     confess "Must specify at least one role to apply to $applicant" unless @_;
71     
72     my $roles = Data::OptList::mkopt([ @_ ]);
73     
74     #use Data::Dumper;
75     #warn Dumper $roles;
76     
77     my $meta;
78     if (blessed $applicant                     && 
79         ($applicant->isa('Class::MOP::Class')  || 
80          $applicant->isa('Moose::Meta::Role')) ){
81         $meta = $applicant;
82     }
83     else {
84         $meta = find_meta($applicant);
85     }
86     
87     Class::MOP::load_class($_->[0]) for @$roles;
88     
89     ($_->[0]->can('meta') && $_->[0]->meta->isa('Moose::Meta::Role'))
90         || confess "You can only consume roles, " . $_->[0] . " is not a Moose role"
91             foreach @$roles;
92
93     if (scalar @$roles == 1) {
94         my ($role, $params) = @{$roles->[0]};
95         $role->meta->apply($meta, (defined $params ? %$params : ()));
96     }
97     else {
98         Moose::Meta::Role->combine(
99             @$roles
100         )->apply($meta);
101     }    
102 }
103
104 1;
105
106 __END__
107
108 =pod
109
110 =head1 NAME
111
112 Moose::Util - Utilities for working with Moose classes
113
114 =head1 SYNOPSIS
115
116   use Moose::Util qw/find_meta does_role search_class_by_role/;
117
118   my $meta = find_meta($object) || die "No metaclass found";
119
120   if (does_role($object, $role)) {
121     print "The object can do $role!\n";
122   }
123
124   my $class = search_class_by_role($object, 'FooRole');
125   print "Nearest class with 'FooRole' is $class\n";
126
127 =head1 DESCRIPTION
128
129 This is a set of utility functions to help working with Moose classes. This 
130 is an experimental module, and it's not 100% clear what purpose it will serve. 
131 That said, ideas, suggestions and contributions to this collection are most 
132 welcome. See the L<TODO> section below for a list of ideas for possible 
133 functions to write.
134
135 =head1 EXPORTED FUNCTIONS
136
137 =over 4
138
139 =item B<find_meta ($class_or_obj)>
140
141 This will attempt to locate a metaclass for the given C<$class_or_obj>
142 and return it.
143
144 =item B<does_role ($class_or_obj, $role_name)>
145
146 Returns true if C<$class_or_obj> can do the role C<$role_name>.
147
148 =item B<search_class_by_role ($class_or_obj, $role_name)>
149
150 Returns first class in precedence list that consumed C<$role_name>.
151
152 =item B<apply_all_roles ($applicant, @roles)>
153
154 Given an C<$applicant> (which can somehow be turned into either a 
155 metaclass or a metarole) and a list of C<@roles> this will do the 
156 right thing to apply the C<@roles> to the C<$applicant>. This is 
157 actually used internally by both L<Moose> and L<Moose::Role>.
158
159 =back
160
161 =head1 TODO
162
163 Here is a list of possible functions to write
164
165 =over 4
166
167 =item discovering original method from modified method
168
169 =item search for origin class of a method or attribute
170
171 =back
172
173 =head1 BUGS
174
175 All complex software has bugs lurking in it, and this module is no 
176 exception. If you find a bug please either email me, or add the bug
177 to cpan-RT.
178
179 =head1 AUTHOR
180
181 Anders Nor Berle E<lt>debolaz@gmail.comE<gt>
182
183 B<with contributions from:>
184
185 Robert (phaylon) Sedlacek
186
187 Stevan Little
188
189 =head1 COPYRIGHT AND LICENSE
190
191 Copyright 2007-2008 by Infinity Interactive, Inc.
192
193 L<http://www.iinteractive.com>
194
195 This library is free software; you can redistribute it and/or modify
196 it under the same terms as Perl itself. 
197
198 =cut
199