File Coverage

blib/lib/CGI/Snapp/Dispatch.pm
Criterion Covered Total %
statement 232 241 96.2
branch 104 136 76.4
condition 32 50 64.0
subroutine 25 25 100.0
pod 5 6 83.3
total 398 458 86.9


line stmt bran cond sub pod time code
1             package CGI::Snapp::Dispatch;
2              
3 4     4   7051 use strict;
  4         10  
  4         264  
4 4     4   22 use warnings;
  4         8  
  4         128  
5              
6 4     4   23 use Carp;
  4         28  
  4         436  
7              
8 4     4   4822 use CGI::PSGI;
  4         113886  
  4         44  
9              
10 4     4   5533 use Class::Load ':all';
  4         191328  
  4         794  
11              
12 4     4   3495 use Hash::FieldHash ':all';
  4         6425  
  4         619  
13              
14 4     4   3592 use HTTP::Exception;
  4         35481  
  4         31  
15              
16 4     4   347235 use Log::Handler;
  4         221109  
  4         45  
17              
18 4     4   223 use Try::Tiny;
  4         8  
  4         14945  
19              
20             fieldhash my %logger => 'logger';
21             fieldhash my %return_type => 'return_type';
22              
23             our $VERSION = '1.04';
24              
25             # --------------------------------------------------
26              
27             sub as_psgi
28             {
29 2     2 1 6 my($self, @args) = @_;
30              
31 2         29 $self -> log(debug => 'as_psgi(...)');
32              
33 2 50 33     10 croak "Parameter \@args to dispatch() must be a hashref or a hash\n" if ( ($#args > 0) && ($#args % 2 != 1) );
34              
35 2 50       12 my($options) = ref $args[0] eq 'HASH' ? $args[0] : {@args};
36 2         11 my($args) = $self -> _merge_args($options);
37              
38 2 50 33     19 croak "Missing dispatch table, or it's not an arrayref\n" if (! $$args{table} || ref $$args{table} ne 'ARRAY');
39              
40 2         3 my($output);
41              
42             return
43             sub
44             {
45 2     2   1815 my($env) = shift @_;
46 2         6 my($http_method) = $$env{REQUEST_METHOD};
47 2         22 my($named_args) = $self -> _parse_path($http_method, $self -> _clean_path($$env{PATH_INFO}, $args), $$args{table});
48              
49 2 50       8 HTTP::Exception -> throw(404, status_message => 'Not Found') if (! $$named_args{app});
50 2 50 33     8 HTTP::Exception -> throw(400, status_message => "Invalid characters in run mode name '$$named_args{rm}'") if ($$named_args{rm} && ($$named_args{rm} !~ m/^([a-zA-Z_][\w\']+)$/) );
51              
52             # If _prepare() croaks, error number is 404.
53             # If run() croaks, error number is 500,
54             # because the error message will not match /^\d+$/.
55              
56             try
57             {
58 2         82 my($module, $rm, $args_to_new) = $self -> _prepare($http_method, $args, $named_args);
59 2         7 $$args_to_new{_psgi} = 1; # Required.
60 2 50       29 $$args_to_new{QUERY} = CGI::PSGI -> new($env) if (! $$args_to_new{QUERY});
61 2         9251 my($app) = $module -> new(%$args_to_new);
62              
63 2 50       660 $app -> mode_param(sub {return $rm}) if ($rm);
  0         0  
64              
65 2         15 $output = $app -> run;
66             }
67             catch
68             {
69 0 0       0 my($error) = $_ =~ /^404/ ? 404 : 500;
70 0 0       0 my($message) = $error == 404 ? 'Not Found' : 'Internal Server Error';
71              
72 0         0 HTTP::Exception -> throw($error, status_message => $message);
73 2         25 };
74              
75 2         1651 return $output;
76 2         20 };
77              
78             } # End of as_psgi.
79              
80             # --------------------------------------------------
81              
82             sub _clean_path
83             {
84 27     27   57 my($self, $path_info, $args) = @_;
85 27 100 66     171 $path_info = '' if (! defined $path_info || length $path_info == 0);
86              
87 27         97 $self -> log(debug => "_clean_path($path_info, ...)");
88              
89 27 100 66     143 $path_info = $$args{default} if (! defined $path_info || ($path_info eq '/') );
90 27 100 66     140 $path_info = '' if (! defined $path_info || length $path_info == 0);
91              
92             # Standardize the format of the path info, to simplify processing in _parse_path().
93              
94 27 100       103 $path_info = "/$path_info" if (index($path_info, '/') != 0);
95 27 100       99 $path_info = "$path_info/" if (substr($path_info, -1) ne '/');
96              
97 27         88 $self -> log(debug => "Path info '$path_info'");
98              
99 27         122 return $path_info;
100              
101             } # End of _clean_path.
102              
103             # --------------------------------------------------
104              
105             sub dispatch
106             {
107 28     28 1 201 my($self, @args) = @_;
108              
109 28         106 $self -> log(debug => 'dispatch(...)');
110              
111 28 50 66     188 croak "Parameter \@args to dispatch() must be a hashref or a hash\n" if ( ($#args > 0) && ($#args % 2 != 1) );
112              
113 28 50       150 my($options) = ref $args[0] eq 'HASH' ? $args[0] : {@args};
114 28         99 my($args) = $self -> _merge_args($options);
115              
116 28 50 33     185 croak "Missing dispatch table, or it's not an arrayref\n" if (! $$args{table} || ref $$args{table} ne 'ARRAY');
117              
118             # Return the args if the caller is testing.
119              
120 28 100       148 return $args if ($self -> return_type == 1);
121              
122 25         41 my($error);
123             my($output);
124              
125             try
126             {
127 25   66 25   805 my($http_method) = $ENV{HTTP_REQUEST_METHOD} || $ENV{REQUEST_METHOD};
128 25         106 my($named_args) = $self -> _parse_path($http_method, $self -> _clean_path($ENV{PATH_INFO}, $args), $$args{table});
129              
130 25 100       213 croak 404 if (! $$named_args{app});
131 24 100 100     392 croak 400 if ($$named_args{rm} && ($$named_args{rm} !~ m/^([a-zA-Z_][\w\']+)$/) );
132              
133 23 100       89 if ($self -> return_type == 2)
134             {
135             # Return the args if the caller is testing.
136             # Warning: You can't just return when within 'try',
137             # or your return value is discarded. Hence this 'if'.
138              
139 3         17 $output = $named_args;
140             }
141             else
142             {
143             # If run() croaks, _http_error() uses error number 500,
144             # because the error message will not match /^\d+$/.
145              
146 20         73 my($module, $rm, $args_to_new) = $self -> _prepare($http_method, $args, $named_args);
147 19         148 my($app) = $module -> new(%$args_to_new);
148              
149 19 100       6578 $app -> mode_param(sub {return $rm}) if ($rm);
  17         425  
150              
151 19         423 $output = $app -> run;
152             }
153             }
154             catch
155             {
156             # Remove any trailing text from error number, placed there by croak.
157              
158 6     6   1462 ($error = $_) =~ s/^(\d+).+/$1/s;
159 6 50       32 $error = 500 if (! $error);
160 25         238 };
161              
162 25 100       37403 return $error ? $self -> _http_error($$args{error_document}, $error) : $output;
163              
164             } # End of dispatch.
165              
166             # --------------------------------------------------
167              
168             sub dispatch_args
169             {
170 10     10 1 21 my($self, $args) = @_;
171              
172             return
173             {
174 10         65 args_to_new => {},
175             default => '',
176             prefix => '',
177             table =>
178             [
179             ':app' => {},
180             ':app/:rm' => {},
181             ],
182             };
183              
184             } # End of dispatch_args.
185              
186             # --------------------------------------------------
187              
188             sub _http_error
189             {
190 6     6   18 my($self, $error_document, $error_number) = @_;
191              
192 6         22 $self -> log(debug => "_http_error(..., $error_number)");
193              
194 6   100     36 $error_document ||= '';
195 6 100       27 $error_number = 500 if ($error_number !~ /^\d+/);
196              
197 6         24 $self -> log(debug => "Processing HTTP error $error_number");
198              
199 6         9 my($output);
200             my($url);
201              
202 6 100       25 ($output, $url) = $self -> _parse_error_document($error_document, $error_number) if ($error_document);
203              
204             # Now process either the $output or the $url.
205              
206 6         34 my(%error_message) =
207             (
208             400 => 'Bad Request',
209             404 => 'Not Found',
210             500 => 'Internal Server Error',
211             );
212 6         31 my($message) = "$error_number $error_message{$error_number}";
213              
214 6 50       17 if ($url)
215             {
216             # Fabricate a somewhat malformed header. There'll be no error in the access log,
217             # but browers display the $url's document, and the old url in the address bar.
218              
219 0         0 $output = "HTTP/1.0 $message\nLocation: $url\n\n";
220             }
221             else
222             {
223             # Fabricate a HTML document if necessary.
224              
225 6         10 my($header) = '';
226 6   50     67 local $ENV{SERVER_ADMIN} ||= '';
227 6   50     48 local $ENV{SERVER_SIGNATURE} ||= '';
228              
229 6 100       30 $output = <
230            
231            
232            
233             $message
234            
235            
236             $header
237            

$message

238            

239            
240             $ENV{SERVER_ADMIN}
241            
242            

243            
244             $ENV{SERVER_SIGNATURE}
245            
246            
247             EOS
248 6 100       48 $header = "Status: $message\nContent-type: text/" .
249             ($output =~ /^(?:
250              
251             # Work around an IE bug. 'IE bug' is a tautology if I ever saw one...
252              
253 6 50       52 $output .= ' ' x (520 - length $output) if (length $output < 520);
254 6         52 $output = $header . $output;
255             }
256              
257 6         43 return $output;
258              
259             } # End of _http_error.
260              
261             # --------------------------------------------------
262              
263             sub _init
264             {
265 31     31   55 my($self, $arg) = @_;
266 31   50     173 $$arg{logger} ||= ''; # Caller can set.
267 31   100     155 $$arg{return_type} ||= 0; # Caller can set.
268 31         356 $self = from_hash($self, $arg);
269              
270 31         82 return $self;
271              
272             } # End of _init.
273              
274             # --------------------------------------------------
275              
276             sub log
277             {
278 627     627 0 1691 my($self, $level, $s) = @_;
279              
280 627 50       1943 croak "Error: No level defined in call to log()\n" if (! defined $level);
281              
282 627 50       2253 $self -> logger -> $level($s) if ($self -> logger);
283              
284             } # End of log.
285              
286             # --------------------------------------------------
287              
288             sub _merge_args
289             {
290 30     30   43 my($self, $args) = @_;
291              
292 30         66 $self -> log(debug => '_merge_args(...)');
293              
294 30         120 my($extra_args) = $self -> dispatch_args;
295 30         347 my($final_args) = {};
296              
297             # Process all args to dispatch().
298              
299 30         105 for my $key (keys %$args)
300             {
301             # Merge args_to_new because it's a hashref.
302              
303 36 100       84 if ($key eq 'args_to_new')
304             {
305 24         56 $$final_args{$key} = {};
306              
307             # Process all args to this key (args_to_new).
308              
309 24         41 for my $sub_key (keys %{$$args{$key} })
  24         81  
310             {
311             # If the sub key points to a hashref, merge data.
312              
313 27 100       81 if (ref $$args{$key}{$sub_key} eq 'HASH')
314             {
315             # But only merge if dispatch_args() returned data. Otherwise, overwrite.
316              
317 3 100       8 if (exists $$extra_args{$key}{$sub_key})
318             {
319 1         2 $$final_args{$key}{$sub_key} = {%{$$extra_args{$key}{$sub_key} }, %{$$args{$key}{$sub_key} } };
  1         3  
  1         17  
320             }
321             else
322             {
323 2         4 $$final_args{$key}{$sub_key} = {%{$$args{$key}{$sub_key} } };
  2         31  
324             }
325             }
326             else
327             {
328 24 50       139 $$final_args{$key}{$sub_key} = defined $$args{$key}{$sub_key} ? $$args{$key}{$sub_key} : $$extra_args{$key}{$sub_key};
329             }
330             }
331             }
332             else
333             {
334             # Overwrite when not a hashref.
335              
336 12         33 $$final_args{$key} = $$args{$key};
337             }
338             }
339              
340             # Now process args returned from dispatch_args() but not sent to this method.
341              
342 30         91 for my $key (keys %$extra_args)
343             {
344             # If the sub key points to a hashref, merge data.
345              
346 99 100       214 if (ref $$extra_args{$key} eq 'HASH')
347             {
348             # But only merge if $final_args contains data. Otherwise, overwrite.
349              
350 24 100       59 if (exists $$final_args{$key})
351             {
352             # But PARAMS itself is a hashref key.
353              
354 18 100       42 if ($$final_args{$key}{PARAMS})
355             {
356 3 100       10 if (exists $$extra_args{$key}{PARAMS})
357             {
358 1         2 $$final_args{$key}{PARAMS} = {%{$$final_args{$key}{PARAMS} }, %{$$extra_args{$key}{PARAMS} } };
  1         3  
  1         6  
359             }
360             }
361             else
362             {
363 15         27 $$final_args{$key}= {%{$$final_args{$key} }, %{$$extra_args{$key} } };
  15         40  
  15         71  
364             }
365             }
366             else
367             {
368 6         72 $$final_args{$key} = $$extra_args{$key};
369             }
370             }
371             else
372             {
373             # Overwrite since $$args{$key} does not exist.
374              
375 75 100       245 $$final_args{$key} = $$extra_args{$key} if (! exists $$args{$key});
376             }
377             }
378              
379 30         112 return $final_args;
380              
381             } # End of _merge_args.
382              
383             # --------------------------------------------------
384              
385             sub new
386             {
387 31     31 1 19362 my($class, %arg) = @_;
388 31         146 my($self) = bless {}, $class;
389 31         137 $self = $self -> _init(\%arg);
390              
391 31         128 return $self;
392              
393             } # End of new.
394              
395             # --------------------------------------------------
396              
397             sub _parse_error_document
398             {
399 2     2   7 my($self, $error_document, $error_number) = @_;
400              
401 2         8 $self -> log(debug => "_parse_error_document(..., $error_number)");
402              
403             # Jam the error number into the document, if the latter contains %s.
404              
405 2         11 my($s) = sprintf($error_document, $error_number);
406              
407 2         4 my($output);
408             my($url);
409              
410 2 100       13 if (index($s, '"') == 0)
    50          
411             {
412             # It's a customised error string.
413             # Discard the leading " & use it as the output.
414              
415 1         12 $output = substr($s, 1);
416             }
417             elsif (index($s, '<') == 0)
418             {
419             # It's a local file, which is - hopefully - secure. Read it as the output.
420             # If we can't read it, $output will remain undef.
421              
422 1         12 require File::Spec;
423              
424 1         5 my($doc_root) = $ENV{DOCUMENT_ROOT};
425 1         3 $s = substr($s, 1);
426 1 50       3 $s = File::Spec -> catdir($doc_root, $s) if ($doc_root);
427              
428 1         14 $self -> log(debug => "Reading file $s");
429              
430 1 50 33     90 if (-f $s && open(INX, '<', $s) )
431             {
432 1         6 local $/ = undef;
433 1         750 $output = ;
434              
435 1         30 close INX;
436             }
437             else
438             {
439 0         0 carp "[Dispatch] Unable to open error_document file $s";
440             }
441             }
442             else
443             {
444             # It's neither customised error string nor file name.
445             # Assume it's a url. Keep it separate from $output for later.
446              
447 0         0 $url = $s;
448             }
449              
450 2 50       11 $self -> log(debug => "Redirecting HTTP error $error_number to $url") if ($url);
451 2 50       64 $self -> log(debug => "Displaying message for HTTP error $error_number") if ($output);
452              
453 2         8 return ($output, $url);
454              
455             } # End of _parse_error_document.
456              
457             # --------------------------------------------------
458              
459             sub _parse_path
460             {
461 25     25   50 my($self, $http_method, $path_info, $table) = @_;
462              
463 25         103 $self -> log(debug => "_parse_path($path_info, ...)");
464              
465             # Compare each rule in the table with the path_info, and process the 1st match.
466              
467 25         40 my($request_method_regexp, $rule);
468              
469 25         75 for (my $i = 0; $i < scalar @$table; $i += 2)
470             {
471 71         122 $rule = $$table[$i];
472              
473 71 50       153 next if (! defined $rule);
474              
475 71         215 $self -> log(debug => "Original rule '$rule'");
476              
477             # Firstly, look for a HTTP method name in the rule,
478             # as something like ':app/news[post]' => {rm => 'add_news'}.
479              
480 71         279 $request_method_regexp = qr/\[([^\]]+)\]$/;
481              
482 71 100       345 if ($rule =~ /$request_method_regexp/)
483             {
484             # If the method doesn't match the rule can't possibly match.
485              
486 3 100       98 next if (lc $http_method ne lc $1);
487              
488 2         10 $self -> log(debug => "Matched HTTP method '$http_method'");
489              
490             # Remove the method portion from the rule.
491              
492 2         17 $rule =~ s/$request_method_regexp//;
493             }
494              
495             # Standardize the format of the rule, to match the standardized path info.
496              
497 70 100       233 $rule = "/$rule" if (index($rule, '/') != 0);
498 70 100       199 $rule = "$rule/" if (substr($rule, -1) ne '/');
499              
500 70         180 $self -> log(debug => "Rule is now '$rule'");
501              
502             # Translate the rule into a regular expression, remembering where the named args are.
503             # '/:foo' will become '/([^\/]*)'
504             # and
505             # '/:bar?' will become '/?([^\/]*)?'
506             # and then remember which position it matches.
507              
508 70         554 my(@names);
509              
510 70         344 $rule =~
511             s{
512             (^|/) # Beginning, or a /.
513             (:([^/\?]+)(\?)?) # Stuff in between.
514             }{
515 83         182 push @names, $3;
516 83 100       360 $1 . ($4 ? '?([^/]*)?' : '([^/]*)')
517             }gxe;
518              
519             # '/*/' will become '/(.*)/$'.
520             # The final '/' has been added to the end of both $rule and $path_info already.
521              
522 70 100       206 if ($rule =~ m{/\*/$})
523             {
524 9         36 $rule =~ s{/\*/$}{/(.*)/\$};
525              
526 9         17 push @names, 'dispatch_url_remainder';
527             }
528              
529 70         205 $self -> log(debug => "Rule is now '$rule'");
530 70         248 $self -> log(debug => "Names in rule [" . join(', ', @names) . ']');
531 70         280 $self -> log(debug => "Trying to match path info '$path_info' against rule '$$table[$i]' using regexp '$rule'");
532              
533             # If we find a match, then run with it.
534              
535 70 100       2210 if (my @values = ($path_info =~ m#^$rule$#) )
536             {
537 25         67 $self -> log(debug => 'Matched!');
538              
539 25         32 my(%named_args) = %{$$table[++$i]};
  25         108  
540 25 100       128 @named_args{@names} = @values if @names;
541              
542 25         210 return {%named_args};
543             }
544             }
545              
546             # No rule matched the given path info.
547              
548 0         0 $self -> log(debug => 'Nothing matched');
549              
550 0         0 return {};
551              
552             } # End of _parse_path.
553              
554             # --------------------------------------------------
555              
556             sub _prepare
557             {
558 22     22   45 my($self, $http_method, $args, $named_args) = @_;
559 22   100     83 $http_method ||= '';
560              
561 22         77 $self -> log(debug => "_prepare($http_method, ...)");
562              
563 22         47 my($module, $prefix, $rm, $args_to_new) = delete @{$named_args}{qw(app prefix rm args_to_new)};
  22         76  
564 22 50       71 $module = '' if (! defined $module); # Stop uninit warning.
565 22 100       57 $rm = '' if (! defined $rm);
566              
567             # If another name for dispatch_url_remainder has been set, move the value to the requested name.
568              
569 22 100       61 if ($$named_args{'*'})
570             {
571 1         4 $$named_args{$$named_args{'*'} } = $$named_args{'dispatch_url_remainder'};
572              
573 1         3 delete $$named_args{'*'};
574 1         2 delete $$named_args{'dispatch_url_remainder'};
575             }
576              
577             # Warning: The following statement was copied from CGI::Application::Dispatch,
578             # but it does not do what you think, due to the way Perl equivalences hashrefs.
579             # The symptom is that up at line 62:
580             # $$args_to_new{QUERY} = CGI::PSGI -> new($env) if (! $$args_to_new{QUERY});
581             # it has the effect of setting $args{args_to_new}, and not just $args_to_new.
582             # That means the 'if (! $$args_to_new{QUERY})' stops a new CGI::PSGI being assigned
583             # during each call of the subref, so the initial CGI::PSGI object is preserved,
584             # and of course it has no CGI parameters, so no parameters are ever received :-(.
585              
586             #$args_to_new ||= $$args{args_to_new};
587              
588 22 50       229 if (! $args_to_new)
589             {
590 22         32 my(%new_args) = %{$$args{args_to_new} };
  22         82  
591 22         73 $args_to_new = {%new_args};
592             }
593              
594 22         66 @{$$args_to_new{PARAMS} }{keys %$named_args} = values %$named_args;
  22         54  
595 22 50       70 $args_to_new = {} if (! $args_to_new);
596 22         84 $module = $self -> translate_module_name($module);
597 22   100     141 $prefix ||= $$args{prefix} || '';
      100        
598 22 100       71 $module = $prefix . '::' . $module if ($prefix);
599 22 50       81 my($auto_rest) = defined $$named_args{auto_rest} ? $$named_args{auto_rest} : $$args{auto_rest};
600              
601 22 100       53 if ($auto_rest)
602             {
603 2 50       10 my($method_lc) = defined $$named_args{auto_rest_lc} ? $$named_args{auto_rest_lc} : $$args{auto_rest_lc};
604 2 100       16 $http_method = lc $http_method if ($method_lc);
605 2 50       13 $rm = length $rm ? "${rm}_$http_method" : $rm;
606             }
607              
608 22         88 $self -> log(debug => "Trying to load '$module'. Run method is '$rm'");
609              
610 22         108 try_load_class $module;
611              
612 22 100       50384 if (is_class_loaded $module)
613             {
614 21         1182 $self -> log(debug => "Loaded '$module'");
615             }
616             else
617             {
618 1         237 croak 404;
619             }
620              
621 21         88 return ($module, $rm, $args_to_new);
622              
623             } # End of _prepare.
624              
625             # --------------------------------------------------
626              
627             sub translate_module_name
628             {
629 22     22 1 37 my($self, $name) = @_;
630              
631 22         76 $self -> log(debug => "translate_module_name($name)");
632              
633 22         87 $name = join('::', map{ucfirst $_} split(/_/, $name) );
  35         128  
634 22         72 $name = join('', map{ucfirst $_} split(/-/, $name) );
  24         89  
635              
636 22         53 return $name;
637              
638             } # End of translate_module_name.
639              
640             # --------------------------------------------------
641              
642             1;
643              
644             =pod
645              
646             =head1 NAME
647              
648             CGI::Snapp::Dispatch - Dispatch requests to CGI::Snapp-based objects
649              
650             =head1 Synopsis
651              
652             =head2 CGI Scripts
653              
654             Here is a minimal CGI instance script. I
655              
656             #!/usr/bin/env perl
657              
658             use CGI::Snapp::Dispatch;
659              
660             CGI::Snapp::Dispatch -> new -> dispatch;
661              
662             (The use of new() is discussed in detail under L, just below.)
663              
664             But, to override the default dispatch table, you probably want something like this:
665              
666             MyApp/Dispatch.pm:
667              
668             package MyApp::Dispatch;
669             parent 'CGI::Snapp::Dispatch';
670              
671             sub dispatch_args
672             {
673             my($self) = @_;
674              
675             return
676             {
677             prefix => 'MyApp',
678             table =>
679             [
680             '' => {app => 'Initialize', rm => 'start'},
681             ':app/:rm' => {},
682             'admin/:app/:rm' => {prefix => 'MyApp::Admin'},
683             ],
684             };
685             }
686              
687             And then you can write ... I
688              
689             #!/usr/bin/env perl
690              
691             use MyApp::Dispatch;
692              
693             MyApp::Dispatch -> new -> dispatch;
694              
695             =head2 PSGI Scripts
696              
697             Here is a PSGI script in production on my development machine. I
698              
699             #!/usr/bin/env perl
700             #
701             # Run with:
702             # starman -l 127.0.0.1:5020 --workers 1 httpd/cgi-bin/local/wines.psgi &
703             # or, for more debug output:
704             # plackup -l 127.0.0.1:5020 httpd/cgi-bin/local/wines.psgi &
705              
706             use strict;
707             use warnings;
708              
709             use CGI::Snapp::Dispatch;
710              
711             use Plack::Builder;
712              
713             # ---------------------
714              
715             my($app) = CGI::Snapp::Dispatch -> new -> as_psgi
716             (
717             prefix => 'Local::Wines::Controller', # A sub-class of CGI::Snapp.
718             table =>
719             [
720             '' => {app => 'Initialize', rm => 'display'},
721             ':app' => {rm => 'display'},
722             ':app/:rm/:id?' => {},
723             ],
724             );
725              
726             builder
727             {
728             enable "ContentLength";
729             enable "Static",
730             path => qr!^/(assets|favicon|yui)!,
731             root => '/dev/shm/html'; # /dev/shm/ is Debian's RAM disk.
732             $app;
733             };
734              
735             I The line my($app) = ... contains a call to L. This is definitely not the same as if you
736             were using L or L. They look like this:
737              
738             my($app) = CGI::Application::Dispatch -> as_psgi
739              
740             The lack of a call to new() there tells you I've implemented something very similar but different.
741             You have been warned...
742              
743             The point of this difference is that new() returns an object, and passing that into L as $self
744             allows the latter method to be much more sophisticated than it would otherwise be. Specifically, it can now share
745             a lot of code with L.
746              
747             Lastly, if you want to use regexps to match the path info, see L.
748              
749             =head1 Description
750              
751             This module provides a way to automatically look at the path info - $ENV{PATH_INFO} - of the incoming HTTP request,
752             and to process that path info like this:
753              
754             =over 4
755              
756             =item o Parse off a module name
757              
758             =item o Parse off a run mode
759              
760             =item o Create an instance of that module (i.e. load it)
761              
762             =item o Run that instance
763              
764             =item o Return the output of that run as the result of requsting that path info (i.e. module and run mode combo)
765              
766             =back
767              
768             Thus, it will translate a URI like this:
769              
770             /app/index.cgi/module_name/run_mode
771              
772             into something that is functionally equivalent to this:
773              
774             my($app) = Module::Name -> new(...);
775              
776             $app -> mode_param(sub {return 'run_mode'});
777              
778             return $app -> run;
779              
780             =head1 Distributions
781              
782             This module is available as a Unix-style distro (*.tgz).
783              
784             See L
785             for help on unpacking and installing distros.
786              
787             =head1 Installation
788              
789             Install L as you would for any C module:
790              
791             Run:
792              
793             cpanm CGI::Snapp::Dispatch
794              
795             or run:
796              
797             sudo cpan CGI::Snapp::Dispatch
798              
799             or unpack the distro, and then either:
800              
801             perl Build.PL
802             ./Build
803             ./Build test
804             sudo ./Build install
805              
806             or:
807              
808             perl Makefile.PL
809             make (or dmake or nmake)
810             make test
811             make install
812              
813             =head1 Constructor and Initialization
814              
815             C is called as C<< my($app) = CGI::Snapp::Dispatch -> new(k1 => v1, k2 => v2, ...) >>.
816              
817             It returns a new object of type C.
818              
819             Key-value pairs accepted in the parameter list (see corresponding methods for details
820             [e.g. L]):
821              
822             =over 4
823              
824             =item o logger => $aLoggerObject
825              
826             Specify a logger compatible with L.
827              
828             Note: This logs method calls etc inside CGI::Snapp::Dispatch.
829              
830             To log within L, see L.
831              
832             Default: '' (The empty string).
833              
834             To clarify: The built-in calls to log() all use a log level of 'debug', so if your logger has 'maxlevel' set
835             to anything less than 'debug', nothing nothing will get logged.
836              
837             'maxlevel' and 'minlevel' are discussed in L and L.
838              
839             =item o return_type => $integer
840              
841             Possible values for $integer:
842              
843             =over 4
844              
845             =item o 0 (zero)
846              
847             dispatch() returns the output of the run mode.
848              
849             This is the default.
850              
851             =item o 1 (one)
852              
853             dispatch() returns the hashref of args built from combining the output of dispatch_args() and the
854             args to dispatch().
855              
856             The requested module is I loaded and run. See t/args.t.
857              
858             =item o 2 (two)
859              
860             dispatch() returns the hashref of args build from parsing the path info.
861              
862             The requested module is I loaded and run. See t/args.t.
863              
864             =back
865              
866             Default: 0.
867              
868             Note: I is ignored by L.
869              
870             =back
871              
872             =head1 Methods
873              
874             =head2 as_psgi(@args)
875              
876             Returns a L-compatible coderef which, when called, runs your sub-class of L
877             as a L app.
878              
879             This works because the coderef actually calls L.
880              
881             See the next method, L, for a discussion of @args, which may be a hash or hashref.
882              
883             Lastly: as_psgi() does not support the I option the way dispatch({table => {error_document => ...} })
884             does. Rather, it throws errors of type L. Consider handling these errors with
885             L or similar.
886              
887             =head2 dispatch(@args)
888              
889             Returns the output generated by calling a L-based module.
890              
891             @args is a hash or hashref of options, which includes the all-important 'table' key, to define a dispatch table.
892             See L for details.
893              
894             The unfortunate mismatch between dispatch() taking a hash and dispatch_args() taking a hashref has been copied
895             from L. But, to clean things up, L allows dispatch() to accept
896             a hashref. You are encouraged to always use hashrefs, to avoid confusion.
897              
898             (Key => value) pairs which may appear in the hashref parameter ($args[0]):
899              
900             =over
901              
902             =item o args_to_new => $hashref
903              
904             This is a hashref of arguments that are passed into the constructor (C) of the application.
905              
906             If you wish to set parameters in your app which can be retrieved by the $self -> param($key) method, then use:
907              
908             my($app) = CGI::Snapp::Dispatch -> new;
909             my($output) = $app -> dispatch(args_to_new => {PARAMS => {key1 => 'value1'} });
910              
911             This means that inside your app, $self -> param('key1') will return 'value1'.
912              
913             See t/args.t's test_13(), which calls t/lib/CGI/Snapp/App1.pm's rm2().
914              
915             See also t/lib/CGI/Snapp/Dispatch/SubClass1.pm's dispatch_args() for how to pass in one or more such values via
916             your sub-class.
917              
918             =item o auto_rest => $Boolean
919              
920             If 1, this tells Dispatch that you are using REST by default and that you care about which HTTP method
921             is being used. Dispatch will append the HTTP method name (upper case by default) to
922             the run mode that is determined after finding the appropriate dispatch rule. So a GET request
923             that translates into C<< MyApp::Module -> foo >> will become C<< MyApp::Module -> foo_GET >>.
924              
925             This can be overridden on a per-rule basis in a derived class's dispatch table. See also the next option.
926              
927             Default: 0.
928              
929             See t/args.t test_27().
930              
931             =item o auto_rest_lc => $Boolean
932              
933             If 1, then in combination with I, this tells Dispatch that you prefer lower cased HTTP method names.
934             So instead of C and C you'll get C and C.
935              
936             See t/args.t test_28().
937              
938             =item o default
939              
940             Specify a value to use for the path info if one is not available.
941             This could be the case if the default page is selected (e.g.: '/cgi-bin/x.cgi' or perhaps '/cgi-bin/x.cgi/').
942              
943             =item o error_document
944              
945             Note: When using L, error_document makes no sense, and is ignored.
946             In that case, use L or similar.
947              
948             If this value is not provided, and something goes wrong, then Dispatch will return a '500 Internal Server Error',
949             using an internal HTML page. See t/args.t, test_25().
950              
951             Otherwise, the value should be one of the following:
952              
953             =over 4
954              
955             =item o A customised error string
956              
957             To use this, the string must start with a single double-quote (") character. This character
958             character will be trimmed from final output.
959              
960             =item o A file name
961              
962             To use this, the string must start with a less-than sign (<) character. This character
963             character will be trimmed from final output.
964              
965             $ENV{DOCUMENT_ROOT}, if not empty, will be prepended to this file name.
966              
967             The file will be read in and used as the error document.
968              
969             See t/args.t, test_26().
970              
971             =item o A URL to which the application will be redirected
972              
973             This happens when the I does not start with " or <.
974              
975             =back
976              
977             Note: In all 3 cases, the string may contain a '%s', which will be replaced with the error number (by sprintf).
978              
979             Currently CGI::Snapp::Dispatch uses three HTTP errors:
980              
981             =over 4
982              
983             =item o 400 Bad Request
984              
985             This is output if the run mode is not specified, or it contains an invalid character.
986              
987             =item o 404 Not Found
988              
989             This is output if the module name is not specified, or if there was no match with the dispatch table,
990             or the module could not be loaded by L.
991              
992             =item o 500 Internal Server Error
993              
994             This is output if the application dies.
995              
996             =back
997              
998             See t/args.t, test_24().
999              
1000             =item o prefix
1001              
1002             This option will set the string to be prepended to the name of the application
1003             module before it is loaded and created.
1004              
1005             For instance, consider /app/index.cgi/module_name/run_mode.
1006              
1007             This would, by default, load and create a module named 'Module::Name'. But let's say that you
1008             have all of your application specific modules under the 'My' namespace. If you set this option
1009             - C - to 'My' then it would instead load the 'My::Module::Name' application module instead.
1010              
1011             The algorithm for converting a path info into a module name is documented in L.
1012              
1013             =item o table
1014              
1015             In most cases, simply using Dispatch with the C and C is enough
1016             to simplify your application and your URLs, but there are many cases where you want
1017             more power. Enter the dispatch table (a hashref), specified here as the value of the C key.
1018              
1019             Since this table can be slightly complicated, a whole section exists on its use. Please see the L section.
1020              
1021             Examples are in the dispatch_args() method of both t/lib/CGI/Snapp/Dispatch/SubClass1.pm and
1022             t/lib/CGI/Snapp/Dispatch/SubClass2.pm.
1023              
1024             =back
1025              
1026             =head2 dispatch_args($args)
1027              
1028             Returns a hashref of args to be used by L.
1029              
1030             This hashref is a dispatch table. See L for details.
1031              
1032             L calls this method, passing in the hash/hashref which was passed in to L.
1033              
1034             Default output:
1035              
1036             {
1037             args_to_new => {},
1038             default => '',
1039             prefix => '',
1040             table =>
1041             [
1042             ':app' => {},
1043             ':app/:rm' => {},
1044             ],
1045             }
1046              
1047             This is the perfect method to override when creating a subclass to provide a richer L.
1048              
1049             See CGI::Snapp::Dispatch::SubClass1 and CGI::Snapp::Dispatch::SubClass2, both under t/lib/. These modules are
1050             exercised by t/args.t.
1051              
1052             =head2 new()
1053              
1054             See L for details on the parameters accepted by L.
1055              
1056             Returns an object of type L.
1057              
1058             =head2 translate_module_name($name)
1059              
1060             This method is used to control how the module name is translated from
1061             the matching section of the path. See L.
1062              
1063             The main reason that this method exists is so that it can be overridden if it doesn't do
1064             exactly what you want.
1065              
1066             The following transformations are performed on the input:
1067              
1068             =over 4
1069              
1070             =item o The text is split on '_'s (underscores)
1071              
1072             Next, each word has its first letter capitalized. The words are then joined
1073             back together using '::'.
1074              
1075             =item o The text is split on '-'s (hyphens)
1076              
1077             Next, each word has its first letter capitalized. The words are then joined
1078             back together without the '-'s.
1079              
1080             =back
1081              
1082             Examples:
1083              
1084             module_name => Module::Name
1085             module-name => ModuleName
1086             admin_top-scores => Admin::TopScores
1087              
1088             =head1 FAQ
1089              
1090             =head2 What is 'path info'?
1091              
1092             For a L script, it is just $ENV{PATH_INFO}. The value of $ENV{PATH_INFO} is normally set by the web server
1093             from the path info sent by the HTTP client.
1094              
1095             A request to /cgi-bin/x.cgi/path/info will set $ENV{PATH_INFO} to /path/info.
1096              
1097             For Apache, whether $ENV{PATH_INFO} is set or not depends on the setting of the
1098             L directive.
1099              
1100             For a L script, it is $$env{PATH_INFO}, within the $env hashref provided by PSGI.
1101              
1102             Path info is also discussed in L.
1103              
1104             Similar comments apply to the request method (GET, PUT etc) which may be used in rules.
1105              
1106             For CGI scripts, request method comes from $ENV{HTTP_REQUEST_METHOD} || $ENV{REQUEST_METHOD}, whereas for PSGI
1107             scripts it is just $$env{REQUEST_METHOD}.
1108              
1109             =head2 Is there any sample code?
1110              
1111             Yes. See t/args.t and t/lib/*.
1112              
1113             =head2 Why did you fork L?
1114              
1115             To be a companion module for L.
1116              
1117             =head2 What version of L did you fork?
1118              
1119             V 3.07.
1120              
1121             =head2 How does CGI::Snapp::Dispatch differ from CGI::Application::Dispatch?
1122              
1123             =head3 There is no module called CGI::Snapp::Dispatch::PSGI
1124              
1125             This just means the L-specific code is incorporated into CGI::Snapp::Dispatch.
1126             See L.
1127              
1128             =head3 Processing parameters to dispatch() and dispatch_args()
1129              
1130             The code which combines parameters to these 2 subs has been written from scratch. Obviously, the intention is that
1131             the new code behave in an identical fashion to the corresponding code in L.
1132              
1133             Also, the re-write allowed me to support a version of L which accepts a hashref, not just a hash.
1134             The same flexibility has been added to L.
1135              
1136             =head3 No special code for Apache, mod_perl or plugins
1137              
1138             I suggest that sort of stuff is best put in sub-classes.
1139              
1140             =head3 Unsupported features
1141              
1142             =over 4
1143              
1144             =item o dispatch_path()
1145              
1146             Method dispatch_path() is not provided. For L scripts, the code in dispatch() accesses $ENV{PATH_INFO} directly,
1147             whereas for L scripts, as_psgi() accesses the L environment
1148             hashref $$env{PATH_INFO}.
1149              
1150             =back
1151              
1152             =head3 Enhanced features
1153              
1154             L can take extra parameters:
1155              
1156             =over 4
1157              
1158             =item o return_type
1159              
1160             Note: I is ignored by L.
1161              
1162             =back
1163              
1164             =head3 This module uses Class::Load to try loading your application's module
1165              
1166             L uses:
1167              
1168             eval "require $module";
1169              
1170             whereas CGI::Snapp::Dispatch uses 2 methods from L:
1171              
1172             try_load_class $module;
1173             croak 404 if (! is_class_loaded $module);
1174              
1175             For L scripts, the 404 (and all other error numbers) is handled by sub _http_error(), whereas for
1176             L scripts, the code throws errors of type L.
1177              
1178             =head3 Reading an error document from a file
1179              
1180             L always prepends $ENV{DOCUMENT_ROOT} to the file name.
1181             Unfortunately, this means that when $ENV{DOCUMENT_ROOT} is not set, File::Spec prepends a '/' to the file name.
1182             So, an I of '
1183              
1184             This module only prepends $ENV{DOCUMENT_ROOT} if it is not empty. Hence, with an empty $ENV{DOCUMENT_ROOT},
1185             an I of '
1186              
1187             See sub _parse_error_document() and t/args.t test_26().
1188              
1189             =head3 Handling of exceptions
1190              
1191             L uses a combination of eval and L, together with L.
1192             Likewise, L uses the same combination, although without L.
1193              
1194             CGI::Snapp::Dispatch just uses L. This applies both to CGI scripts and PSGI scripts.
1195             For L scripts, errors are handled by sub _http_errror(). For L scripts, the code
1196             throws errors of type L.
1197              
1198             =head2 How does CGI::Snapp parse the path info?
1199              
1200             Firstly, the path info is split on '/' chars. Hence /module_name/mode1 gives us ('', 'module_name', 'mode1').
1201              
1202             The value 'module_name' is passed to L. In this case, the result is 'Module::Name'.
1203              
1204             You are free to override L to customize it.
1205              
1206             After that, the I option's value, if any, is added to the front of 'Module::Name'. See L for
1207             more about I.
1208              
1209             FInally, 'mode1' becomes the name of the run mode.
1210              
1211             Remember from the docs for L, that this is the I of the run mode, but is not necessarily the name
1212             of the method which will be run. The code in your sub-class of L can map run mode names to method
1213             names.
1214              
1215             For instance, a statement like:
1216              
1217             $self -> run_modes({rm_name_1 => 'rm_method_1', rm_name_2 => 'rm_method_2'});
1218              
1219             in (probably) sub setup(), shows how to separate run mode names from method names.
1220              
1221             =head2 What is the structure of the dispatch table?
1222              
1223             Sometimes it's easiest to explain with an example, so here you go:
1224              
1225             CGI::Snapp::Dispatch -> new -> dispatch # Note the new()!
1226             (
1227             args_to_new =>
1228             {
1229             PARAMS => {big => 'small'},
1230             },
1231             default => '/app',
1232             prefix => 'MyApp',
1233             table =>
1234             [
1235             '' => {app => 'Blog', rm => 'recent'},
1236             'posts/:category' => {app => 'Blog', rm => 'posts'},
1237             ':app/:rm/:id' => {app => 'Blog'},
1238             'date/:year/:month?/:day?' =>
1239             {
1240             app => 'Blog',
1241             rm => 'by_date',
1242             args_to_new => {PARAMS => {small => 'big'} },
1243             },
1244             ]
1245             );
1246              
1247             Firstly note, that besides passing this structure into L, you could sub-class L
1248             and design L to return exactly the same structure.
1249              
1250             OK. The components, all of which are optional, are:
1251              
1252             =over 4
1253              
1254             =item o args_to_new => $hashref
1255              
1256             This is how you specify a hashref of parameters to be passed to the constructor (new() ) of your sub-class of
1257             L.
1258              
1259             =item o default => $string
1260              
1261             This specifies a default for the path info in the case this code is called with an empty $ENV{PATH_INFO}.
1262              
1263             =item o prefix => $string
1264              
1265             This specifies a namespace to prepend to the class name derived by processing the path info.
1266              
1267             E.g. If path info was /module_name, then the above would produce 'MyApp::Module::Name'.
1268              
1269             =item o table => $arrayref
1270              
1271             This provides a set of rules, which are compared - 1 at a time, in the given order - with the path info, as the code tries to match the
1272             incoming path info to a rule you have provided.
1273              
1274             The first match wins.
1275              
1276             Each element of the array consists of a I and an I.
1277              
1278             Rules can be empty (see '' above), or they may be a combination of '/' chars and tokens. A token can be one of:
1279              
1280             =over 4
1281              
1282             =item o A literal
1283              
1284             Any token which does not start with a colon (:) is taken to be a literal string and must appear exactly as-is
1285             in the path info in order to match. In the rule 'posts/:category', posts is a literal.
1286              
1287             =item o A variable
1288              
1289             Any token which begins with a colon (:) is a variable token. These are simply wild-card place holders in the rule
1290             that will match anything - in the corresponding position - in the path info that isn't a slash.
1291              
1292             These variables can later be referred to in your application (sub-class of L) by using the
1293             $self -> param($name) mechanism. In the rule 'posts/:category', ':category' is a variable token.
1294              
1295             If the path info matched this rule, you could retrieve the value of that token from within your application
1296             like so: my($category) = $self -> param('category');.
1297              
1298             There are some variable tokens which are special. These can be used to further customize the dispatching.
1299              
1300             =over 4
1301              
1302             =item o :app
1303              
1304             This is the module name of the application. The value of this token will be sent to L
1305             and then prefixed with the prefix if there is one.
1306              
1307             =item o :rm
1308              
1309             This is the run mode of the application. The value of this token will be the actual name of the run mode used.
1310             As explained just above (L), this is not necessarily the name of the
1311             method within the module which will be run.
1312              
1313             =back
1314              
1315             =item o An optional variable
1316              
1317             Any token which begins with a colon (:) and ends with a question mark (?) is considered optional.
1318             If the rest of the path info matches the rest of the rule, then it doesn't matter whether it contains this token
1319             or not. It's best to only include optional variable tokens at the end of your rule. In the rule
1320             'date/:year/:month?/:day?', ':month?' and ':day?' are optional-variable tokens.
1321              
1322             Just as with variable tokens, optional-variable tokens' values can be retrieved by the application,
1323             if they existed in the path info. Try:
1324              
1325             if (defined $self -> param('month') )
1326             {
1327             ...
1328             }
1329              
1330             Lastly, $self -> param('month') will return undef if ':month?' does not match anything in the path info.
1331              
1332             =item o A wildcard
1333              
1334             The wildcard token '*' allows for partial matches. The token I appear at the end of the rule.
1335              
1336             E.g.: 'posts/list/*'. Given this rule, the 'dispatch_url_remainder' param is set to the remainder of the
1337             path info matched by the *. The name ('dispatch_url_remainder') of the param can be changed by setting '*'
1338             argument in the I. This example:
1339              
1340             'posts/list/*' => {'*' => 'post_list_filter'}
1341              
1342             specifies that $self -> param('post_list_filter') rather than $self -> param('dispatch_url_remainder') is to be
1343             used in your app, to retrieve the value which was passed in via the path info.
1344              
1345             See t/args.t, test_21() and test_22(), and the corresponding sub rm5() in t/lib/CGI/Snapp/App2.pm.
1346              
1347             =item o A HTTP method name
1348              
1349             You can also dispatch based on HTTP method. This is similar to using I but offers more fine-grained
1350             control. You include the (case insensitive) method name at the end of the rule and enclose it in square brackets.
1351             Samples:
1352              
1353             ':app/news[post]' => {rm => 'add_news' },
1354             ':app/news[get]' => {rm => 'news' },
1355             ':app/news[delete]' => {rm => 'delete_news'},
1356              
1357             The main reason that we don't use regular expressions for dispatch rules is that regular expressions did not provide
1358             for named back references (until recent versions of Perl), in the way variable tokens do.
1359              
1360             =back
1361              
1362             =back
1363              
1364             =head2 How do I use my own logger object?
1365              
1366             Study the sample code in L, which shows how to supply a L *.ini file to configure the logger via the wrapper class
1367             L.
1368              
1369             Also, see t/logs.t, t/log.a.pl and t/log.b.pl.
1370              
1371             See also L for important info and sample code.
1372              
1373             =head2 This module uses Hash::FieldHash, which has an XS component!
1374              
1375             Yep.
1376              
1377             My policy is that stand-alone modules should use a light-weight object manager (my choice is L), whereas apps can - and probably should - use L.
1378              
1379             =head2 How do I sub-class CGI::Snapp::Dispatch?
1380              
1381             You do this the same way you sub-class L. See L.
1382              
1383             =head2 Are there any security implications from using this module?
1384              
1385             Yes. Since CGI::Snapp::Dispatch will dynamically choose which modules to use as content generators,
1386             it may give someone the ability to execute specially crafted modules on your system if those modules can be found
1387             in Perl's @INC path. This should only be a problem if you don't use a I.
1388              
1389             Of course those modules would have to behave like L based modules, but that still opens up the door
1390             more than most want.
1391              
1392             By using the I option you are only allowing Dispatch to pick modules from a pre-defined namespace.
1393              
1394             =head2 Why is CGI::PSGI required in Build.PL and Makefile.PL when it's sometimes not needed?
1395              
1396             It's a tradeoff. Leaving it out of those files is convenient for users who don't run under a PSGI environment,
1397             but it means users who do use PSGI must install L explicitly. And, worse, it means their code
1398             does not run by default, but only runs after manually installing that module.
1399              
1400             So, since L's only requirement is L, it's simpler to just always require it.
1401              
1402             =head1 Troubleshooting
1403              
1404             =head2 It doesn't work!
1405              
1406             Things to consider:
1407              
1408             =over 4
1409              
1410             =item o Run the *.cgi script from the command line
1411              
1412             shell> perl httpd/cgi-bin/cgi.snapp.one.cgi
1413              
1414             If that doesn't work, you're in b-i-g trouble. Keep reading for suggestions as to what to do next.
1415              
1416             =item o Did you try using a logger to trace the method calls?
1417              
1418             Pass a logger to your sub-class of L like this:
1419              
1420             my($logger) = Log::Handler -> new;
1421              
1422             $logger -> add
1423             (
1424             screen =>
1425             {
1426             maxlevel => 'debug',
1427             message_layout => '%m',
1428             minlevel => 'error',
1429             newline => 1, # When running from the command line.
1430             }
1431             );
1432             CGI::Snapp::Dispatch -> new -> as_psgi({args_to_new => {logger => $logger} }, ...);
1433              
1434             In addition, you can trace CGI::Snapp::Dispatch itself with the same (or a different) logger:
1435              
1436             CGI::Snapp::Dispatch -> new(logger => $logger) -> as_psgi({args_to_new => {logger => $logger} }, ...);
1437              
1438             The entry to each method in L and CGI::Snapp::Dispatch is logged using this technique,
1439             although only when maxlevel is 'debug'. Lower levels for maxlevel do not trigger logging.
1440             See the source for details. By 'this technique' I mean there is a statement like this at the entry of each method:
1441              
1442             $self -> log(debug => 'Entered x()');
1443              
1444             =item o Are you confused about combining parameters to dispatch() and dispatch_args()?
1445              
1446             I suggest you use the I option to L to capture output from the parameter merging code
1447             before trying to run your module. See t/args.t.
1448              
1449             =item o Are you confused about patterns in tables which do/don't use ':app' and ':rm'?
1450              
1451             The golden rule is:
1452              
1453             =over 4
1454              
1455             =item o If the rule uses 'app', then it is non-capturing
1456              
1457             This means the matching app name from $ENV{PATH_INFO} is I saved, so you must provide a modue name
1458             in the table's rule. E.g.: 'app/:rm' => {app => 'MyModule}, or perhaps use the I option to specify
1459             the complete module name.
1460              
1461             =item o If the rule uses ':app', then it is capturing
1462              
1463             This means the matching app name from $ENV{PATH_INFO} I saved, and it becomes the name of the module.
1464             Of course, I might come into play here, too.
1465              
1466             =back
1467              
1468             =item o Did you forget the leading < (read from file) in the customised error document file name?
1469              
1470             =item o Did you forget the leading " (double-quote) in the customised error document string?
1471              
1472             =item o Did you forget the embedded %s in the customised error document?
1473              
1474             This triggers the use of sprintf to merge the error number into the string.
1475              
1476             =item o Are you trying to use this module with an app non based on CGI::Snapp?
1477              
1478             Remember that L's new() takes a hash, not a hashref.
1479              
1480             =item o Did you get the mysterious error 'No such field "priority"'?
1481              
1482             You did this:
1483              
1484             as_psgi(args_to_new => $logger, ...)
1485              
1486             instead of this:
1487              
1488             as_psgi(args_to_new => {logger => $logger, ...}, ...)
1489              
1490             =item o The system Perl 'v' perlbrew
1491              
1492             Are you using perlbrew? If so, recall that your web server will use the first line of your L script to find a Perl,
1493             and that line probably says something like #!/usr/bin/env perl.
1494              
1495             So, perhaps you'd better turn perlbrew off and install L and this module under the system Perl, before trying again.
1496              
1497             =item o Generic advice
1498              
1499             L.
1500              
1501             =back
1502              
1503             =head1 See Also
1504              
1505             L - A almost back-compat fork of CGI::Application.
1506              
1507             As of V 1.01, L now supports L-style apps.
1508              
1509             And see L for another way of matching the path info.
1510              
1511             =head1 Machine-Readable Change Log
1512              
1513             The file CHANGES was converted into Changelog.ini by L.
1514              
1515             =head1 Version Numbers
1516              
1517             Version numbers < 1.00 represent development versions. From 1.00 up, they are production versions.
1518              
1519             =head1 Credits
1520              
1521             Please read L, since this module is a fork of the non-Apache
1522             components of L.
1523              
1524             =head1 Support
1525              
1526             Email the author, or log a bug on RT:
1527              
1528             L.
1529              
1530             =head1 Author
1531              
1532             L was written by Ron Savage Iron@savage.net.auE> in 2012.
1533              
1534             Home page: L.
1535              
1536             =head1 Copyright
1537              
1538             Australian copyright (c) 2012, Ron Savage.
1539              
1540             All Programs of mine are 'OSI Certified Open Source Software';
1541             you can redistribute them and/or modify them under the terms of
1542             The Artistic License, a copy of which is available at:
1543             http://www.opensource.org/licenses/index.html
1544              
1545             =cut