Improve the error message of having a plain reference in 'default'
[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';
8use Scalar::Util 'reftype', 'blessed';
9use Sub::Name 'subname';
10
11our $VERSION = '0.02';
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 {
69e3ab0a 72 my $class = shift;
73 my $code = shift;
74 (blessed($code) && $code->isa('Class::MOP::Method'))
75 || confess "Can only wrap blessed CODE";
76 my $modifier_table = {
77 cache => undef,
78 orig => $code,
79 before => [],
80 after => [],
81 around => {
82 cache => $code->body,
83 methods => [],
84 },
85 };
86 $_build_wrapped_method->($modifier_table);
87 my $method = $class->SUPER::wrap(sub { $modifier_table->{cache}->(@_) });
88 $method->{'%!modifier_table'} = $modifier_table;
89 $method;
ba38bf08 90}
91
92sub get_original_method {
69e3ab0a 93 my $code = shift;
c23184fc 94 $code->{'%!modifier_table'}->{orig};
ba38bf08 95}
96
97sub add_before_modifier {
69e3ab0a 98 my $code = shift;
99 my $modifier = shift;
100 unshift @{$code->{'%!modifier_table'}->{before}} => $modifier;
101 $_build_wrapped_method->($code->{'%!modifier_table'});
ba38bf08 102}
103
104sub add_after_modifier {
69e3ab0a 105 my $code = shift;
106 my $modifier = shift;
107 push @{$code->{'%!modifier_table'}->{after}} => $modifier;
108 $_build_wrapped_method->($code->{'%!modifier_table'});
ba38bf08 109}
110
111{
69e3ab0a 112 # NOTE:
113 # this is another possible candidate for
114 # optimization as well. There is an overhead
115 # associated with the currying that, if
116 # eliminated might make around modifiers
117 # more manageable.
118 my $compile_around_method = sub {{
119 my $f1 = pop;
120 return $f1 unless @_;
121 my $f2 = pop;
122 push @_, sub { $f2->( $f1, @_ ) };
123 redo;
124 }};
125
126 sub add_around_modifier {
127 my $code = shift;
128 my $modifier = shift;
129 unshift @{$code->{'%!modifier_table'}->{around}->{methods}} => $modifier;
130 $code->{'%!modifier_table'}->{around}->{cache} = $compile_around_method->(
131 @{$code->{'%!modifier_table'}->{around}->{methods}},
132 $code->{'%!modifier_table'}->{orig}->body
133 );
134 $_build_wrapped_method->($code->{'%!modifier_table'});
135 }
ba38bf08 136}
137
1381;
139
140__END__
141
142=pod
143
b7bdffc3 144=head1 NAME
ba38bf08 145
146Class::MOP::Method::Wrapped - Method Meta Object to handle before/around/after modifiers
147
ba38bf08 148=head1 DESCRIPTION
149
127d39a7 150This is a L<Class::MOP::Method> subclass which provides the funtionality
151to wrap a given CODE reference with before, after and around method modifiers.
152
ba38bf08 153=head1 METHODS
154
155=head2 Construction
156
157=over 4
158
127d39a7 159=item B<wrap ($code)>
160
161This is the constructor, it will return a B<Class::MOP::Method::Wrapped>
162instance that can be used to add before, after and around modifiers to.
ba38bf08 163
164=item B<get_original_method>
165
127d39a7 166This returns the original CODE reference that was provided to the
167constructor.
168
ba38bf08 169=back
170
171=head2 Modifiers
172
127d39a7 173These three methods will add the method modifiers to the wrapped
174CODE reference. For more information on how method modifiers work,
175see the section in L<Class::MOP::Class>.
176
ba38bf08 177=over 4
178
179=item B<add_before_modifier ($code)>
180
181=item B<add_after_modifier ($code)>
182
183=item B<add_around_modifier ($code)>
184
185=back
186
187=head1 AUTHORS
188
189Stevan Little E<lt>stevan@iinteractive.comE<gt>
190
ba38bf08 191=head1 COPYRIGHT AND LICENSE
192
69e3ab0a 193Copyright 2006-2008 by Infinity Interactive, Inc.
ba38bf08 194
195L<http://www.iinteractive.com>
196
197This library is free software; you can redistribute it and/or modify
b7bdffc3 198it under the same terms as Perl itself.
ba38bf08 199
200=cut
201