File Coverage

blib/lib/Params/Callback.pm
Criterion Covered Total %
statement 135 151 89.4
branch 28 50 56.0
condition 12 23 52.1
subroutine 30 33 90.9
pod 8 10 80.0
total 213 267 79.7


line stmt bran cond sub pod time code
1             package Params::Callback;
2              
3 7     7   221769 use strict;
  7         14  
  7         272  
4             require 5.006;
5 7     7   3373 use Params::Validate ();
  7         41852  
  7         195  
6 7     7   1979 use Params::CallbackRequest::Exceptions (abbr => [qw(throw_bad_params)]);
  7         20  
  7         61  
7              
8 7     7   37 use vars qw($VERSION);
  7         13  
  7         375  
9             $VERSION = '1.20';
10 7     7   38 use constant DEFAULT_PRIORITY => 5;
  7         13  
  7         490  
11 7     7   35 use constant REDIRECT => 302;
  7         11  
  7         1162  
12              
13             # Set up an exception to be thrown by Params::Validate, and allow extra
14             # parameters not specified, since subclasses may add others.
15             Params::Validate::validation_options
16             ( on_fail => sub { throw_bad_params join '', @_ },
17             allow_extra => 1 );
18              
19             my $is_num = { 'valid priority' => sub { $_[0] =~ /^\d$/ } };
20              
21             # Use Apache2?::RequestRec for mod_perl 2
22 7 0       806 use constant APREQ_CLASS => exists $ENV{MOD_PERL_API_VERSION}
    50          
23             ? $ENV{MOD_PERL_API_VERSION} >= 2
24             ? 'Apache2::RequestRec'
25             : 'Apache::RequestRec'
26 7     7   38 : 'Apache';
  7         15  
