File Coverage

blib/lib/CGI/Application/Plugin/ParsePath.pm
Criterion Covered Total %
statement 39 47 82.9
branch 20 28 71.4
condition n/a
subroutine 4 5 80.0
pod n/a
total 63 80 78.7


line stmt bran cond sub pod time code
1             package CGI::Application::Plugin::ParsePath;
2              
3 1     1   21280 use warnings;
  1         2  
  1         31  
4 1     1   5 use strict;
  1         1  
  1         631  
5              
6             =head1 NAME
7              
8             CGI::Application::Plugin::ParsePath - populate query parameters by parsing the
9             PATH_INFO
10              
11             =head1 VERSION
12              
13             Version 0.01
14              
15             =cut
16              
17             our $VERSION = '0.01';
18             our $DEBUG=0;
19              
20             sub import {
21 1     1   9 my $caller = scalar(caller);
22 1         7 $caller->add_callback(prerun => \&_parse_path);
23 1         71 goto &Exporter::import;
24             }
25              
26             =head1 SYNOPSIS
27              
28             This module populates the CGI query parameters based on the query path.
29             It shamelessly steals the PATH_INFO parsing method from Michael
30             Peters' CGI::Application::Dispatch.
31              
32             Because the query parameters rather than the application
33             parameters are populated, modules like
34             CGI::Application::Plugin::ValidateRM are supported.
35              
36             In your webapp.pl instance script:
37              
38             use My::Blog;
39              
40             # Supply a table that specifies rules for parsing the PATH.
41             # Basically, we loop through each line stopping at the first rule
42             # that matches. Path element definitions that preceded by colons
43             # populate CGI query parameters with the same name. In the case
44             # where an element name is followed by a question mark, the
45             # parameter is optional.
46              
47             my $webapp = My::Blog->new(
48             PARAMS => {
49             'table' => = [
50             '' => {rm => 'recent'},
51             'posts/:category' => {rm => 'posts' },
52             'date/:year/:month?/:day?' => {
53             rm => 'by_date',
54             },
55             '/:rm/:id' => { },
56             ];
57             }
58             );
59             $webapp->run();
60              
61             # Examples
62             # QUERY PATH: webapp.pl/
63             # QUERY PARAMS: rm = recent
64              
65             # QUERY PATH: webapp.pl/posts/3
66             # QUERY PARAMS: rm = posts, category = 3
67              
68             # QUERY PATH: webapp.pl/date/2004/12/02
69             # QUERY PARAMS: rm = by_date, year = 2004, month = 12, day = 02
70              
71             # QUERY PATH: webapp.pl/edit/1234
72             # QUERY PARAMS: rm = edit, id = 1234
73              
74             In your application module simply include the plugin:
75              
76             use CGI::Application::Plugin::ParsePath;
77              
78             =cut
79              
80             # We allow for mod_perl here, but we need to populate the mp query
81             # parameters in _parse)path
82 0     0   0 sub _http_method { $ENV{HTTP_REQUEST_METHOD}; }
83              
84             # This is Michael Peters path parser from CGI::Application::Dispatch
85             sub _parse_path {
86 6     6   42706 my $self = shift;
87 6         27 my $path = $ENV{PATH_INFO};
88 6         19 my $table = $self->param('table');
89 6 100       99 $path .='/' unless substr($path, -1, 1) eq '/';
90              
91             # get the module name from the table
92 6 50       19 return unless defined($path);
93              
94 6 50       25 unless (ref($table) eq 'ARRAY' ) {
95 0         0 warn "[Dispatch] Invalid or no dispatch table!\n";
96 0         0 return;
97             }
98              
99             # look at each rule and stop when we get a match
100 6         27 for ( my $i = 0 ; $i < scalar(@$table) ; $i += 2 ) {
101 24         205 my $rule = $table->[$i];
102              
103             # are we trying to dispatch based on HTTP_METHOD?
104 24         83 my $http_method_regex = qr/\[([^\]]+)\]$/;
105 24 50       140 if( $rule =~ /$http_method_regex/ ) {
106 0         0 my $http_method = $1;
107             # go ahead to the next rule
108 0 0       0 next unless lc($1) eq lc(_http_method);
109             # remove the method portion from the rule
110 0         0 $rule =~ s/$http_method_regex//;
111             }
112              
113             # make sure they start and end with a '/' to match how PATH_INFO is formatted
114 24 100       256 $rule = "/$rule" unless ( index( $rule, '/' ) == 0 );
115 24 100       78 $rule = "$rule/" if ( substr( $rule, -1 ) ne '/' );
116              
117 24         36 my @names = ();
118              
119             # translate the rule into a regular expression, but remember where the named args are
120             # '/:foo' will become '/([^\/]*)'
121             # and
122             # '/:bar?' will become '/?([^\/]*)?'
123             # and then remember which position it matches
124              
125 24         88 $rule =~ s{
126             (^|/) # beginning or a /
127             (:([^/\?]+)(\?)?) # stuff in between
128             }{
129 17         75 push(@names, $3);
130 17 100       90 $1 . ($4 ? '?([^/]*)?' : '([^/]*)')
131             }gxe;
132              
133             # '/*/' will become '/(.*)/$' the end / is added to the end of
134             # both $rule and $path elsewhere
135 24 50       65 if($rule =~ m{/\*/$}) {
136 0         0 $rule =~ s{/\*/$}{/(.*)/\$};
137 0         0 push(@names,'dispatch_url_remainder');
138             }
139              
140 24 50       45 warn "[Dispatch] Trying to match '${path}' against rule '$table->[$i]' using regex '${rule}'\n"
141             if $DEBUG;
142              
143             # if we found a match, then run with it
144 24 100       640 if ( my @values = ( $path =~ m#^$rule$# ) ) {
145              
146 5 50       14 warn "[Dispatch] Matched!\n" if $DEBUG;
147              
148 5         6 my %named_args = %{ $table->[ ++$i ] };
  5         22  
149 5 100       24 @named_args{@names} = @values if @names;
150              
151             # Populate the Query parameters. Need a solution for
152             # mod_perl too
153 5         20 my $q = $self->query;
154 5         50 my $rm_key = $self->mode_param;
155 5         65 foreach my $param (%named_args) {
156 20 100       767 if ($param eq $rm_key) {
157 5         25 $self->prerun_mode($named_args{$param});
158             }
159 20         128 $q->param($param, $named_args{$param});
160             }
161             }
162             }
163              
164 6         65 return;
165             }
166              
167              
168             =head1 AUTHOR
169              
170             Dan Horne, C<< >>, largely based on code by
171             Michael Peters C<< >>
172              
173             =head1 BUGS
174              
175             Please report any bugs or feature requests to
176             C, or through the web interface at
177             L.
178             I will be notified, and then you'll automatically be notified of progress on
179             your bug as I make changes.
180              
181             =head1 SUPPORT
182              
183             You can find documentation for this module with the perldoc command.
184              
185             perldoc CGI::Application::Plugin::ParsePath
186              
187             You can also look for information at:
188              
189             =over 4
190              
191             =item * AnnoCPAN: Annotated CPAN documentation
192              
193             L
194              
195             =item * CPAN Ratings
196              
197             L
198              
199             =item * RT: CPAN's request tracker
200              
201             L
202              
203             =item * Search CPAN
204              
205             L
206              
207             =back
208              
209             =head1 ACKNOWLEDGEMENTS
210              
211             =head1 COPYRIGHT & LICENSE
212              
213             Copyright 2007 Michael Peters & Dan Horne, all rights reserved.
214              
215             This program is free software; you can redistribute it and/or modify it
216             under the same terms as Perl itself.
217              
218             =cut
219              
220             1; # End of CGI::Application::Plugin::ParsePath