commit-before-the-branch
[gitmo/Class-MOP.git] / lib / Class / MOP / Method.pm
CommitLineData
8b978dd5 1
2package Class::MOP::Method;
3
4use strict;
5use warnings;
6
2eb717d5 7use Carp 'confess';
aa448b16 8use Scalar::Util 'reftype', 'blessed';
de19f115 9use B 'svref_2object';
2eb717d5 10
f0480c45 11our $VERSION = '0.03';
12our $AUTHORITY = 'cpan:STEVAN';
de19f115 13
14# introspection
2eb717d5 15
727919c5 16sub meta {
17 require Class::MOP::Class;
aa448b16 18 Class::MOP::Class->initialize(blessed($_[0]) || $_[0]);
727919c5 19}
2eb717d5 20
de19f115 21# construction
22
a4258ffd 23sub wrap {
2eb717d5 24 my $class = shift;
25 my $code = shift;
ee5e71d4 26 ('CODE' eq (reftype($code) || ''))
4d47b77f 27 || confess "You must supply a CODE reference to bless, not (" . ($code || 'undef') . ")";
de19f115 28 bless $code => blessed($class) || $class;
29}
30
de19f115 31# informational
32
33sub package_name {
34 my $code = shift;
35 (blessed($code))
36 || confess "Can only ask the package name of a blessed CODE";
37 svref_2object($code)->GV->STASH->NAME;
38}
39
40sub name {
41 my $code = shift;
42 (blessed($code))
43 || confess "Can only ask the package name of a blessed CODE";
44 svref_2object($code)->GV->NAME;
2eb717d5 45}
de19f115 46
96ceced8 47sub fully_qualified_name {
48 my $code = shift;
49 (blessed($code))
50 || confess "Can only ask the package name of a blessed CODE";
51 $code->package_name . '::' . $code->name;
52}
53
ddc8edba 54package Class::MOP::Method::Wrapped;
55
56use strict;
57use warnings;
58
59use Carp 'confess';
60use Scalar::Util 'reftype', 'blessed';
96ceced8 61use Sub::Name 'subname';
ddc8edba 62
63our $VERSION = '0.01';
64
65our @ISA = ('Class::MOP::Method');
66
96ceced8 67# NOTE:
68# this ugly beast is the result of trying
69# to micro optimize this as much as possible
70# while not completely loosing maintainability.
71# At this point it's "fast enough", after all
72# you can't get something for nothing :)
73my $_build_wrapped_method = sub {
74 my $modifier_table = shift;
75 my ($before, $after, $around) = (
76 $modifier_table->{before},
77 $modifier_table->{after},
78 $modifier_table->{around},
79 );
80 if (@$before && @$after) {
81 $modifier_table->{cache} = sub {
82 $_->(@_) for @{$before};
83 my @rval;
84 ((defined wantarray) ?
85 ((wantarray) ?
86 (@rval = $around->{cache}->(@_))
87 :
88 ($rval[0] = $around->{cache}->(@_)))
89 :
90 $around->{cache}->(@_));
91 $_->(@_) for @{$after};
92 return unless defined wantarray;
93 return wantarray ? @rval : $rval[0];
94 }
95 }
96 elsif (@$before && !@$after) {
97 $modifier_table->{cache} = sub {
98 $_->(@_) for @{$before};
99 return $around->{cache}->(@_);
100 }
101 }
102 elsif (@$after && !@$before) {
103 $modifier_table->{cache} = sub {
104 my @rval;
105 ((defined wantarray) ?
106 ((wantarray) ?
107 (@rval = $around->{cache}->(@_))
108 :
109 ($rval[0] = $around->{cache}->(@_)))
110 :
111 $around->{cache}->(@_));
112 $_->(@_) for @{$after};
113 return unless defined wantarray;
114 return wantarray ? @rval : $rval[0];
115 }
116 }
117 else {
118 $modifier_table->{cache} = $around->{cache};
119 }
120};
121
ddc8edba 122my %MODIFIERS;
123
124sub wrap {
125 my $class = shift;
126 my $code = shift;
127 (blessed($code) && $code->isa('Class::MOP::Method'))
128 || confess "Can only wrap blessed CODE";
129 my $modifier_table = {
96ceced8 130 cache => undef,
ddc8edba 131 orig => $code,
132 before => [],
133 after => [],
134 around => {
135 cache => $code,
96ceced8 136 methods => [],
ddc8edba 137 },
138 };
96ceced8 139 $_build_wrapped_method->($modifier_table);
140 my $method = $class->SUPER::wrap(sub { $modifier_table->{cache}->(@_) });
ddc8edba 141 $MODIFIERS{$method} = $modifier_table;
142 $method;
143}
144
195f5bf8 145sub get_original_method {
146 my $code = shift;
147 $MODIFIERS{$code}->{orig}
148 if exists $MODIFIERS{$code};
149}
150
ddc8edba 151sub add_before_modifier {
152 my $code = shift;
153 my $modifier = shift;
154 (exists $MODIFIERS{$code})
155 || confess "You must first wrap your method before adding a modifier";
156 (blessed($code))
157 || confess "Can only ask the package name of a blessed CODE";
158 ('CODE' eq (reftype($code) || ''))
159 || confess "You must supply a CODE reference for a modifier";
160 unshift @{$MODIFIERS{$code}->{before}} => $modifier;
96ceced8 161 $_build_wrapped_method->($MODIFIERS{$code});
ddc8edba 162}
163
164sub add_after_modifier {
165 my $code = shift;
166 my $modifier = shift;
167 (exists $MODIFIERS{$code})
168 || confess "You must first wrap your method before adding a modifier";
169 (blessed($code))
170 || confess "Can only ask the package name of a blessed CODE";
171 ('CODE' eq (reftype($code) || ''))
172 || confess "You must supply a CODE reference for a modifier";
173 push @{$MODIFIERS{$code}->{after}} => $modifier;
96ceced8 174 $_build_wrapped_method->($MODIFIERS{$code});
ddc8edba 175}
176
177{
96ceced8 178 # NOTE:
179 # this is another possible canidate for
180 # optimization as well. There is an overhead
181 # associated with the currying that, if
182 # eliminated might make around modifiers
183 # more manageable.
ddc8edba 184 my $compile_around_method = sub {{
185 my $f1 = pop;
186 return $f1 unless @_;
187 my $f2 = pop;
188 push @_, sub { $f2->( $f1, @_ ) };
189 redo;
190 }};
191
192 sub add_around_modifier {
193 my $code = shift;
194 my $modifier = shift;
195 (exists $MODIFIERS{$code})
196 || confess "You must first wrap your method before adding a modifier";
197 (blessed($code))
198 || confess "Can only ask the package name of a blessed CODE";
199 ('CODE' eq (reftype($code) || ''))
200 || confess "You must supply a CODE reference for a modifier";
201 unshift @{$MODIFIERS{$code}->{around}->{methods}} => $modifier;
202 $MODIFIERS{$code}->{around}->{cache} = $compile_around_method->(
203 @{$MODIFIERS{$code}->{around}->{methods}},
204 $MODIFIERS{$code}->{orig}
205 );
96ceced8 206 $_build_wrapped_method->($MODIFIERS{$code});
ddc8edba 207 }
208}
209
8b978dd5 2101;
211
212__END__
213
214=pod
215
216=head1 NAME
217
218Class::MOP::Method - Method Meta Object
219
220=head1 SYNOPSIS
221
fe122940 222 # ... more to come later maybe
223
8b978dd5 224=head1 DESCRIPTION
225
552e3d24 226The Method Protocol is very small, since methods in Perl 5 are just
227subroutines within the particular package. Basically all we do is to
fe122940 228bless the subroutine.
229
230Currently this package is largely unused. Future plans are to provide
231some very simple introspection methods for the methods themselves.
232Suggestions for this are welcome.
552e3d24 233
2eb717d5 234=head1 METHODS
235
de19f115 236=head2 Introspection
2eb717d5 237
de19f115 238=over 4
fe122940 239
2eb717d5 240=item B<meta>
241
fe122940 242This will return a B<Class::MOP::Class> instance which is related
243to this class.
244
2eb717d5 245=back
246
de19f115 247=head2 Construction
248
249=over 4
250
a4258ffd 251=item B<wrap (&code)>
de19f115 252
253This simply blesses the C<&code> reference passed to it.
254
255=back
256
257=head2 Informational
258
259=over 4
260
261=item B<name>
262
263=item B<package_name>
264
96ceced8 265=item B<fully_qualified_name>
266
267=back
268
269=head1 Class::MOP::Method::Wrapped METHODS
270
271=head2 Construction
272
273=over 4
274
275=item B<wrap (&code)>
276
277This simply blesses the C<&code> reference passed to it.
278
195f5bf8 279=item B<get_original_method>
280
de19f115 281=back
282
ee5e71d4 283=head2 Modifiers
284
285=over 4
de19f115 286
ee5e71d4 287=item B<add_before_modifier ($code)>
de19f115 288
ee5e71d4 289=item B<add_after_modifier ($code)>
290
291=item B<add_around_modifier ($code)>
292
293=back
de19f115 294
1a09d9cc 295=head1 AUTHORS
8b978dd5 296
a2e85e6c 297Stevan Little E<lt>stevan@iinteractive.comE<gt>
8b978dd5 298
1a09d9cc 299Yuval Kogman E<lt>nothingmuch@woobling.comE<gt>
300
8b978dd5 301=head1 COPYRIGHT AND LICENSE
302
303Copyright 2006 by Infinity Interactive, Inc.
304
305L<http://www.iinteractive.com>
306
307This library is free software; you can redistribute it and/or modify
308it under the same terms as Perl itself.
309
16e960bd 310=cut
311