File Coverage

blib/lib/CGI/Easy/URLconf.pm
Criterion Covered Total %
statement 143 148 96.6
branch 40 54 74.0
condition 12 16 75.0
subroutine 21 21 100.0
pod 6 6 100.0
total 222 245 90.6


line stmt bran cond sub pod time code
1             package CGI::Easy::URLconf;
2 3     3   173493 use 5.010001;
  3         23  
3 3     3   13 use warnings;
  3         4  
  3         57  
4 3     3   10 use strict;
  3         5  
  3         59  
5 3     3   1374 use utf8;
  3         33  
  3         12  
6 3     3   72 use Carp;
  3         4  
  3         179  
7              
8             our $VERSION = 'v2.0.1';
9              
10 3     3   16129 use Export::Attrs;
  3         29089  
  3         14  
11 3     3   1265 use URI::Escape qw( uri_escape_utf8 );
  3         3636  
  3         798  
12              
13              
14             my %PATH2VIEW;
15             my %VIEW2PATH;
16              
17              
18             sub setup_path :Export {
19 6     6 1 1296 my (@data) = @_;
20 6 100 100     31 my $method = ref $data[0] || $data[0] =~ m{\A/}xms ? q{} : shift @data;
21 6         16 for (my $i = 0; $i <= $#data; $i++) {
22 13         16 my $match = $data[$i];
23 13 100       21 if (ref $match) {
24 5 50       11 croak "expect SCALAR or Regexp at parameter $i" if ref $match ne 'Regexp';
25             } else {
26 8 50       17 croak "path at parameter $i must begin with /" if $match !~ m{\A/}xms;
27             }
28 13 50       28 croak 'not enough params' if $i == $#data;
29 13         23 my @code = ($data[++$i]);
30 13 50       22 croak "expect CODE at parameter $i" if ref $code[0] ne 'CODE';
31 13         25 while (ref $data[$i+1] eq 'CODE') {
32 3         6 push @code, $data[++$i];
33             }
34 13         15 my $view = pop @code;
35 13         13 push @{ $PATH2VIEW{$method} }, {
  13         46  
36             match => $match,
37             view => $view,
38             prepare => \@code,
39             };
40             }
41 6         12 return;
42 3     3   25 }
  3         4  
  3         13  
43              
44             sub path2view :Export {
45 17     17 1 1218 my ($r) = @_;
46 17         24 my $path = $r->{path};
47 17         17 my $view;
48 17   100     61 my $for_method = $PATH2VIEW{ $r->{ENV}{REQUEST_METHOD} } || [];
49 17   50     40 my $for_any = $PATH2VIEW{ q{} } || [];
50 17         28 for my $path2view (@{$for_method}, @{$for_any}) {
  17         23  
  17         33  
51 63         66 my @match;
52 63         73 my $match = $path2view->{match};
53 63 100       93 if (!ref $match) {
54 45 100       82 next if $path ne $match;
55             } else {
56 18 100       76 next if $path !~ /$match/xms;
57 5         13 for my $i (0 .. $#-) {
58 9 50       19 if (defined $-[$i]) {
59 9         38 push @match, substr $path, $-[$i], $+[$i] - $-[$i];
60             }
61             else {
62 0         0 push @match, undef;
63             }
64             }
65             }
66 13         23 for my $prepare (@{ $path2view->{prepare} }) {
  13         22  
67 3         7 $prepare->($r, \@match);
68             }
69 13         60 return $path2view->{view};
70             }
71 4         17 return;
72 3     3   1193 }
  3         5  
  3         10  
73              
74             sub set_param :Export {
75 3     3 1 21 my (@names) = @_;
76             return sub {
77 3     3   6 my ($r, $values) = @_;
78 3         6 for my $i (0 .. $#names) {
79 4 50       9 if (defined $values->[$i+1]) {
80 4 50       10 if (ref $r->{GET}{ $names[$i] }) {
81 0         0 $r->{GET}{ $names[$i] } = [ $values->[$i+1] ];
82             } else {
83 4         10 $r->{GET}{ $names[$i] } = $values->[$i+1];
84             }
85             }
86             else {
87 0         0 delete $r->{GET}{ $names[$i] };
88             }
89             }
90 3         6 return;
91 3         37 };
92 3     3   831 }
  3         6  
  3         9  
