bump version to 0.75
[gitmo/Class-MOP.git] / lib / Class / MOP / Method / Wrapped.pm
CommitLineData
ba38bf08 1
2package Class::MOP::Method::Wrapped;
3
4use strict;
5use warnings;
6
7use Carp 'confess';
9b522fc4 8use Scalar::Util 'blessed';
ba38bf08 9
db4c4962 10our $VERSION = '0.75';
d519662a 11$VERSION = eval $VERSION;
ba38bf08 12our $AUTHORITY = 'cpan:STEVAN';
13
b7bdffc3 14use base 'Class::MOP::Method';
ba38bf08 15
16# NOTE:
b7bdffc3 17# this ugly beast is the result of trying
ba38bf08 18# to micro optimize this as much as possible
19# while not completely loosing maintainability.
20# At this point it's "fast enough", after all
21# you can't get something for nothing :)
22my $_build_wrapped_method = sub {
69e3ab0a 23 my $modifier_table = shift;
24 my ($before, $after, $around) = (
25 $modifier_table->{before},
26 $modifier_table->{after},
27 $modifier_table->{around},
28 );
29 if (@$before && @$after) {
30 $modifier_table->{cache} = sub {
31 $_->(@_) for @{$before};
32 my @rval;
33 ((defined wantarray) ?
34 ((wantarray) ?
35 (@rval = $around->{cache}->(@_))
36 :
37 ($rval[0] = $around->{cache}->(@_)))
38 :
39 $around->{cache}->(@_));
40 $_->(@_) for @{$after};
41 return unless defined wantarray;
42 return wantarray ? @rval : $rval[0];
43 }
44 }
45 elsif (@$before && !@$after) {
46 $modifier_table->{cache} = sub {
47 $_->(@_) for @{$before};
48 return $around->{cache}->(@_);
49 }
50 }
51 elsif (@$after && !@$before) {
52 $modifier_table->{cache} = sub {
53 my @rval;
54 ((defined wantarray) ?
55 ((wantarray) ?
56 (@rval = $around->{cache}->(@_))
57 :
58 ($rval[0] = $around->{cache}->(@_)))
59 :
60 $around->{cache}->(@_));
61 $_->(@_) for @{$after};
62 return unless defined wantarray;
63 return wantarray ? @rval : $rval[0];
64 }
65 }
66 else {
67 $modifier_table->{cache} = $around->{cache};
68 }
ba38bf08 69};
70
71sub wrap {
4c105333 72 my ( $class, $code, %params ) = @_;
73
69e3ab0a 74 (blessed($code) && $code->isa('Class::MOP::Method'))
75 || confess "Can only wrap blessed CODE";
4c105333 76
69e3ab0a 77 my $modifier_table = {
78 cache => undef,
79 orig => $code,
80 before => [],
81 after => [],
82 around => {
83 cache => $code->body,
84 methods => [],
85 },
86 };
87 $_build_wrapped_method->($modifier_table);
4c105333 88 my $method = $class->SUPER::wrap(
89 sub { $modifier_table->{cache}->(@_) },
90 # get these from the original
91 # unless explicitly overriden
92 package_name => $params{package_name} || $code->package_name,
93 name => $params{name} || $code->name,
94 );
8683db0e 95 $method->{'modifier_table'} = $modifier_table;
69e3ab0a 96 $method;
ba38bf08 97}
98
99sub get_original_method {
69e3ab0a 100 my $code = shift;
8683db0e 101 $code->{'modifier_table'}->{orig};
ba38bf08 102}
103
104sub add_before_modifier {
69e3ab0a 105 my $code = shift;
106 my $modifier = shift;
8683db0e 107 unshift @{$code->{'modifier_table'}->{before}} => $modifier;
108 $_build_wrapped_method->($code->{'modifier_table'});
ba38bf08 109}
110
b88aa2e8 111sub before_modifiers {
112 my $code = shift;
113 return @{$code->{'modifier_table'}->{before}};
114}
115
ba38bf08 116sub add_after_modifier {
69e3ab0a 117 my $code = shift;
118 my $modifier = shift;
8683db0e 119 push @{$code->{'modifier_table'}->{after}} => $modifier;
120 $_build_wrapped_method->($code->{'modifier_table'});
ba38bf08 121}
122
b88aa2e8 123sub after_modifiers {
124 my $code = shift;
125 return @{$code->{'modifier_table'}->{after}};
126}
127
ba38bf08 128{
69e3ab0a 129 # NOTE:
130 # this is another possible candidate for
131 # optimization as well. There is an overhead
132 # associated with the currying that, if
133 # eliminated might make around modifiers
134 # more manageable.
135 my $compile_around_method = sub {{
136 my $f1 = pop;
137 return $f1 unless @_;
138 my $f2 = pop;
139 push @_, sub { $f2->( $f1, @_ ) };
140 redo;
141 }};
142
143 sub add_around_modifier {
144 my $code = shift;
145 my $modifier = shift;
8683db0e 146 unshift @{$code->{'modifier_table'}->{around}->{methods}} => $modifier;
147 $code->{'modifier_table'}->{around}->{cache} = $compile_around_method->(
148 @{$code->{'modifier_table'}->{around}->{methods}},
149 $code->{'modifier_table'}->{orig}->body
69e3ab0a 150 );
8683db0e 151 $_build_wrapped_method->($code->{'modifier_table'});
69e3ab0a 152 }
ba38bf08 153}
154
b88aa2e8 155sub around_modifiers {
156 my $code = shift;
157 return @{$code->{'modifier_table'}->{around}->{methods}};
158}
159
ba38bf08 1601;
161
162__END__
163
164=pod
165
b7bdffc3 166=head1 NAME
ba38bf08 167
168Class::MOP::Method::Wrapped - Method Meta Object to handle before/around/after modifiers
169
ba38bf08 170=head1 DESCRIPTION
171
127d39a7 172This is a L<Class::MOP::Method> subclass which provides the funtionality
173to wrap a given CODE reference with before, after and around method modifiers.
174
ba38bf08 175=head1 METHODS
176
177=head2 Construction
178
179=over 4
180
127d39a7 181=item B<wrap ($code)>
182
183This is the constructor, it will return a B<Class::MOP::Method::Wrapped>
184instance that can be used to add before, after and around modifiers to.
ba38bf08 185
186=item B<get_original_method>
187
127d39a7 188This returns the original CODE reference that was provided to the
189constructor.
190
ba38bf08 191=back
192
193=head2 Modifiers
194
127d39a7 195These three methods will add the method modifiers to the wrapped
196CODE reference. For more information on how method modifiers work,
197see the section in L<Class::MOP::Class>.
198
ba38bf08 199=over 4
200
201=item B<add_before_modifier ($code)>
202
203=item B<add_after_modifier ($code)>
204
205=item B<add_around_modifier ($code)>
206
207=back
208
b88aa2e8 209These three methods each returna list of method modifiers I<in the
210order in which they are run>.
211
212=over 4
213
214=item B<before_modifiers>
215
216=item B<after_modifiers>
217
218=item B<around_modifiers>
219
220=back
221
ba38bf08 222=head1 AUTHORS
223
224Stevan Little E<lt>stevan@iinteractive.comE<gt>
225
ba38bf08 226=head1 COPYRIGHT AND LICENSE
227
69e3ab0a 228Copyright 2006-2008 by Infinity Interactive, Inc.
ba38bf08 229
230L<http://www.iinteractive.com>
231
232This library is free software; you can redistribute it and/or modify
b7bdffc3 233it under the same terms as Perl itself.
ba38bf08 234
235=cut
236