bar
[gitmo/Class-MOP.git] / lib / Class / MOP / Method.pm
CommitLineData
8b978dd5 1
2package Class::MOP::Method;
3
4use strict;
5use warnings;
6
2eb717d5 7use Carp 'confess';
aa448b16 8use Scalar::Util 'reftype', 'blessed';
de19f115 9use B 'svref_2object';
2eb717d5 10
de19f115 11our $VERSION = '0.02';
12
13# introspection
2eb717d5 14
727919c5 15sub meta {
16 require Class::MOP::Class;
aa448b16 17 Class::MOP::Class->initialize(blessed($_[0]) || $_[0]);
727919c5 18}
2eb717d5 19
de19f115 20# construction
21
a4258ffd 22sub wrap {
2eb717d5 23 my $class = shift;
24 my $code = shift;
ee5e71d4 25 ('CODE' eq (reftype($code) || ''))
de19f115 26 || confess "You must supply a CODE reference to bless";
27 bless $code => blessed($class) || $class;
28}
29
de19f115 30# informational
31
32sub package_name {
33 my $code = shift;
34 (blessed($code))
35 || confess "Can only ask the package name of a blessed CODE";
36 svref_2object($code)->GV->STASH->NAME;
37}
38
39sub name {
40 my $code = shift;
41 (blessed($code))
42 || confess "Can only ask the package name of a blessed CODE";
43 svref_2object($code)->GV->NAME;
2eb717d5 44}
de19f115 45
96ceced8 46sub fully_qualified_name {
47 my $code = shift;
48 (blessed($code))
49 || confess "Can only ask the package name of a blessed CODE";
50 $code->package_name . '::' . $code->name;
51}
52
ddc8edba 53package Class::MOP::Method::Wrapped;
54
55use strict;
56use warnings;
57
58use Carp 'confess';
59use Scalar::Util 'reftype', 'blessed';
96ceced8 60use Sub::Name 'subname';
ddc8edba 61
62our $VERSION = '0.01';
63
64our @ISA = ('Class::MOP::Method');
65
96ceced8 66# NOTE:
67# this ugly beast is the result of trying
68# to micro optimize this as much as possible
69# while not completely loosing maintainability.
70# At this point it's "fast enough", after all
71# you can't get something for nothing :)
72my $_build_wrapped_method = sub {
73 my $modifier_table = shift;
74 my ($before, $after, $around) = (
75 $modifier_table->{before},
76 $modifier_table->{after},
77 $modifier_table->{around},
78 );
79 if (@$before && @$after) {
80 $modifier_table->{cache} = sub {
81 $_->(@_) for @{$before};
82 my @rval;
83 ((defined wantarray) ?
84 ((wantarray) ?
85 (@rval = $around->{cache}->(@_))
86 :
87 ($rval[0] = $around->{cache}->(@_)))
88 :
89 $around->{cache}->(@_));
90 $_->(@_) for @{$after};
91 return unless defined wantarray;
92 return wantarray ? @rval : $rval[0];
93 }
94 }
95 elsif (@$before && !@$after) {
96 $modifier_table->{cache} = sub {
97 $_->(@_) for @{$before};
98 return $around->{cache}->(@_);
99 }
100 }
101 elsif (@$after && !@$before) {
102 $modifier_table->{cache} = sub {
103 my @rval;
104 ((defined wantarray) ?
105 ((wantarray) ?
106 (@rval = $around->{cache}->(@_))
107 :
108 ($rval[0] = $around->{cache}->(@_)))
109 :
110 $around->{cache}->(@_));
111 $_->(@_) for @{$after};
112 return unless defined wantarray;
113 return wantarray ? @rval : $rval[0];
114 }
115 }
116 else {
117 $modifier_table->{cache} = $around->{cache};
118 }
119};
120
ddc8edba 121my %MODIFIERS;
122
123sub wrap {
124 my $class = shift;
125 my $code = shift;
126 (blessed($code) && $code->isa('Class::MOP::Method'))
127 || confess "Can only wrap blessed CODE";
128 my $modifier_table = {
96ceced8 129 cache => undef,
ddc8edba 130 orig => $code,
131 before => [],
132 after => [],
133 around => {
134 cache => $code,
96ceced8 135 methods => [],
ddc8edba 136 },
137 };
96ceced8 138 $_build_wrapped_method->($modifier_table);
139 my $method = $class->SUPER::wrap(sub { $modifier_table->{cache}->(@_) });
ddc8edba 140 $MODIFIERS{$method} = $modifier_table;
141 $method;
142}
143
144sub add_before_modifier {
145 my $code = shift;
146 my $modifier = shift;
147 (exists $MODIFIERS{$code})
148 || confess "You must first wrap your method before adding a modifier";
149 (blessed($code))
150 || confess "Can only ask the package name of a blessed CODE";
151 ('CODE' eq (reftype($code) || ''))
152 || confess "You must supply a CODE reference for a modifier";
153 unshift @{$MODIFIERS{$code}->{before}} => $modifier;
96ceced8 154 $_build_wrapped_method->($MODIFIERS{$code});
ddc8edba 155}
156
157sub add_after_modifier {
158 my $code = shift;
159 my $modifier = shift;
160 (exists $MODIFIERS{$code})
161 || confess "You must first wrap your method before adding a modifier";
162 (blessed($code))
163 || confess "Can only ask the package name of a blessed CODE";
164 ('CODE' eq (reftype($code) || ''))
165 || confess "You must supply a CODE reference for a modifier";
166 push @{$MODIFIERS{$code}->{after}} => $modifier;
96ceced8 167 $_build_wrapped_method->($MODIFIERS{$code});
ddc8edba 168}
169
170{
96ceced8 171 # NOTE:
172 # this is another possible canidate for
173 # optimization as well. There is an overhead
174 # associated with the currying that, if
175 # eliminated might make around modifiers
176 # more manageable.
ddc8edba 177 my $compile_around_method = sub {{
178 my $f1 = pop;
179 return $f1 unless @_;
180 my $f2 = pop;
181 push @_, sub { $f2->( $f1, @_ ) };
182 redo;
183 }};
184
185 sub add_around_modifier {
186 my $code = shift;
187 my $modifier = shift;
188 (exists $MODIFIERS{$code})
189 || confess "You must first wrap your method before adding a modifier";
190 (blessed($code))
191 || confess "Can only ask the package name of a blessed CODE";
192 ('CODE' eq (reftype($code) || ''))
193 || confess "You must supply a CODE reference for a modifier";
194 unshift @{$MODIFIERS{$code}->{around}->{methods}} => $modifier;
195 $MODIFIERS{$code}->{around}->{cache} = $compile_around_method->(
196 @{$MODIFIERS{$code}->{around}->{methods}},
197 $MODIFIERS{$code}->{orig}
198 );
96ceced8 199 $_build_wrapped_method->($MODIFIERS{$code});
ddc8edba 200 }
201}
202
8b978dd5 2031;
204
205__END__
206
207=pod
208
209=head1 NAME
210
211Class::MOP::Method - Method Meta Object
212
213=head1 SYNOPSIS
214
fe122940 215 # ... more to come later maybe
216
8b978dd5 217=head1 DESCRIPTION
218
552e3d24 219The Method Protocol is very small, since methods in Perl 5 are just
220subroutines within the particular package. Basically all we do is to
fe122940 221bless the subroutine.
222
223Currently this package is largely unused. Future plans are to provide
224some very simple introspection methods for the methods themselves.
225Suggestions for this are welcome.
552e3d24 226
2eb717d5 227=head1 METHODS
228
de19f115 229=head2 Introspection
2eb717d5 230
de19f115 231=over 4
fe122940 232
2eb717d5 233=item B<meta>
234
fe122940 235This will return a B<Class::MOP::Class> instance which is related
236to this class.
237
2eb717d5 238=back
239
de19f115 240=head2 Construction
241
242=over 4
243
a4258ffd 244=item B<wrap (&code)>
de19f115 245
246This simply blesses the C<&code> reference passed to it.
247
248=back
249
250=head2 Informational
251
252=over 4
253
254=item B<name>
255
256=item B<package_name>
257
96ceced8 258=item B<fully_qualified_name>
259
260=back
261
262=head1 Class::MOP::Method::Wrapped METHODS
263
264=head2 Construction
265
266=over 4
267
268=item B<wrap (&code)>
269
270This simply blesses the C<&code> reference passed to it.
271
de19f115 272=back
273
ee5e71d4 274=head2 Modifiers
275
276=over 4
de19f115 277
ee5e71d4 278=item B<add_before_modifier ($code)>
de19f115 279
ee5e71d4 280=item B<add_after_modifier ($code)>
281
282=item B<add_around_modifier ($code)>
283
284=back
de19f115 285
8b978dd5 286=head1 AUTHOR
287
a2e85e6c 288Stevan Little E<lt>stevan@iinteractive.comE<gt>
8b978dd5 289
290=head1 COPYRIGHT AND LICENSE
291
292Copyright 2006 by Infinity Interactive, Inc.
293
294L<http://www.iinteractive.com>
295
296This library is free software; you can redistribute it and/or modify
297it under the same terms as Perl itself.
298
299=cut