Version 1.12
[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
bd2550f8 10our $VERSION = '1.12';
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 {
2dfbb99a 31 for my $c (@$before) { $c->(@_) };
69e3ab0a 32 my @rval;
33 ((defined wantarray) ?
34 ((wantarray) ?
35 (@rval = $around->{cache}->(@_))
36 :
37 ($rval[0] = $around->{cache}->(@_)))
38 :
39 $around->{cache}->(@_));
2dfbb99a 40 for my $c (@$after) { $c->(@_) };
69e3ab0a 41 return unless defined wantarray;
42 return wantarray ? @rval : $rval[0];
43 }
44 }
45 elsif (@$before && !@$after) {
46 $modifier_table->{cache} = sub {
2dfbb99a 47 for my $c (@$before) { $c->(@_) };
69e3ab0a 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}->(@_));
2dfbb99a 61 for my $c (@$after) { $c->(@_) };
69e3ab0a 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 ) = @_;
2dfbb99a 73
69e3ab0a 74 (blessed($code) && $code->isa('Class::MOP::Method'))
75 || confess "Can only wrap blessed CODE";
2dfbb99a 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);
7fe2c0b6 88 return $class->SUPER::wrap(
4c105333 89 sub { $modifier_table->{cache}->(@_) },
2dfbb99a 90 # get these from the original
4c105333 91 # unless explicitly overriden
7fe2c0b6 92 package_name => $params{package_name} || $code->package_name,
93 name => $params{name} || $code->name,
94
95 modifier_table => $modifier_table,
4c105333 96 );
7fe2c0b6 97}
98
99sub _new {
100 my $class = shift;
101 return Class::MOP::Class->initialize($class)->new_object(@_)
102 if $class ne __PACKAGE__;
103
104 my $params = @_ == 1 ? $_[0] : {@_};
105
106 return bless {
107 # inherited from Class::MOP::Method
108 'body' => $params->{body},
109 'associated_metaclass' => $params->{associated_metaclass},
110 'package_name' => $params->{package_name},
111 'name' => $params->{name},
112 'original_method' => $params->{original_method},
113
114 # defined in this class
115 'modifier_table' => $params->{modifier_table}
116 } => $class;
ba38bf08 117}
118
119sub get_original_method {
69e3ab0a 120 my $code = shift;
8683db0e 121 $code->{'modifier_table'}->{orig};
ba38bf08 122}
123
124sub add_before_modifier {
69e3ab0a 125 my $code = shift;
126 my $modifier = shift;
8683db0e 127 unshift @{$code->{'modifier_table'}->{before}} => $modifier;
128 $_build_wrapped_method->($code->{'modifier_table'});
ba38bf08 129}
130
b88aa2e8 131sub before_modifiers {
132 my $code = shift;
133 return @{$code->{'modifier_table'}->{before}};
134}
135
ba38bf08 136sub add_after_modifier {
69e3ab0a 137 my $code = shift;
138 my $modifier = shift;
8683db0e 139 push @{$code->{'modifier_table'}->{after}} => $modifier;
140 $_build_wrapped_method->($code->{'modifier_table'});
ba38bf08 141}
142
b88aa2e8 143sub after_modifiers {
144 my $code = shift;
145 return @{$code->{'modifier_table'}->{after}};
146}
147
ba38bf08 148{
69e3ab0a 149 # NOTE:
150 # this is another possible candidate for
151 # optimization as well. There is an overhead
152 # associated with the currying that, if
153 # eliminated might make around modifiers
154 # more manageable.
155 my $compile_around_method = sub {{
156 my $f1 = pop;
157 return $f1 unless @_;
158 my $f2 = pop;
159 push @_, sub { $f2->( $f1, @_ ) };
160 redo;
161 }};
162
163 sub add_around_modifier {
164 my $code = shift;
165 my $modifier = shift;
8683db0e 166 unshift @{$code->{'modifier_table'}->{around}->{methods}} => $modifier;
167 $code->{'modifier_table'}->{around}->{cache} = $compile_around_method->(
168 @{$code->{'modifier_table'}->{around}->{methods}},
169 $code->{'modifier_table'}->{orig}->body
69e3ab0a 170 );
8683db0e 171 $_build_wrapped_method->($code->{'modifier_table'});
69e3ab0a 172 }
ba38bf08 173}
174
b88aa2e8 175sub around_modifiers {
176 my $code = shift;
177 return @{$code->{'modifier_table'}->{around}->{methods}};
178}
179
5281e1f9 180sub _make_compatible_with {
181 my $self = shift;
182 my ($other) = @_;
183
184 # XXX: this is pretty gross. the issue here is that CMOP::Method::Wrapped
185 # objects are subclasses of CMOP::Method, but when we get to moose, they'll
186 # need to be compatible with Moose::Meta::Method, which isn't possible. the
187 # right solution here is to make ::Wrapped into a role that gets applied to
188 # whatever the method_metaclass happens to be and get rid of
189 # wrapped_method_metaclass entirely, but that's not going to happen until
190 # we ditch cmop and get roles into the bootstrapping, so. i'm not
191 # maintaining the previous behavior of turning them into instances of the
192 # new method_metaclass because that's equally broken, and at least this way
193 # any issues will at least be detectable and potentially fixable. -doy
194 return $self unless $other->_is_compatible_with($self->_real_ref_name);
195
196 return $self->SUPER::_make_compatible_with(@_);
197}
198
ba38bf08 1991;
200
201__END__
202
203=pod
204
b7bdffc3 205=head1 NAME
ba38bf08 206
07ba54f8 207Class::MOP::Method::Wrapped - Method Meta Object for methods with before/after/around modifiers
ba38bf08 208
ba38bf08 209=head1 DESCRIPTION
210
07ba54f8 211This is a L<Class::MOP::Method> subclass which implements before,
212after, and around method modifiers.
127d39a7 213
ba38bf08 214=head1 METHODS
215
216=head2 Construction
217
218=over 4
219
07ba54f8 220=item B<< Class::MOP::Method::Wrapped->wrap($metamethod, %options) >>
127d39a7 221
07ba54f8 222This is the constructor. It accepts a L<Class::MOP::Method> object and
223a hash of options.
ba38bf08 224
07ba54f8 225The options are:
ba38bf08 226
07ba54f8 227=over 8
ba38bf08 228
07ba54f8 229=item * name
ba38bf08 230
07ba54f8 231The method name (without a package name). This will be taken from the
232provided L<Class::MOP::Method> object if it is not provided.
127d39a7 233
07ba54f8 234=item * package_name
ba38bf08 235
07ba54f8 236The package name for the method. This will be taken from the provided
237L<Class::MOP::Method> object if it is not provided.
ba38bf08 238
07ba54f8 239=item * associated_metaclass
ba38bf08 240
07ba54f8 241An optional L<Class::MOP::Class> object. This is the metaclass for the
242method's class.
ba38bf08 243
244=back
245
07ba54f8 246=item B<< $metamethod->get_original_method >>
b88aa2e8 247
07ba54f8 248This returns the L<Class::MOP::Method> object that was passed to the
249constructor.
250
251=item B<< $metamethod->add_before_modifier($code) >>
252
253=item B<< $metamethod->add_after_modifier($code) >>
254
255=item B<< $metamethod->add_around_modifier($code) >>
256
257These methods all take a subroutine reference and apply it as a
258modifier to the original method.
259
260=item B<< $metamethod->before_modifiers >>
b88aa2e8 261
07ba54f8 262=item B<< $metamethod->after_modifiers >>
b88aa2e8 263
07ba54f8 264=item B<< $metamethod->around_modifiers >>
b88aa2e8 265
07ba54f8 266These methods all return a list of subroutine references which are
267acting as the specified type of modifier.
b88aa2e8 268
269=back
270
ba38bf08 271=head1 AUTHORS
272
273Stevan Little E<lt>stevan@iinteractive.comE<gt>
274
ba38bf08 275=head1 COPYRIGHT AND LICENSE
276
3e2c8600 277Copyright 2006-2010 by Infinity Interactive, Inc.
ba38bf08 278
279L<http://www.iinteractive.com>
280
281This library is free software; you can redistribute it and/or modify
b7bdffc3 282it under the same terms as Perl itself.
ba38bf08 283
284=cut
285