File Coverage

blib/lib/Class/MOP/Method/Wrapped.pm
Criterion Covered Total %
statement 59 60 98.3
branch 6 8 75.0
condition 6 9 66.6
subroutine 58 58 100.0
pod 8 8 100.0
total 137 143 95.8


line stmt bran cond sub pod time code
1             package Class::MOP::Method::Wrapped;
2             our $VERSION = '2.2203';
3              
4 462     285564   2961 use strict;
  462         879  
  462         12674  
5 462     142343   2055 use warnings;
  462         877  
  462         11662  
6              
7 462     84123   2245 use Scalar::Util 'blessed';
  462         1061  
  462         22617  
8 462     75514   2669 use Sub::Util 1.40 'set_subname';
  462         7452  
  462         19726  
9              
10 462     64591   2749 use parent 'Class::MOP::Method';
  462         909  
  462         2554  
11              
12             # NOTE:
13             # this ugly beast is the result of trying
14             # to micro optimize this as much as possible
15             # while not completely loosing maintainability.
16             # At this point it's "fast enough", after all
17             # you can't get something for nothing :)
18             my $_build_wrapped_method = sub {
19             my $modifier_table = shift;
20             my ($before, $after, $around) = (
21             $modifier_table->{before},
22             $modifier_table->{after},
23             $modifier_table->{around},
24             );
25             if (@$before && @$after) {
26             $modifier_table->{cache} = sub {
27             for my $c (@$before) { $c->(@_) };
28             my @rval;
29             ((defined wantarray) ?
30             ((wantarray) ?
31             (@rval = $around->{cache}->(@_))
32             :
33             ($rval[0] = $around->{cache}->(@_)))
34             :
35             $around->{cache}->(@_));
36             for my $c (@$after) { $c->(@_) };
37             return unless defined wantarray;
38             return wantarray ? @rval : $rval[0];
39             }
40             }
41             elsif (@$before) {
42             $modifier_table->{cache} = sub {
43             for my $c (@$before) { $c->(@_) };
44             return $around->{cache}->(@_);
45             }
46             }
47             elsif (@$after) {
48             $modifier_table->{cache} = sub {
49             my @rval;
50             ((defined wantarray) ?
51             ((wantarray) ?
52             (@rval = $around->{cache}->(@_))
53             :
54             ($rval[0] = $around->{cache}->(@_)))
55             :
56             $around->{cache}->(@_));
57             for my $c (@$after) { $c->(@_) };
58             return unless defined wantarray;
59             return wantarray ? @rval : $rval[0];
60             }
61             }
62             else {
63             $modifier_table->{cache} = $around->{cache};
64             }
65             };
66              
67             sub wrap {
68 15922     70868 1 47918 my ( $class, $code, %params ) = @_;
69              
70 15922 100 66     73756 (blessed($code) && $code->isa('Class::MOP::Method'))
71             || $class->_throw_exception( CanOnlyWrapBlessedCode => params => \%params,
72             class => $class,
73             code => $code
74             );
75              
76 15921         107077 my $modifier_table = {
77             cache => undef,
78             orig => $code->body,
79             before => [],
80             after => [],
81             around => {
82             cache => $code->body,
83             methods => [],
84             },
85             };
86 15921         40747 $_build_wrapped_method->($modifier_table);
87              
88             # get these from the original unless explicitly overridden
89 15921   66     32496 my $pkg_name = $params{package_name} || $code->package_name;
90 15921   66     29827 my $method_name = $params{name} || $code->name;
91              
92             return $class->SUPER::wrap(
93             sub {
94             my $wrapped
95             = set_subname( "${pkg_name}::_wrapped_${method_name}" =>
96 268982     319044   1554767 $modifier_table->{cache} );
        310348      
        306141      
        300413      
        299171      
        296286      
        301253      
        295298      
        472690      
        527981      
        494015      
        490078      
        658631      
        840405      
        675260      
        500577      
        492876      
        491022      
        539459      
        485984      
        483818      
        479486      
        477867      
        171049      
        35118      
        7907      
        5947      
        5247      
        5247      
        5247      
        5247      
        2651      
        2651      
        2041      
        2041      
        2041      
        2041      
        2041      
        2041      
        2041      
        2041      
        2041      
        2041      
97 268982         554922 return $wrapped->(@_) ;
98             },
99 15921         99769 package_name => $pkg_name,
100             name => $method_name,
101             original_method => $code,
102             modifier_table => $modifier_table,
103             );
104             }
105              
106             sub _new {
107 15921     225311   24110 my $class = shift;
108 15921 100       31260 return Class::MOP::Class->initialize($class)->new_object(@_)
109             if $class ne __PACKAGE__;
110              
111 15920 50       31083 my $params = @_ == 1 ? $_[0] : {@_};
112              
113             return bless {
114             # inherited from Class::MOP::Method
115             'body' => $params->{body},
116             'associated_metaclass' => $params->{associated_metaclass},
117             'package_name' => $params->{package_name},
118             'name' => $params->{name},
119             'original_method' => $params->{original_method},
120              
121             # defined in this class
122             'modifier_table' => $params->{modifier_table}
123 15920         83424 } => $class;
124             }
125              
126             sub get_original_method {
127 2     32155 1 8 my $code = shift;
128 2         15 $code->original_method;
129             }
130              
131             sub add_before_modifier {
132 163     24241 1 1622 my $code = shift;
133 163         248 my $modifier = shift;
134 163         281 unshift @{$code->{'modifier_table'}->{before}} => $modifier;
  163         503  
135 163         450 $_build_wrapped_method->($code->{'modifier_table'});
136             }
137              
138             sub before_modifiers {
139 4     20871 1 9 my $code = shift;
140 4         7 return @{$code->{'modifier_table'}->{before}};
  4         27  
141             }
142              
143             sub add_after_modifier {
144 61     18762 1 1105 my $code = shift;
145 61         96 my $modifier = shift;
146 61         103 push @{$code->{'modifier_table'}->{after}} => $modifier;
  61         183  
147 61         192 $_build_wrapped_method->($code->{'modifier_table'});
148             }
149              
150             sub after_modifiers {
151 4     16539 1 9 my $code = shift;
152 4         5 return @{$code->{'modifier_table'}->{after}};
  4         22  
153             }
154              
155             {
156             # NOTE:
157             # this is another possible candidate for
158             # optimization as well. There is an overhead
159             # associated with the currying that, if
160             # eliminated might make around modifiers
161             # more manageable.
162             my $compile_around_method = sub {{
163             my $f1 = pop;
164             return $f1 unless @_;
165             my $f2 = pop;
166             push @_, sub { $f2->( $f1, @_ ) };
167             redo;
168             }};
169              
170             sub add_around_modifier {
171 15726     30095 1 26618 my $code = shift;
172 15726         20167 my $modifier = shift;
173 15726         20471 unshift @{$code->{'modifier_table'}->{around}->{methods}} => $modifier;
  15726         42441  
174             $code->{'modifier_table'}->{around}->{cache} = $compile_around_method->(
175 15726         38841 @{$code->{'modifier_table'}->{around}->{methods}},
176             $code->{'modifier_table'}->{orig}
177 15726         22212 );
178 15726         30830 $_build_wrapped_method->($code->{'modifier_table'});
179             }
180             }
181              
182             sub around_modifiers {
183 4     10889 1 8 my $code = shift;
184 4         6 return @{$code->{'modifier_table'}->{around}->{methods}};
  4         32  
185             }
186              
187             sub _make_compatible_with {
188 5     8724   11 my $self = shift;
189 5         19 my ($other) = @_;
190              
191             # XXX: this is pretty gross. the issue here is that CMOP::Method::Wrapped
192             # objects are subclasses of CMOP::Method, but when we get to moose, they'll
193             # need to be compatible with Moose::Meta::Method, which isn't possible. the
194             # right solution here is to make ::Wrapped into a role that gets applied to
195             # whatever the method_metaclass happens to be and get rid of
196             # wrapped_method_metaclass entirely, but that's not going to happen until
197             # we ditch cmop and get roles into the bootstrapping, so. i'm not
198             # maintaining the previous behavior of turning them into instances of the
199             # new method_metaclass because that's equally broken, and at least this way
200             # any issues will at least be detectable and potentially fixable. -doy
201 5 50       20 return $self unless $other->_is_compatible_with($self->_real_ref_name);
202              
203 0           return $self->SUPER::_make_compatible_with(@_);
204             }
205              
206             1;
207              
208             # ABSTRACT: Method Meta Object for methods with before/after/around modifiers
209              
210             __END__
211              
212             =pod
213              
214             =encoding UTF-8
215              
216             =head1 NAME
217              
218             Class::MOP::Method::Wrapped - Method Meta Object for methods with before/after/around modifiers
219              
220             =head1 VERSION
221              
222             version 2.2203
223              
224             =head1 DESCRIPTION
225              
226             This is a L<Class::MOP::Method> subclass which implements before,
227             after, and around method modifiers.
228              
229             =head1 METHODS
230              
231             =head2 Class::MOP::Method::Wrapped->wrap($metamethod, %options)
232              
233             This is the constructor. It accepts a L<Class::MOP::Method> object and
234             a hash of options.
235              
236             The options are:
237              
238             =over 4
239              
240             =item * name
241              
242             The method name (without a package name). This will be taken from the
243             provided L<Class::MOP::Method> object if it is not provided.
244              
245             =item * package_name
246              
247             The package name for the method. This will be taken from the provided
248             L<Class::MOP::Method> object if it is not provided.
249              
250             =item * associated_metaclass
251              
252             An optional L<Class::MOP::Class> object. This is the metaclass for the
253             method's class.
254              
255             =back
256              
257             =head2 $metamethod->get_original_method
258              
259             This returns the L<Class::MOP::Method> object that was passed to the
260             constructor.
261              
262             =head2 $metamethod->add_before_modifier($code)
263              
264             =head2 $metamethod->add_after_modifier($code)
265              
266             =head2 $metamethod->add_around_modifier($code)
267              
268             These methods all take a subroutine reference and apply it as a
269             modifier to the original method.
270              
271             =head2 $metamethod->before_modifiers
272              
273             =head2 $metamethod->after_modifiers
274              
275             =head2 $metamethod->around_modifiers
276              
277             These methods all return a list of subroutine references which are
278             acting as the specified type of modifier.
279              
280             =head1 AUTHORS
281              
282             =over 4
283              
284             =item *
285              
286             Stevan Little <stevan@cpan.org>
287              
288             =item *
289              
290             Dave Rolsky <autarch@urth.org>
291              
292             =item *
293              
294             Jesse Luehrs <doy@cpan.org>
295              
296             =item *
297              
298             Shawn M Moore <sartak@cpan.org>
299              
300             =item *
301              
302             יובל קוג'מן (Yuval Kogman) <nothingmuch@woobling.org>
303              
304             =item *
305              
306             Karen Etheridge <ether@cpan.org>
307              
308             =item *
309              
310             Florian Ragwitz <rafl@debian.org>
311              
312             =item *
313              
314             Hans Dieter Pearcey <hdp@cpan.org>
315              
316             =item *
317              
318             Chris Prather <chris@prather.org>
319              
320             =item *
321              
322             Matt S Trout <mstrout@cpan.org>
323              
324             =back
325              
326             =head1 COPYRIGHT AND LICENSE
327              
328             This software is copyright (c) 2006 by Infinity Interactive, Inc.
329              
330             This is free software; you can redistribute it and/or modify it under
331             the same terms as the Perl 5 programming language system itself.
332              
333             =cut