Bump the version # and updates Changes for 0.64_03
[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
df7077cd 10our $VERSION = '0.64_03';
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
111sub add_after_modifier {
69e3ab0a 112 my $code = shift;
113 my $modifier = shift;
8683db0e 114 push @{$code->{'modifier_table'}->{after}} => $modifier;
115 $_build_wrapped_method->($code->{'modifier_table'});
ba38bf08 116}
117
118{
69e3ab0a 119 # NOTE:
120 # this is another possible candidate for
121 # optimization as well. There is an overhead
122 # associated with the currying that, if
123 # eliminated might make around modifiers
124 # more manageable.
125 my $compile_around_method = sub {{
126 my $f1 = pop;
127 return $f1 unless @_;
128 my $f2 = pop;
129 push @_, sub { $f2->( $f1, @_ ) };
130 redo;
131 }};
132
133 sub add_around_modifier {
134 my $code = shift;
135 my $modifier = shift;
8683db0e 136 unshift @{$code->{'modifier_table'}->{around}->{methods}} => $modifier;
137 $code->{'modifier_table'}->{around}->{cache} = $compile_around_method->(
138 @{$code->{'modifier_table'}->{around}->{methods}},
139 $code->{'modifier_table'}->{orig}->body
69e3ab0a 140 );
8683db0e 141 $_build_wrapped_method->($code->{'modifier_table'});
69e3ab0a 142 }
ba38bf08 143}
144
1451;
146
147__END__
148
149=pod
150
b7bdffc3 151=head1 NAME
ba38bf08 152
153Class::MOP::Method::Wrapped - Method Meta Object to handle before/around/after modifiers
154
ba38bf08 155=head1 DESCRIPTION
156
127d39a7 157This is a L<Class::MOP::Method> subclass which provides the funtionality
158to wrap a given CODE reference with before, after and around method modifiers.
159
ba38bf08 160=head1 METHODS
161
162=head2 Construction
163
164=over 4
165
127d39a7 166=item B<wrap ($code)>
167
168This is the constructor, it will return a B<Class::MOP::Method::Wrapped>
169instance that can be used to add before, after and around modifiers to.
ba38bf08 170
171=item B<get_original_method>
172
127d39a7 173This returns the original CODE reference that was provided to the
174constructor.
175
ba38bf08 176=back
177
178=head2 Modifiers
179
127d39a7 180These three methods will add the method modifiers to the wrapped
181CODE reference. For more information on how method modifiers work,
182see the section in L<Class::MOP::Class>.
183
ba38bf08 184=over 4
185
186=item B<add_before_modifier ($code)>
187
188=item B<add_after_modifier ($code)>
189
190=item B<add_around_modifier ($code)>
191
192=back
193
194=head1 AUTHORS
195
196Stevan Little E<lt>stevan@iinteractive.comE<gt>
197
ba38bf08 198=head1 COPYRIGHT AND LICENSE
199
69e3ab0a 200Copyright 2006-2008 by Infinity Interactive, Inc.
ba38bf08 201
202L<http://www.iinteractive.com>
203
204This library is free software; you can redistribute it and/or modify
b7bdffc3 205it under the same terms as Perl itself.
ba38bf08 206
207=cut
208