bump version to 0.90
[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
41ffa8a4 10our $VERSION = '0.90';
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 );
342c0218 29 my $c;
69e3ab0a 30 if (@$before && @$after) {
31 $modifier_table->{cache} = sub {
342c0218 32 for $c (@$before) { $c->(@_) };
69e3ab0a 33 my @rval;
34 ((defined wantarray) ?
35 ((wantarray) ?
36 (@rval = $around->{cache}->(@_))
37 :
38 ($rval[0] = $around->{cache}->(@_)))
39 :
40 $around->{cache}->(@_));
342c0218 41 for $c (@$after) { $c->(@_) };
69e3ab0a 42 return unless defined wantarray;
43 return wantarray ? @rval : $rval[0];
44 }
45 }
46 elsif (@$before && !@$after) {
47 $modifier_table->{cache} = sub {
342c0218 48 for $c (@$before) { $c->(@_) };
69e3ab0a 49 return $around->{cache}->(@_);
50 }
51 }
52 elsif (@$after && !@$before) {
53 $modifier_table->{cache} = sub {
54 my @rval;
55 ((defined wantarray) ?
56 ((wantarray) ?
57 (@rval = $around->{cache}->(@_))
58 :
59 ($rval[0] = $around->{cache}->(@_)))
60 :
61 $around->{cache}->(@_));
342c0218 62 for $c (@$after) { $c->(@_) };
69e3ab0a 63 return unless defined wantarray;
64 return wantarray ? @rval : $rval[0];
65 }
66 }
67 else {
68 $modifier_table->{cache} = $around->{cache};
69 }
ba38bf08 70};
71
72sub wrap {
4c105333 73 my ( $class, $code, %params ) = @_;
74
69e3ab0a 75 (blessed($code) && $code->isa('Class::MOP::Method'))
76 || confess "Can only wrap blessed CODE";
4c105333 77
69e3ab0a 78 my $modifier_table = {
79 cache => undef,
80 orig => $code,
81 before => [],
82 after => [],
83 around => {
84 cache => $code->body,
85 methods => [],
86 },
87 };
88 $_build_wrapped_method->($modifier_table);
7fe2c0b6 89 return $class->SUPER::wrap(
4c105333 90 sub { $modifier_table->{cache}->(@_) },
91 # get these from the original
92 # unless explicitly overriden
7fe2c0b6 93 package_name => $params{package_name} || $code->package_name,
94 name => $params{name} || $code->name,
95
96 modifier_table => $modifier_table,
4c105333 97 );
7fe2c0b6 98}
99
100sub _new {
101 my $class = shift;
102 return Class::MOP::Class->initialize($class)->new_object(@_)
103 if $class ne __PACKAGE__;
104
105 my $params = @_ == 1 ? $_[0] : {@_};
106
107 return bless {
108 # inherited from Class::MOP::Method
109 'body' => $params->{body},
110 'associated_metaclass' => $params->{associated_metaclass},
111 'package_name' => $params->{package_name},
112 'name' => $params->{name},
113 'original_method' => $params->{original_method},
114
115 # defined in this class
116 'modifier_table' => $params->{modifier_table}
117 } => $class;
ba38bf08 118}
119
120sub get_original_method {
69e3ab0a 121 my $code = shift;
8683db0e 122 $code->{'modifier_table'}->{orig};
ba38bf08 123}
124
125sub add_before_modifier {
69e3ab0a 126 my $code = shift;
127 my $modifier = shift;
8683db0e 128 unshift @{$code->{'modifier_table'}->{before}} => $modifier;
129 $_build_wrapped_method->($code->{'modifier_table'});
ba38bf08 130}
131
b88aa2e8 132sub before_modifiers {
133 my $code = shift;
134 return @{$code->{'modifier_table'}->{before}};
135}
136
ba38bf08 137sub add_after_modifier {
69e3ab0a 138 my $code = shift;
139 my $modifier = shift;
8683db0e 140 push @{$code->{'modifier_table'}->{after}} => $modifier;
141 $_build_wrapped_method->($code->{'modifier_table'});
ba38bf08 142}
143
b88aa2e8 144sub after_modifiers {
145 my $code = shift;
146 return @{$code->{'modifier_table'}->{after}};
147}
148
ba38bf08 149{
69e3ab0a 150 # NOTE:
151 # this is another possible candidate for
152 # optimization as well. There is an overhead
153 # associated with the currying that, if
154 # eliminated might make around modifiers
155 # more manageable.
156 my $compile_around_method = sub {{
157 my $f1 = pop;
158 return $f1 unless @_;
159 my $f2 = pop;
160 push @_, sub { $f2->( $f1, @_ ) };
161 redo;
162 }};
163
164 sub add_around_modifier {
165 my $code = shift;
166 my $modifier = shift;
8683db0e 167 unshift @{$code->{'modifier_table'}->{around}->{methods}} => $modifier;
168 $code->{'modifier_table'}->{around}->{cache} = $compile_around_method->(
169 @{$code->{'modifier_table'}->{around}->{methods}},
170 $code->{'modifier_table'}->{orig}->body
69e3ab0a 171 );
8683db0e 172 $_build_wrapped_method->($code->{'modifier_table'});
69e3ab0a 173 }
ba38bf08 174}
175
b88aa2e8 176sub around_modifiers {
177 my $code = shift;
178 return @{$code->{'modifier_table'}->{around}->{methods}};
179}
180
ba38bf08 1811;
182
183__END__
184
185=pod
186
b7bdffc3 187=head1 NAME
ba38bf08 188
07ba54f8 189Class::MOP::Method::Wrapped - Method Meta Object for methods with before/after/around modifiers
ba38bf08 190
ba38bf08 191=head1 DESCRIPTION
192
07ba54f8 193This is a L<Class::MOP::Method> subclass which implements before,
194after, and around method modifiers.
127d39a7 195
ba38bf08 196=head1 METHODS
197
198=head2 Construction
199
200=over 4
201
07ba54f8 202=item B<< Class::MOP::Method::Wrapped->wrap($metamethod, %options) >>
127d39a7 203
07ba54f8 204This is the constructor. It accepts a L<Class::MOP::Method> object and
205a hash of options.
ba38bf08 206
07ba54f8 207The options are:
ba38bf08 208
07ba54f8 209=over 8
ba38bf08 210
07ba54f8 211=item * name
ba38bf08 212
07ba54f8 213The method name (without a package name). This will be taken from the
214provided L<Class::MOP::Method> object if it is not provided.
127d39a7 215
07ba54f8 216=item * package_name
ba38bf08 217
07ba54f8 218The package name for the method. This will be taken from the provided
219L<Class::MOP::Method> object if it is not provided.
ba38bf08 220
07ba54f8 221=item * associated_metaclass
ba38bf08 222
07ba54f8 223An optional L<Class::MOP::Class> object. This is the metaclass for the
224method's class.
ba38bf08 225
226=back
227
07ba54f8 228=item B<< $metamethod->get_original_method >>
b88aa2e8 229
07ba54f8 230This returns the L<Class::MOP::Method> object that was passed to the
231constructor.
232
233=item B<< $metamethod->add_before_modifier($code) >>
234
235=item B<< $metamethod->add_after_modifier($code) >>
236
237=item B<< $metamethod->add_around_modifier($code) >>
238
239These methods all take a subroutine reference and apply it as a
240modifier to the original method.
241
242=item B<< $metamethod->before_modifiers >>
b88aa2e8 243
07ba54f8 244=item B<< $metamethod->after_modifiers >>
b88aa2e8 245
07ba54f8 246=item B<< $metamethod->around_modifiers >>
b88aa2e8 247
07ba54f8 248These methods all return a list of subroutine references which are
249acting as the specified type of modifier.
b88aa2e8 250
251=back
252
ba38bf08 253=head1 AUTHORS
254
255Stevan Little E<lt>stevan@iinteractive.comE<gt>
256
ba38bf08 257=head1 COPYRIGHT AND LICENSE
258
070bb6c9 259Copyright 2006-2009 by Infinity Interactive, Inc.
ba38bf08 260
261L<http://www.iinteractive.com>
262
263This library is free software; you can redistribute it and/or modify
b7bdffc3 264it under the same terms as Perl itself.
ba38bf08 265
266=cut
267