93              
94             ###
95              
96             sub setup_view :Export {
97 3     3 1 8 my (@data) = @_;
98 3         10 for (my $i = 0; $i <= $#data; $i++) {
99 4         5 my $view = $data[$i];
100 4 50       10 croak "expect CODE at parameter $i" if ref $view ne 'CODE';
101 4 50       9 croak "already exists CODE at parameter $i" if exists $VIEW2PATH{$view};
102 4 50       8 croak 'not enough params' if $i == $#data;
103 4         5 my $path = $data[++$i];
104 4 50 66     19 croak "expect SCALAR or ARRAY at parameter $i" if ref $path && ref $path ne 'ARRAY';
105 4 50 66     17 croak "expect even elements in parameter $i" if ref $path && @{$path} % 2;
  2         7  
106 4         13 $VIEW2PATH{$view} = $path;
107             }
108 3         6 return;
109 3     3   925 }
  3         5  
  3         16  
110              
111             sub view2path :Export {
112 13     13 1 4031 my ($view, %p) = @_;
113 13         28 my $path = $VIEW2PATH{$view};
114 13 100       28 if (!defined $path) {
115 4         9 my @path = grep { $_->{view} eq $view } map { @{$_} } values %PATH2VIEW;
  27         50  
  8         11  
  8         15  
116 4 100 66     20 if (@path == 1 && !ref $path[0]{match}) {
117 3         8 $path = $path[0]{match};
118             }
119             }
120 13 100       32 croak 'unknown CODE, use setup_view first' if !defined $path;
121 12 100       19 if (ref $path) {
122 7         9 my @try = @{$path};
  7         15  
123 7         11 $path = undef;
124 7         13 while (@try) {
125 10         12 my $try = shift @try;
126 10         12 my $tmpl= shift @try;
127 10 100       17 my $values = $try->(\%p) or next;
128 5 50       6 if (@{$values} != ($tmpl =~ tr/?//)) {
  5         12  
129 0         0 croak "incorrect values amount for template '$tmpl'";
130             }
131             # WARNING apache doesn't allow %2F in path (nginx allow)
132 5         8 for (@{$values}) {
  5         7  
133 7         16 $_ = uri_escape_utf8($_);
134 7         131 s/%2F/\//msg;
135             }
136 5         31 $tmpl =~ s/[?]/shift @{$values}/xmsge;
  7         10  
  7         25  
137 5         8 $path = $tmpl;
138 5         9 last;
139             }
140 7 100       33 croak 'these parameters do not match configured urls' if !defined $path;
141             }
142 10         11 my @params;
143 10         28 for my $n (keys %p) {
144 2 50       6 my @v = ref $p{$n} ? @{ $p{$n} } : $p{$n};
  0         0  
145 2         3 for my $v (@v) {
146 2         5 push @params, uri_escape_utf8($n).q{=}.uri_escape_utf8($v);
147             }
148             }
149 10 100       59 if (@params) {
150 2         5 $path .= q{?} . join q{&}, @params;
151             }
152 10         25 return $path;
153 3     3   1550 }
  3         4  
  3         9  
154              
155             sub with_params :Export {
156 3     3 1 7 my (@names) = @_;
157             return sub {
158 10     10   13 my ($p) = @_;
159 10         10 my @values;
160 10         15 for my $name (@names) {
161 12 100       30 return if !defined $p->{ $name };
162 7         14 push @values, $p->{ $name };
163             }
164 5         7 for (@names) {
165 7         20 delete $p->{$_};
166             }
167 5         12 return \@values;
168 3         13 };
169 3     3   990 }
  3         4  
  3         8  
170              
171              
172             1; # Magic true value required at end of module
173             __END__