bump version to 0.83
[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
e00524a0 10our $VERSION = '0.83';
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
07ba54f8 168Class::MOP::Method::Wrapped - Method Meta Object for methods with before/after/around modifiers
ba38bf08 169
ba38bf08 170=head1 DESCRIPTION
171
07ba54f8 172This is a L<Class::MOP::Method> subclass which implements before,
173after, and around method modifiers.
127d39a7 174
ba38bf08 175=head1 METHODS
176
177=head2 Construction
178
179=over 4
180
07ba54f8 181=item B<< Class::MOP::Method::Wrapped->wrap($metamethod, %options) >>
127d39a7 182
07ba54f8 183This is the constructor. It accepts a L<Class::MOP::Method> object and
184a hash of options.
ba38bf08 185
07ba54f8 186The options are:
ba38bf08 187
07ba54f8 188=over 8
ba38bf08 189
07ba54f8 190=item * name
ba38bf08 191
07ba54f8 192The method name (without a package name). This will be taken from the
193provided L<Class::MOP::Method> object if it is not provided.
127d39a7 194
07ba54f8 195=item * package_name
ba38bf08 196
07ba54f8 197The package name for the method. This will be taken from the provided
198L<Class::MOP::Method> object if it is not provided.
ba38bf08 199
07ba54f8 200=item * associated_metaclass
ba38bf08 201
07ba54f8 202An optional L<Class::MOP::Class> object. This is the metaclass for the
203method's class.
ba38bf08 204
205=back
206
07ba54f8 207=item B<< $metamethod->get_original_method >>
b88aa2e8 208
07ba54f8 209This returns the L<Class::MOP::Method> object that was passed to the
210constructor.
211
212=item B<< $metamethod->add_before_modifier($code) >>
213
214=item B<< $metamethod->add_after_modifier($code) >>
215
216=item B<< $metamethod->add_around_modifier($code) >>
217
218These methods all take a subroutine reference and apply it as a
219modifier to the original method.
220
221=item B<< $metamethod->before_modifiers >>
b88aa2e8 222
07ba54f8 223=item B<< $metamethod->after_modifiers >>
b88aa2e8 224
07ba54f8 225=item B<< $metamethod->around_modifiers >>
b88aa2e8 226
07ba54f8 227These methods all return a list of subroutine references which are
228acting as the specified type of modifier.
b88aa2e8 229
230=back
231
ba38bf08 232=head1 AUTHORS
233
234Stevan Little E<lt>stevan@iinteractive.comE<gt>
235
ba38bf08 236=head1 COPYRIGHT AND LICENSE
237
070bb6c9 238Copyright 2006-2009 by Infinity Interactive, Inc.
ba38bf08 239
240L<http://www.iinteractive.com>
241
242This library is free software; you can redistribute it and/or modify
b7bdffc3 243it under the same terms as Perl itself.
ba38bf08 244
245=cut
246