27              
28             BEGIN {
29             # The object-oriented interface is only supported with the use of
30             # Attribute::Handlers in Perl 5.6 and later. We'll use Class::ISA
31             # to get a list of all the classes that a class inherits from so
32             # that we can tell ApacheHandler::WithCallbacks that they exist and
33             # are loaded.
34 7 50   7   54 unless ($] < 5.006) {
35 7         3993 require Attribute::Handlers;
36 7         26583 require Class::ISA;
37             }
38              
39             # Build read-only accessors.
40 7         12060 for my $attr (qw(
41             cb_request
42             params
43             apache_req
44             priority
45             cb_key
46             pkg_key
47             requester
48             trigger_key
49             value
50             )) {
51 7     7   35 no strict 'refs';
  7         14  
  7         540  
52 63     576   216 *{$attr} = sub { $_[0]->{$attr} };
  63         329  
  576         67995  
53             }
54 7         2823 *class_key = \&pkg_key;
55             }
56              
57             my %valid_params = (
58             cb_request => { isa => 'Params::CallbackRequest' },
59              
60             params => {
61             type => Params::Validate::HASHREF,
62             },
63              
64             apache_req => {
65             isa => APREQ_CLASS,
66             optional => 1,
67             },
68              
69             priority => {
70             type => Params::Validate::SCALAR,
71             callbacks => $is_num,
72             optional => 1,
73             desc => 'Priority'
74             },
75              
76             cb_key => {
77             type => Params::Validate::SCALAR,
78             optional => 1,
79             desc => 'Callback key'
80             },
81              
82             pkg_key => {
83             type => Params::Validate::SCALAR,
84             optional => 1,
85             desc => 'Package key'
86             },
87              
88             trigger_key => {
89             type => Params::Validate::SCALAR,
90             optional => 1,
91             desc => 'Trigger key'
92             },
93              
94             value => {
95             optional => 1,
96             desc => 'Callback value'
97             },
98              
99             requester => {
100             optional => 1,
101             desc => 'Requesting object'
102             }
103             );
104              
105             sub new {
106 430     430 0 575 my $proto = shift;
107 430         19636 my %p = Params::Validate::validate(@_, \%valid_params);
108 430   33     8660 return bless \%p, ref $proto || $proto;
109             }
110              
111             ##############################################################################
112             # Subclasses must use register_subclass() to register the subclass. They can
113             # also use it to set up the class key and a default priority for the subclass,
114             # But base class CLASS_KEY() and DEFAULT_PRIORITY() methods can also be
115             # overridden to do that.
116             my (%priorities, %classes, %pres, %posts, @reqs, %isas, @classes);
117             sub register_subclass {
118 7     7 0 277 shift; # Not needed.
119 7         18 my $class = caller;
120 7 50 33     66 return unless UNIVERSAL::isa($class, __PACKAGE__)
121             and $class ne __PACKAGE__;
122 7         44 my $spec = {
123             default_priority => {
124             type => Params::Validate::SCALAR,
125             optional => 1,
126             callbacks => $is_num
127             },
128             class_key => {
129             type => Params::Validate::SCALAR,
130             optional => 1
131             },
132             };
133              
134 7         178 my %p = Params::Validate::validate(@_, $spec);
135              
136             # Grab the class key. Default to the actual class name.
137 7   66     49 my $ckey = $p{class_key} || $class;
138              
139             # Create the CLASS_KEY method if it doesn't exist already.
140 7 100       11 unless (defined &{"$class\::CLASS_KEY"}) {
  7         49  
141 7     7   54 no strict 'refs';
  7         16  
  7         656  
142 5     10   20 *{"$class\::CLASS_KEY"} = sub { $ckey };
  5         35  
  10         36  
143             }
144 7         23 $classes{$class->CLASS_KEY} = $class;
145              
146 7 50       21 if (defined $p{default_priority}) {
147             # Override any base class DEFAULT_PRIORITY methods.
148 7     7   38 no strict 'refs';
  7         37  
  7         1478  
149 0     0   0 *{"$class\::DEFAULT_PRIORITY"} = sub { $p{default_priority} };
  0         0  
  0         0  
150             }
151              
152             # Push the class into an array so that we can be sure to process it in
153             # the proper order later.
154 7         35 push @classes, $class;
155             }
156              
157             ##############################################################################
158              
159             # This method is called by subclassed methods that want to be
160             # parameter-triggered callbacks.
161              
162             sub Callback : ATTR(CODE, BEGIN) {
163 18     18 1 8240 my ($class, $symbol, $coderef, $attr, $data, $phase) = @_;
164             # Validate the arguments. At this point, there's only one allowed,
165             # priority. This is to set a priority for the callback method that
166             # overrides that set for the class.
167 18         83 my $spec = {
168             priority => {
169             type => Params::Validate::SCALAR,
170             optional => 1,
171             callbacks => $is_num
172             },
173             };
174 18         377 my %p = Params::Validate::validate(@$data, $spec);
175             # Get the priority.
176 18 100       161 my $priority = exists $p{priority} ? $p{priority} :
177             $class->DEFAULT_PRIORITY;
178             # Store the priority under the code reference.
179 18         105 $priorities{$coderef} = $priority;
180 7     7   45 }
  7         13  
  7         60  
181              
182             ##############################################################################
183              
184             # These methods are called by subclassed methods that want to be request
185             # callbacks.
186              
187             sub PreCallback : ATTR(CODE, BEGIN) {
188 4     4 1 2145 my ($class, $symbol, $coderef) = @_;
189             # Just return if we've been here before. This is to prevent hiccups when
190             # mod_perl loads packages twice.
191 4 50 66     49 return if $pres{$class} and ref $pres{$class}->[0];
192             # Store a reference to the code in a temporary location and a pointer to
193             # it in the array.
194 4         9 push @reqs, $coderef;
195 4         7 push @{$pres{$class}}, $#reqs;
  4         16  
196 7     7   3024 }
  7         136  
  7         42  
197              
198             sub PostCallback : ATTR(CODE, BEGIN) {
199 6     6 1 2228 my ($class, $symbol, $coderef) = @_;
200             # Just return if we've been here before. This is to prevent hiccups when
201             # mod_perl loads packages twice.
202 6 50 66     33 return if $posts{$class} and ref $posts{$class}->[0];
203             # Store a reference to the code in a temporary location and a pointer to
204             # it in the array.
205 6         52 push @reqs, $coderef;
206 6         18 push @{$posts{$class}}, $#reqs;
  6         33  
207 7     7   2698 }
  7         29  
  7         31  
