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