foo
[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
be960ba1 11our $VERSION = '0.03';
de19f115 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) || ''))
4d47b77f 26 || confess "You must supply a CODE reference to bless, not (" . ($code || 'undef') . ")";
de19f115 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
195f5bf8 144sub get_original_method {
145 my $code = shift;
146 $MODIFIERS{$code}->{orig}
147 if exists $MODIFIERS{$code};
148}
149
ddc8edba 150sub add_before_modifier {
151 my $code = shift;
152 my $modifier = shift;
153 (exists $MODIFIERS{$code})
154 || confess "You must first wrap your method before adding a modifier";
155 (blessed($code))
156 || confess "Can only ask the package name of a blessed CODE";
157 ('CODE' eq (reftype($code) || ''))
158 || confess "You must supply a CODE reference for a modifier";
159 unshift @{$MODIFIERS{$code}->{before}} => $modifier;
96ceced8 160 $_build_wrapped_method->($MODIFIERS{$code});
ddc8edba 161}
162
163sub add_after_modifier {
164 my $code = shift;
165 my $modifier = shift;
166 (exists $MODIFIERS{$code})
167 || confess "You must first wrap your method before adding a modifier";
168 (blessed($code))
169 || confess "Can only ask the package name of a blessed CODE";
170 ('CODE' eq (reftype($code) || ''))
171 || confess "You must supply a CODE reference for a modifier";
172 push @{$MODIFIERS{$code}->{after}} => $modifier;
96ceced8 173 $_build_wrapped_method->($MODIFIERS{$code});
ddc8edba 174}
175
176{
96ceced8 177 # NOTE:
178 # this is another possible canidate for
179 # optimization as well. There is an overhead
180 # associated with the currying that, if
181 # eliminated might make around modifiers
182 # more manageable.
ddc8edba 183 my $compile_around_method = sub {{
184 my $f1 = pop;
185 return $f1 unless @_;
186 my $f2 = pop;
187 push @_, sub { $f2->( $f1, @_ ) };
188 redo;
189 }};
190
191 sub add_around_modifier {
192 my $code = shift;
193 my $modifier = shift;
194 (exists $MODIFIERS{$code})
195 || confess "You must first wrap your method before adding a modifier";
196 (blessed($code))
197 || confess "Can only ask the package name of a blessed CODE";
198 ('CODE' eq (reftype($code) || ''))
199 || confess "You must supply a CODE reference for a modifier";
200 unshift @{$MODIFIERS{$code}->{around}->{methods}} => $modifier;
201 $MODIFIERS{$code}->{around}->{cache} = $compile_around_method->(
202 @{$MODIFIERS{$code}->{around}->{methods}},
203 $MODIFIERS{$code}->{orig}
204 );
96ceced8 205 $_build_wrapped_method->($MODIFIERS{$code});
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
260=item B<name>
261
262=item B<package_name>
263
96ceced8 264=item B<fully_qualified_name>
265
266=back
267
268=head1 Class::MOP::Method::Wrapped METHODS
269
270=head2 Construction
271
272=over 4
273
274=item B<wrap (&code)>
275
276This simply blesses the C<&code> reference passed to it.
277
195f5bf8 278=item B<get_original_method>
279
de19f115 280=back
281
ee5e71d4 282=head2 Modifiers
283
284=over 4
de19f115 285
ee5e71d4 286=item B<add_before_modifier ($code)>
de19f115 287
ee5e71d4 288=item B<add_after_modifier ($code)>
289
290=item B<add_around_modifier ($code)>
291
292=back
de19f115 293
8b978dd5 294=head1 AUTHOR
295
a2e85e6c 296Stevan Little E<lt>stevan@iinteractive.comE<gt>
8b978dd5 297
298=head1 COPYRIGHT AND LICENSE
299
300Copyright 2006 by Infinity Interactive, Inc.
301
302L<http://www.iinteractive.com>
303
304This library is free software; you can redistribute it and/or modify
305it under the same terms as Perl itself.
306
16e960bd 307=cut
308