line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package CGI::Easy::URLconf; |
2
|
|
|
|
|
|
|
|
3
|
4
|
|
|
4
|
|
98834
|
use warnings; |
|
4
|
|
|
|
|
10
|
|
|
4
|
|
|
|
|
126
|
|
4
|
4
|
|
|
4
|
|
22
|
use strict; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
121
|
|
5
|
4
|
|
|
4
|
|
22
|
use Carp; |
|
4
|
|
|
|
|
13
|
|
|
4
|
|
|
|
|
359
|
|
6
|
|
|
|
|
|
|
|
7
|
4
|
|
|
4
|
|
3633
|
use version; our $VERSION = qv('1.0.1'); # REMINDER: update Changes |
|
4
|
|
|
|
|
8942
|
|
|
4
|
|
|
|
|
33
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
# REMINDER: update dependencies in Makefile.PL |
10
|
4
|
|
|
4
|
|
4173
|
use Perl6::Export::Attrs; |
|
4
|
|
|
|
|
33779
|
|
|
4
|
|
|
|
|
31
|
|
11
|
4
|
|
|
4
|
|
4143
|
use URI::Escape qw( uri_escape_utf8 ); |
|
4
|
|
|
|
|
6503
|
|
|
4
|
|
|
|
|
1491
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
my %PATH2VIEW; |
15
|
|
|
|
|
|
|
my %VIEW2PATH; |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
sub setup_path :Export { |
19
|
6
|
|
|
6
|
1
|
1345
|
my (@data) = @_; |
20
|
6
|
100
|
100
|
|
|
51
|
my $method = ref $data[0] || $data[0] =~ m{\A/}xms ? q{} : shift @data; |
21
|
6
|
|
|
|
|
23
|
for (my $i = 0; $i <= $#data; $i++) { |
22
|
13
|
|
|
|
|
21
|
my $match = $data[$i]; |
23
|
13
|
100
|
|
|
|
28
|
if (ref $match) { |
24
|
5
|
50
|
|
|
|
21
|
croak "expect SCALAR or Regexp at parameter $i" if ref $match ne 'Regexp'; |
25
|
|
|
|
|
|
|
} else { |
26
|
8
|
50
|
|
|
|
33
|
croak "path at parameter $i must begin with /" if $match !~ m{\A/}xms; |
27
|
|
|
|
|
|
|
} |
28
|
13
|
50
|
|
|
|
34
|
croak 'not enough params' if $i == $#data; |
29
|
13
|
|
|
|
|
26
|
my @code = ($data[++$i]); |
30
|
13
|
50
|
|
|
|
32
|
croak "expect CODE at parameter $i" if ref $code[0] ne 'CODE'; |
31
|
13
|
|
|
|
|
55
|
while (ref $data[$i+1] eq 'CODE') { |
32
|
3
|
|
|
|
|
70
|
push @code, $data[++$i]; |
33
|
|
|
|
|
|
|
} |
34
|
13
|
|
|
|
|
18
|
my $view = pop @code; |
35
|
13
|
|
|
|
|
15
|
push @{ $PATH2VIEW{$method} }, { |
|
13
|
|
|
|
|
93
|
|
36
|
|
|
|
|
|
|
match => $match, |
37
|
|
|
|
|
|
|
view => $view, |
38
|
|
|
|
|
|
|
prepare => \@code, |
39
|
|
|
|
|
|
|
}; |
40
|
|
|
|
|
|
|
} |
41
|
6
|
|
|
|
|
19
|
return; |
42
|
4
|
|
|
4
|
|
31
|
} |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
49
|
|
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
sub path2view :Export { |
45
|
17
|
|
|
17
|
1
|
1761
|
my ($r) = @_; |
46
|
17
|
|
|
|
|
23
|
my $path = $r->{path}; |
47
|
17
|
|
|
|
|
18
|
my $view; |
48
|
17
|
|
100
|
|
|
86
|
my $for_method = $PATH2VIEW{ $r->{ENV}{REQUEST_METHOD} } || []; |
49
|
17
|
|
50
|
|
|
41
|
my $for_any = $PATH2VIEW{ q{} } || []; |
50
|
17
|
|
|
|
|
16
|
for my $path2view (@{$for_method}, @{$for_any}) { |
|
17
|
|
|
|
|
31
|
|
|
17
|
|
|
|
|
33
|
|
51
|
63
|
|
|
|
|
56
|
my @match; |
52
|
63
|
|
|
|
|
79
|
my $match = $path2view->{match}; |
53
|
63
|
100
|
|
|
|
96
|
if (!ref $match) { |
54
|
45
|
100
|
|
|
|
120
|
next if $path ne $match; |
55
|
|
|
|
|
|
|
} else { |
56
|
18
|
100
|
|
|
|
98
|
next if $path !~ /$match/xms; |
57
|
5
|
|
|
|
|
22
|
for my $i (0 .. $#-) { |
58
|
9
|
50
|
|
|
|
27
|
if (defined $-[$i]) { |
59
|
9
|
|
|
|
|
50
|
push @match, substr $path, $-[$i], $+[$i] - $-[$i]; |
60
|
|
|
|
|
|
|
} |
61
|
|
|
|
|
|
|
else { |
62
|
0
|
|
|
|
|
0
|
push @match, undef; |
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
} |
66
|
13
|
|
|
|
|
17
|
for my $prepare (@{ $path2view->{prepare} }) { |
|
13
|
|
|
|
|
25
|
|
67
|
3
|
|
|
|
|
11
|
$prepare->($r, \@match); |
68
|
|
|
|
|
|
|
} |
69
|
13
|
|
|
|
|
80
|
return $path2view->{view}; |
70
|
|
|
|
|
|
|
} |
71
|
4
|
|
|
|
|
25
|
return; |
72
|
4
|
|
|
4
|
|
2192
|
} |
|
4
|
|
|
|
|
10
|
|
|
4
|
|
|
|
|
21
|
|
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
sub set_param :Export { |
75
|
3
|
|
|
3
|
1
|
24
|
my (@names) = @_; |
76
|
|
|
|
|
|
|
return sub { |
77
|
3
|
|
|
3
|
|
8
|
my ($r, $values) = @_; |
78
|
3
|
|
|
|
|
9
|
for my $i (0 .. $#names) { |
79
|
4
|
50
|
|
|
|
13
|
if (defined $values->[$i+1]) { |
80
|
4
|
50
|
|
|
|
16
|
if (ref $r->{GET}{ $names[$i] }) { |
81
|
0
|
|
|
|
|
0
|
$r->{GET}{ $names[$i] } = [ $values->[$i+1] ]; |
82
|
|
|
|
|
|
|
} else { |
83
|
4
|
|
|
|
|
15
|
$r->{GET}{ $names[$i] } = $values->[$i+1]; |
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
else { |
87
|
0
|
|
|
|
|
0
|
delete $r->{GET}{ $names[$i] }; |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
} |
90
|
3
|
|
|
|
|
10
|
return; |
91
|
3
|
|
|
|
|
27
|
}; |
92
|
4
|
|
|
4
|
|
1444
|
} |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
19
|
|
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
### |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
sub setup_view :Export { |
97
|
3
|
|
|
3
|
1
|
12
|
my (@data) = @_; |
98
|
3
|
|
|
|
|
12
|
for (my $i = 0; $i <= $#data; $i++) { |
99
|
4
|
|
|
|
|
8
|
my $view = $data[$i]; |
100
|
4
|
50
|
|
|
|
26
|
croak "expect CODE at parameter $i" if ref $view ne 'CODE'; |
101
|
4
|
50
|
|
|
|
12
|
croak "already exists CODE at parameter $i" if exists $VIEW2PATH{$view}; |
102
|
4
|
50
|
|
|
|
37
|
croak 'not enough params' if $i == $#data; |
103
|
4
|
|
|
|
|
9
|
my $path = $data[++$i]; |
104
|
4
|
50
|
66
|
|
|
22
|
croak "expect SCALAR or ARRAY at parameter $i" if ref $path && ref $path ne 'ARRAY'; |
105
|
4
|
50
|
66
|
|
|
13
|
croak "expect even elements in parameter $i" if ref $path && @{$path} % 2; |
|
2
|
|
|
|
|
12
|
|
106
|
4
|
|
|
|
|
19
|
$VIEW2PATH{$view} = $path; |
107
|
|
|
|
|
|
|
} |
108
|
3
|
|
|
|
|
8
|
return; |
109
|
4
|
|
|
4
|
|
1722
|
} |
|
4
|
|
|
|
|
10
|
|
|
4
|
|
|
|
|
20
|
|
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
sub view2path :Export { |
112
|
13
|
|
|
13
|
1
|
4548
|
my ($view, %p) = @_; |
113
|
13
|
|
|
|
|
28
|
my $path = $VIEW2PATH{$view}; |
114
|
13
|
100
|
|
|
|
34
|
if (!defined $path) { |
115
|
4
|
|
|
|
|
11
|
my @path = grep { $_->{view} eq $view } map { @{$_} } values %PATH2VIEW; |
|
27
|
|
|
|
|
69
|
|
|
8
|
|
|
|
|
10
|
|
|
8
|
|
|
|
|
20
|
|
116
|
4
|
100
|
66
|
|
|
30
|
if (@path == 1 && !ref $path[0]{match}) { |
117
|
3
|
|
|
|
|
8
|
$path = $path[0]{match}; |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
} |
120
|
13
|
100
|
|
|
|
38
|
croak 'unknown CODE, use setup_view first' if !defined $path; |
121
|
12
|
100
|
|
|
|
28
|
if (ref $path) { |
122
|
7
|
|
|
|
|
8
|
my @try = @{$path}; |
|
7
|
|
|
|
|
19
|
|
123
|
7
|
|
|
|
|
12
|
$path = undef; |
124
|
7
|
|
|
|
|
20
|
while (@try) { |
125
|
10
|
|
|
|
|
17
|
my $try = shift @try; |
126
|
10
|
|
|
|
|
17
|
my $tmpl= shift @try; |
127
|
10
|
100
|
|
|
|
26
|
my $values = $try->(\%p) or next; |
128
|
5
|
50
|
|
|
|
10
|
if (@{$values} != ($tmpl =~ tr/?//)) { |
|
5
|
|
|
|
|
18
|
|
129
|
0
|
|
|
|
|
0
|
croak "incorrect values amount for template '$tmpl'"; |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
# WARNING apache не ÑазÑеÑÐ°ÐµÑ %2F в пÑÑи (nginx ÑазÑеÑаеÑ) |
132
|
5
|
|
|
|
|
10
|
$_ = uri_escape_utf8($_), s/%2F/\//g for @{$values}; ## no critic |
|
5
|
|
|
|
|
26
|
|
133
|
5
|
|
|
|
|
180
|
$tmpl =~ s/[?]/shift @{$values}/xmsge; |
|
7
|
|
|
|
|
10
|
|
|
7
|
|
|
|
|
18
|
|
134
|
5
|
|
|
|
|
11
|
$path = $tmpl; |
135
|
5
|
|
|
|
|
11
|
last; |
136
|
|
|
|
|
|
|
} |
137
|
7
|
100
|
|
|
|
59
|
croak 'these parameters do not match configured urls' if !defined $path; |
138
|
|
|
|
|
|
|
} |
139
|
10
|
|
|
|
|
14
|
my @params; |
140
|
10
|
|
|
|
|
27
|
for my $n (keys %p) { |
141
|
2
|
50
|
|
|
|
11
|
my @v = ref $p{$n} ? @{ $p{$n} } : $p{$n}; |
|
0
|
|
|
|
|
0
|
|
142
|
2
|
|
|
|
|
4
|
for my $v (@v) { |
143
|
2
|
|
|
|
|
6
|
push @params, uri_escape_utf8($n).q{=}.uri_escape_utf8($v); |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
} |
146
|
10
|
100
|
|
|
|
73
|
if (@params) { |
147
|
2
|
|
|
|
|
8
|
$path .= q{?} . join q{&}, @params; |
148
|
|
|
|
|
|
|
} |
149
|
10
|
|
|
|
|
35
|
return $path; |
150
|
4
|
|
|
4
|
|
2772
|
} |
|
4
|
|
|
|
|
9
|
|
|
4
|
|
|
|
|
19
|
|
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
sub with_params :Export { |
153
|
3
|
|
|
3
|
1
|
7
|
my (@names) = @_; |
154
|
|
|
|
|
|
|
return sub { |
155
|
10
|
|
|
10
|
|
14
|
my ($p) = @_; |
156
|
10
|
|
|
|
|
12
|
my @values; |
157
|
10
|
|
|
|
|
17
|
for my $name (@names) { |
158
|
12
|
100
|
|
|
|
48
|
return if !defined $p->{ $name }; |
159
|
7
|
|
|
|
|
20
|
push @values, $p->{ $name }; |
160
|
|
|
|
|
|
|
} |
161
|
5
|
|
|
|
|
19
|
delete $p->{$_} for @names; ## no critic |
162
|
5
|
|
|
|
|
18
|
return \@values; |
163
|
3
|
|
|
|
|
24
|
}; |
164
|
4
|
|
|
4
|
|
1282
|
} |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
16
|
|
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
1; # Magic true value required at end of module |
168
|
|
|
|
|
|
|
__END__ |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
=encoding utf8 |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
=head1 NAME |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
CGI::Easy::URLconf - map url path to handler sub and vice versa |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
=head1 SYNOPSIS |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
use CGI::Easy::URLconf qw( setup_path path2view set_param ); |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
setup_path( |
182
|
|
|
|
|
|
|
'/about/' => \&myabout, |
183
|
|
|
|
|
|
|
'/terms.php' => \&terms, |
184
|
|
|
|
|
|
|
qr{\A /articles/ \z}xms => \&list_all_articles, |
185
|
|
|
|
|
|
|
); |
186
|
|
|
|
|
|
|
setup_path( |
187
|
|
|
|
|
|
|
qr{\A /articles/(\d+)/ \z}xms |
188
|
|
|
|
|
|
|
=> set_param('year') |
189
|
|
|
|
|
|
|
=> \&list_articles, |
190
|
|
|
|
|
|
|
qr{\A /articles/tag/(\w+)/(\d+)/ \z}xms |
191
|
|
|
|
|
|
|
=> set_param('tag','year') |
192
|
|
|
|
|
|
|
=> \&list_articles, |
193
|
|
|
|
|
|
|
); |
194
|
|
|
|
|
|
|
setup_path( POST => |
195
|
|
|
|
|
|
|
'/articles/' => \&add_article, |
196
|
|
|
|
|
|
|
); |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
my $r = CGI::Easy::Request->new(); |
199
|
|
|
|
|
|
|
my $handler = path2view($r); |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
use CGI::Easy::URLconf qw( setup_view view2path with_params ); |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
setup_view( |
205
|
|
|
|
|
|
|
\&list_all_articles => '/articles/', |
206
|
|
|
|
|
|
|
\&list_articles => [ |
207
|
|
|
|
|
|
|
with_params('tag','year') => '/articles/tag/?/?/', |
208
|
|
|
|
|
|
|
with_params('year') => '/articles/?/', |
209
|
|
|
|
|
|
|
], |
210
|
|
|
|
|
|
|
); |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
# set $url to '/about/' |
213
|
|
|
|
|
|
|
my $url = view2path( \&myabout ); |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
# set $url to '/articles/' |
216
|
|
|
|
|
|
|
my $url = view2path( \&list_all_articles ); |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
# set $url to '/articles/2010/?month=12' |
219
|
|
|
|
|
|
|
my $url = view2path( \&list_articles, year=>2010, month=>12 ); |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
=head1 DESCRIPTION |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
This module provide support for clean, user-friendly URLs. This can be |
225
|
|
|
|
|
|
|
archived by configuring web server to run your CGI/FastCGI script for any |
226
|
|
|
|
|
|
|
url requested by user, and let you manually dispatch different urls to |
227
|
|
|
|
|
|
|
corresponding handlers (subroutines). Additionally, you can take some |
228
|
|
|
|
|
|
|
CGI parameters from url's path instead of usual GET parameters. |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
The idea is to set rules when CGI/FastCGI starts using: |
231
|
|
|
|
|
|
|
a) setup_path() - to map url's path to handler subroutine |
232
|
|
|
|
|
|
|
(also called "view") |
233
|
|
|
|
|
|
|
b) setup_view() - to map handler subroutine to url |
234
|
|
|
|
|
|
|
and then use: |
235
|
|
|
|
|
|
|
a) path2view() - to get handler subroutine matching current url's path |
236
|
|
|
|
|
|
|
b) view2path() - to get url matching some handler subroutine |
237
|
|
|
|
|
|
|
(for inserting into HTML templates or sending redirects). |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
Example: |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
# -- while CGI/FastCGI initialization |
242
|
|
|
|
|
|
|
setup_path( |
243
|
|
|
|
|
|
|
'/articles/' => \&list_articles, |
244
|
|
|
|
|
|
|
'/articles.php' => \&list_articles, |
245
|
|
|
|
|
|
|
'/index.php' => \&show_home_page, |
246
|
|
|
|
|
|
|
); |
247
|
|
|
|
|
|
|
setup_path( POST => |
248
|
|
|
|
|
|
|
'/articles/' => \&add_new_article, |
249
|
|
|
|
|
|
|
); |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
# -- when beginning to handle new CGI/FastCGI request |
252
|
|
|
|
|
|
|
my $r = CGI::Easy::Request->new(); |
253
|
|
|
|
|
|
|
my $handler = path2view($r); |
254
|
|
|
|
|
|
|
# $handler now set to: |
255
|
|
|
|
|
|
|
# \&list_articles if url path /articles/ and request method is GET |
256
|
|
|
|
|
|
|
# \&add_new_article if url path /articles/ and request method is POST |
257
|
|
|
|
|
|
|
# \&list_articles if url path /articles.php (any request method) |
258
|
|
|
|
|
|
|
# \&show_home_page if url path /index.php (any request method) |
259
|
|
|
|
|
|
|
# undef (in all other cases) |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
# -- while CGI/FastCGI initialization |
262
|
|
|
|
|
|
|
setup_view( |
263
|
|
|
|
|
|
|
\&list_articles => '/articles/', |
264
|
|
|
|
|
|
|
# we don't have to configure mapping for \&show_home_page |
265
|
|
|
|
|
|
|
# and \&add_new_article because their mappings can be |
266
|
|
|
|
|
|
|
# unambiguously automatically detected from above setup_path() |
267
|
|
|
|
|
|
|
); |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
# -- when preparing reply (HTML escaping omitted for simplicity) |
270
|
|
|
|
|
|
|
printf '<a href="%s">Articles</a>', view2path(\&list_articles); |
271
|
|
|
|
|
|
|
printf '<form method=POST action="%s">', view2path(\&add_new_article); |
272
|
|
|
|
|
|
|
# -- or redirecting to another url |
273
|
|
|
|
|
|
|
my $h = CGI::Easy::Headers->new(); |
274
|
|
|
|
|
|
|
$h->redirect(view2path(\&show_home_page)); |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
These two parts (setup_path() with path2view() and setup_view() with view2path()) |
277
|
|
|
|
|
|
|
can be used independently - for example, you don't have to use |
278
|
|
|
|
|
|
|
setup_view() and view2path() if you prefer to hardcode urls in HTML templates |
279
|
|
|
|
|
|
|
instead of generating them dynamically. But using both parts will let you |
280
|
|
|
|
|
|
|
configure I<all> urls used in your application in single place, which make |
281
|
|
|
|
|
|
|
it easier to control and modify them. |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
In addition to simple constant path to handler and vice versa mapping you |
284
|
|
|
|
|
|
|
can also map any path matching regular expression and even copy some data |
285
|
|
|
|
|
|
|
from path to GET parameters. Example: |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
# make /article/123/ same as /index.php?id=123 |
288
|
|
|
|
|
|
|
# use same handler for any url beginning with /old/ |
289
|
|
|
|
|
|
|
setup_path( |
290
|
|
|
|
|
|
|
'/article.php' => \&show_article, |
291
|
|
|
|
|
|
|
qr{^/article/(\d+)/$} => set_param('id') => \&show_article, |
292
|
|
|
|
|
|
|
qr{^/old/} => \&unsupported, |
293
|
|
|
|
|
|
|
); |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
# generate urls like /article/123/ dynamically |
296
|
|
|
|
|
|
|
setup_view( |
297
|
|
|
|
|
|
|
\&show_article => [ |
298
|
|
|
|
|
|
|
with_params('id') => '/article/?/', |
299
|
|
|
|
|
|
|
], |
300
|
|
|
|
|
|
|
); |
301
|
|
|
|
|
|
|
$url = view2path(\&show_article, id=>123); |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
=head1 INTERFACE |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
=over |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
=item setup_path( [METHOD =>] MATCH => [CALLBACK => ...] HANDLER, ... ) |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
Configure mapping of url's path to handler subroutine (which will be used |
311
|
|
|
|
|
|
|
by path2view()). |
312
|
|
|
|
|
|
|
Can be called multiple times and will just B<add> new mapping rules on each call. |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
If optional METHOD parameter defined, then all mapping rules in this |
315
|
|
|
|
|
|
|
setup_path() call will be applied only for requests with that HTTP method. |
316
|
|
|
|
|
|
|
If METHOD doesn't used, then these rules will be applied to all HTTP methods. |
317
|
|
|
|
|
|
|
If some path match both rules defined for current HTTP method and rules |
318
|
|
|
|
|
|
|
defined for any HTTP methods, will be used rule defined for current HTTP |
319
|
|
|
|
|
|
|
method. |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
MATCH parameter should be either SCALAR (string equal to url path) or |
322
|
|
|
|
|
|
|
Regexp (which can match any part of url path). |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
HANDLER is REF to your subroutine, which will be returned by path2view() |
325
|
|
|
|
|
|
|
when this rule will match current url. |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
Between MATCH and HANDLER any amount of optional CALLBACK subroutines can |
328
|
|
|
|
|
|
|
be used. These CALLBACKs will be called when MATCH rule matches current |
329
|
|
|
|
|
|
|
url with two parameters: CGI::Easy::Request object and ARRAYREF with |
330
|
|
|
|
|
|
|
contents of all capturing parentheses (when MATCH rule is Regexp with |
331
|
|
|
|
|
|
|
capturing parentheses). Usual task for such CALLBACKs is convert "hidden" |
332
|
|
|
|
|
|
|
CGI parameters included in url path into usual C<< $r->{GET} >> parameters. |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
Return nothing. |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
=item path2view( $r ) |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
Take CGI::Easy::Request object as parameter, and analyse this request |
340
|
|
|
|
|
|
|
according to rules defined previously using setup_path(). |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
Return: HANDLER if find rule which match current request, else undef(). |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
=item set_param( @names ) |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
Take names of C<< {GET} >> parameters which should be set using parts of |
348
|
|
|
|
|
|
|
url path selected by capturing parentheses in MATCH Regexp. |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
Return CALLBACK subroutine suitable for using in setup_path(). |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
=item setup_view( HANDLER => PATH, ... ) |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
Configure mapping of handler subroutine to url path (which will be used by |
356
|
|
|
|
|
|
|
view2path()). |
357
|
|
|
|
|
|
|
Can be called multiple times and will just B<add> new mapping rules on each call. |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
HANDLER must be REF to user's subroutine used to handle requests on PATH. |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
PATH can be either STRING or ARRAYREF. |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
If PATH is ARRAYREF, then this array should consist of CALLBACK => |
364
|
|
|
|
|
|
|
TEMPLATE pairs. CALLBACK is subroutine which will be executed by |
365
|
|
|
|
|
|
|
view2path() with single parameter C<< \%params >>, and should return |
366
|
|
|
|
|
|
|
either FALSE if this CALLBACK unable to handle these %params, or ARRAYREF |
367
|
|
|
|
|
|
|
with values to substitute into path TEMPLATE. TEMPLATE is path STRING |
368
|
|
|
|
|
|
|
which may contain '?' symbols - these will be replaced by values returned |
369
|
|
|
|
|
|
|
in ARRAYREF by CALLBACK which successfully handle %params. |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
Example: map \&handler to /first/ or /second/ with 50% probability |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
setup_view( |
374
|
|
|
|
|
|
|
\&handler => [ |
375
|
|
|
|
|
|
|
sub { return rand < 0.5 ? [] : undef } => '/first/', |
376
|
|
|
|
|
|
|
sub { return [] } => '/second/', |
377
|
|
|
|
|
|
|
], |
378
|
|
|
|
|
|
|
); |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
Example: map \&handler to random article with id 0-999 |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
setup_view( |
383
|
|
|
|
|
|
|
\&handler => [ |
384
|
|
|
|
|
|
|
sub { return [ int rand 1000 ] } => '/article/?/', |
385
|
|
|
|
|
|
|
], |
386
|
|
|
|
|
|
|
); |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
Return nothing. |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
=item view2path( HANDLER, %params ) |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
Take user handler subroutine and it parameters, and convert it to url |
394
|
|
|
|
|
|
|
according to rules defined previously using setup_view(). |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
Example: |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
setup_view( |
399
|
|
|
|
|
|
|
\&handler => 'index.php', |
400
|
|
|
|
|
|
|
); |
401
|
|
|
|
|
|
|
my $url = view2path(\&handler, a=>'some string', b=>[6,7]); |
402
|
|
|
|
|
|
|
# $url will be: 'index.php?a=some%20string&b=6&b=7' |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
If simple mapping from STRING to HANDLER was defined using setup_path(), |
405
|
|
|
|
|
|
|
and this is only mapping to HANDLER defined, then it's not necessary to |
406
|
|
|
|
|
|
|
define reverse mapping using setup_view() - it will be defined |
407
|
|
|
|
|
|
|
automatically. |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
Example: |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
setup_path( |
412
|
|
|
|
|
|
|
'/articles/' => \&list_articles, |
413
|
|
|
|
|
|
|
); |
414
|
|
|
|
|
|
|
my $url = view2path(\&list_articles); |
415
|
|
|
|
|
|
|
# $url will be: '/articles/' |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
Return: url. Throw exception if unable to make url. |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
=item with_params( @names ) |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
Take names of parameters which B<must> exists in %params given to |
423
|
|
|
|
|
|
|
view2path(). |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
Return CALLBACK subroutine suitable for using in setup_view(). |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
=back |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
=head1 BUGS AND LIMITATIONS |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
No bugs have been reported. |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
=head1 SUPPORT |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
Please report any bugs or feature requests through the web interface at |
439
|
|
|
|
|
|
|
L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=CGI-Easy-URLconf>. |
440
|
|
|
|
|
|
|
I will be notified, and then you'll automatically be notified of progress |
441
|
|
|
|
|
|
|
on your bug as I make changes. |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
You can also look for information at: |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
=over |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=CGI-Easy-URLconf> |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
L<http://annocpan.org/dist/CGI-Easy-URLconf> |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
=item * CPAN Ratings |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
L<http://cpanratings.perl.org/d/CGI-Easy-URLconf> |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
=item * Search CPAN |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
L<http://search.cpan.org/dist/CGI-Easy-URLconf/> |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
=back |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
=head1 AUTHOR |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
Alex Efros C<< <powerman-asdf@ya.ru> >> |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
Copyright 2009-2010 Alex Efros <powerman-asdf@ya.ru>. |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
This program is distributed under the MIT (X11) License: |
476
|
|
|
|
|
|
|
L<http://www.opensource.org/licenses/mit-license.php> |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person |
479
|
|
|
|
|
|
|
obtaining a copy of this software and associated documentation |
480
|
|
|
|
|
|
|
files (the "Software"), to deal in the Software without |
481
|
|
|
|
|
|
|
restriction, including without limitation the rights to use, |
482
|
|
|
|
|
|
|
copy, modify, merge, publish, distribute, sublicense, and/or sell |
483
|
|
|
|
|
|
|
copies of the Software, and to permit persons to whom the |
484
|
|
|
|
|
|
|
Software is furnished to do so, subject to the following |
485
|
|
|
|
|
|
|
conditions: |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be |
488
|
|
|
|
|
|
|
included in all copies or substantial portions of the Software. |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
491
|
|
|
|
|
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
492
|
|
|
|
|
|
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
493
|
|
|
|
|
|
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
494
|
|
|
|
|
|
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
495
|
|
|
|
|
|
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
496
|
|
|
|
|
|
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
497
|
|
|
|
|
|
|
OTHER DEALINGS IN THE SOFTWARE. |
498
|
|
|
|
|
|
|
|