208              
209             ##############################################################################
210             # This method is called by Params::CallbackRequest to find the names of all
211             # the callback methods declared with the PreCallback and PostCallback
212             # attributes (might handle those declared with the Callback attribute at some
213             # point, as well -- there's some of it in CVS Revision 1.21 of
214             # MasonX::CallbackHandler). This is necessary because, in a BEGIN block, the
215             # symbol isn't defined when the attribute callback is called. I would use a
216             # CHECK or INIT block, but mod_perl ignores them. So the solution is to have
217             # the callback methods save the code references for the methods, make sure
218             # that Params::CallbackRequest is loaded _after_ all the classes that inherit
219             # from Params::Callback, and have it call this function to go back and find
220             # the names of the callback methods. The method names will then of course be
221             # used for the callback names. In mod_perl2, we'll likely be able to call this
222             # method from a PerlPostConfigHandler instead of making
223             # Params::CallbackRequest do it, thus relieving the enforced loading order.
224             # http://perl.apache.org/docs/2.0/user/handlers/server.html#PerlPostConfigHandler
225              
226             sub _find_names {
227 7     7   27 foreach my $class (@classes) {
228             # Find the names of the request callback methods.
229 7         25 foreach my $type (\%pres, \%posts) {
230             # We've stored an index pointing to each method in the @reqs
231             # array under __TMP in PreCallback() and PostCallback().
232 14         19 for (@{$type->{$class}}) {
  14         47  
233 10         19 my $code = $reqs[$_];
234             # Grab the symbol hash for this code reference.
235 10 50       32 my $sym = Attribute::Handlers::findsym($class, $code)
236             or die "Anonymous subroutines not supported. Make " .
237             "sure that Params::CallbackRequest loads last";
238             # Params::CallbackRequest wants an array reference.
239 10     280   143 $_ = [ sub { goto $code }, $class, *{$sym}{NAME} ];
  280         881  
  10         54  
240             }
241             }
242             # Copy any request callbacks from their parent classes. This is to
243             # ensure that rquest callbacks act like methods, even though,
244             # technically, they're not.
245 7         23 $isas{$class} = _copy_meths($class);
246             }
247             # We don't need these anymore.
248 7         17 @classes = ();
249 7         10125 @reqs = ();
250             }
251              
252             ##############################################################################
253             # This little gem, called by _find_names(), mimics inheritance by copying the
254             # request callback methods declared for parent class keys into the children.
255             # Any methods declared in the children will, of course, override. This means
256             # that the parent methods can never actually be called, since request
257             # callbacks are called for every request, and thus don't have a class
258             # association. They still get the correct object passed as their first
259             # parameter, however.
260              
261             sub _copy_meths {
262 7     7   12 my $class = shift;
263 7         11 my %seen_class;
264             # Grab all of the super classes.
265 7         72 foreach my $super (grep { UNIVERSAL::isa($_, __PACKAGE__) }
  10         371  
266             Class::ISA::super_path($class)) {
267             # Skip classes we've already seen.
268 10 50       32 unless ($seen_class{$super}) {
269             # Copy request callback code references.
270 10         18 foreach my $type (\%pres, \%posts) {
271 20 100 66     146 if ($type->{$class} and $type->{$super}) {
    50          
272             # Copy the methods, but allow newer ones to override.
273 6         8 my %seen_meth;
274 11         49 $type->{$class} =
275 6         13 [ grep { not $seen_meth{$_->[2]}++ }
276 6         9 @{$type->{$class}}, @{$type->{$super}} ];
  6         12  
277             } elsif ($type->{$super}) {
278             # Just copy the methods.
279 0         0 $type->{$class} = [ @{ $type->{$super} } ];
  0         0  
280             }
281             }
282 10         33 $seen_class{$super} = 1;
283             }
284             }
285              
286             # Return an array ref of the super classes.
287 7         43 return [keys %seen_class];
288             }
289              
290             ##############################################################################
291             # This method is called by Params::CallbackRequest to find methods for
292             # callback classes. This is because Params::Callback stores this list of
293             # callback classes, not Params::CallbackRequest. Its arguments are the
294             # callback class, the name of the method (callback), and a reference to the
295             # priority. We'll only assign the priority if it hasn't been assigned one
296             # already -- that is, it hasn't been _called_ with a priority.
297              
298             sub _get_callback {
299 83     83   139 my ($class, $meth, $p) = @_;
300             # Get the callback code reference.
301 83 50       848 my $c = UNIVERSAL::can($class, $meth) or return;
302             # Get the priority for this callback. If there's no priority, it's not
303             # a callback method, so skip it.
304 83 50       282 return unless defined $priorities{$c};
305 83         156 my $priority = $priorities{$c};
306             # Reformat the callback code reference.
307 83     80   368 my $code = sub { goto $c };
  80         232  
308             # Assign the priority, if necessary.
309 83 100       224 $$p = $priority unless $$p ne '';
310             # Create and return the callback.
311 83         341 return $code;
312             }
313              
314             ##############################################################################
315             # This method is also called by Params::CallbackRequest, where the cb_classes
316             # parameter passes in a list of callback class keys or the string "ALL" to
317             # indicate that all of the callback classes should have their callbacks loaded
318             # for use by Params::CallbacRequest.
319              
320             sub _load_classes {
321 6     6   18 my ($pkg, $ckeys) = @_;
322             # Just return success if there are no classes to be loaded.
323 6 50       19 return unless defined $ckeys;
324 6         12 my ($cbs, $pres, $posts);
325             # Process the class keys in the order they're given, or just do all of
326             # them if $ckeys eq 'ALL' or $ckeys->[0] eq '_ALL_' (checked by
327             # Params::CallbackRequest).
328 6 100 66     51 foreach my $ckey (
329             ref $ckeys && $ckeys->[0] ne '_ALL_' ? @$ckeys : keys %classes
330             ) {
331 11 50       40 my $class = $classes{$ckey} or
332             die "Class with class key '$ckey' not loaded. Did you forget use"
333             . " it or to call register_subclass()?";
334             # Map the class key to the class for the class and all of its parent
335             # classes, all for the benefit of Params::CallbackRequest.
336 11         25 $cbs->{$ckey} = $class;
337 11         15 foreach my $c (@{$isas{$class}}) {
  11         44  
338 17 100       45 next if $c eq __PACKAGE__;
339 6         27 $cbs->{$c->CLASS_KEY} = $c;
340             }
341             # Load request callbacks in the order they're defined. Methods
342             # inherited from parents have already been copied, so don't worry
343             # about them.
344 11 50       43 push @$pres, @{ $pres{$class} } if $pres{$class};
  11         26  
345 11 50       32 push @$posts, @{ $posts{$class} } if $posts{$class};
  11         28  
346             }
347 6         34 return ($cbs, $pres, $posts);
348             }
349              
350             ##############################################################################
351              
352             sub redirect {
353 0     0 1 0 my ($self, $url, $wait, $status) = @_;
354 0   0     0 $status ||= REDIRECT;
355 0         0 my $cb_request = $self->cb_request;
356 0         0 $cb_request->{_status} = $status;
357 0         0 $cb_request->{redirected} = $url;
358              
359 0 0       0 if (my $r = $self->apache_req) {
360 0         0 $r->method('GET');
361 0         0 $r->headers_in->unset('Content-length');
362 0         0 $r->err_headers_out->add( Location => $url );
363             }
364 0 0       0 $self->abort($status) unless $wait;
365             }
366              
367             ##############################################################################
368              
369 0     0 1 0 sub redirected { $_[0]->cb_request->redirected }
370              
371             ##############################################################################
372              
373             sub abort {
374 8     8 1 66 my ($self, $aborted_value) = @_;
375 8         32 $self->cb_request->{_status} = $aborted_value;
376 8         100 Params::Callback::Exception::Abort->throw
377             ( error => ref $self . '->abort was called',
378             aborted_value => $aborted_value );
379             }
380              
381             ##############################################################################
382              
383             sub aborted {
384 7     7 1 1911 my ($self, $err) = @_;
385 7 50       20 $err = $@ unless defined $err;
386 7         27 return Params::CallbackRequest::Exceptions::isa_cb_exception( $err, 'Abort' );
387             }
388              
389             ##############################################################################
390              
391             sub notes {
392 9     9 1 34 shift->{cb_request}->notes(@_);
393             }
394              
395             1;
396             __END__