File Coverage

blib/lib/Web/Dispatch/Predicates.pm
Criterion Covered Total %
statement 96 100 96.0
branch 41 42 97.6
condition 13 15 86.6
subroutine 25 27 92.5
pod 0 12 0.0
total 175 196 89.2


line stmt bran cond sub pod time code
1             package Web::Dispatch::Predicates;
2              
3 16     16   1900 use strictures 1;
  16         3247  
  16         847  
4 16     16   1421 use Exporter 'import';
  16         32  
  16         22844  
5              
6             our @EXPORT = qw(
7             match_and match_or match_not match_method match_path match_path_strip
8             match_extension match_query match_body match_uploads match_true match_false
9             );
10              
11 350     350   2109 sub _matcher { bless shift, 'Web::Dispatch::Matcher' }
12              
13             sub match_true {
14 8     8 0 176 _matcher(sub { {} });
  4     4   648  
15             }
16              
17             sub match_false {
18 0     0 0 0 _matcher(sub {});
        0      
19             }
20              
21             sub match_and {
22 72     72 0 137 my @match = @_;
23             _matcher(sub {
24 105     105   386 my ($env) = @_;
25 105         611 my $my_env = { 'Web::Dispatch.original_env' => $env, %$env };
26 105         181 my $new_env;
27             my @got;
28 105         175 foreach my $match (@match) {
29 201 100       372 if (my @this_got = $match->($my_env)) {
30 161         165 my %change_env = %{shift(@this_got)};
  161         371  
31 161         249 @{$my_env}{keys %change_env} = values %change_env;
  161         249  
32 161         209 @{$new_env}{keys %change_env} = values %change_env;
  161         226  
33 161         463 push @got, @this_got;
34             } else {
35 40         227 return;
36             }
37             }
38 65         440 return ($new_env, @got);
39             })
40 72         359 }
41              
42             sub match_or {
43 4     4 0 6 my @match = @_;
44             _matcher(sub {
45 16     16   4701 foreach my $try (@match) {
46 30 100       55 if (my @ret = $try->(@_)) {
47 9         58 return @ret;
48             }
49             }
50 7         33 return;
51             })
52 4         55 }
53              
54             sub match_not {
55 1     1 0 2 my ($match) = @_;
56             _matcher(sub {
57 3 100   3   6 if (my @discard = $match->($_[0])) {
58 1         4 ();
59             } else {
60 2         6 ({});
61             }
62             })
63 1         5 }
64              
65             sub match_method {
66 65     65 0 161 my ($method) = @_;
67             _matcher(sub {
68 92     92   140 my ($env) = @_;
69 92 100       2039 $env->{REQUEST_METHOD} eq $method ? {} : ()
70             })
71 65         264 }
72              
73             sub match_path {
74 98     98 0 180 my ($re, $names) = @_;
75             _matcher(sub {
76 182     182   483 my ($env) = @_;
77 182 100       1234 if (my @cap = ($env->{PATH_INFO} =~ /$re/)) {
78 129         212 $cap[0] = {};
79 129 100       275 $cap[1] = do { my %c; @c{@$names} = splice @cap, 1; \%c } if $names;
  4         43  
  4         15  
  4         8  
80 129         490 return @cap;
81             }
82 53         184 return;
83             })
84 98         515 }
85              
86             sub match_path_strip {
87 12     12 0 20 my ($re, $names) = @_;
88             _matcher(sub {
89 30     30   51 my ($env) = @_;
90 30 100       221 if (my @cap = ($env->{PATH_INFO} =~ /$re/)) {
91             $cap[0] = {
92 25   100     155 SCRIPT_NAME => ($env->{SCRIPT_NAME}||'').$cap[0],
93             PATH_INFO => pop(@cap),
94             };
95 25 50       63 $cap[1] = do { my %c; @c{@$names} = splice @cap, 1; \%c } if $names;
  0         0  
  0         0  
  0         0  
96 25         127 return @cap;
97             }
98 5         21 return;
99             })
100 12         63 }
101              
102             sub match_extension {
103 5     5 0 10 my ($extension) = @_;
104 5   66     26 my $wild = (!$extension or $extension eq '*');
105 5 100       64 my $re = $wild
106             ? qr/\.(\w+)$/
107             : qr/\.(\Q${extension}\E)$/;
108             _matcher(sub {
109 10 100   10   61 if ($_[0]->{PATH_INFO} =~ $re) {
110 5 100       27 ($wild ? ({}, $1) : {});
111             } else {
112 5         18 ();
113             }
114 5         69 });
115             }
116              
117             sub match_query {
118 82     82 0 170 _matcher(_param_matcher(query => $_[0]));
119             }
120              
121             sub match_body {
122 6     6 0 23 _matcher(_param_matcher(body => $_[0]));
123             }
124              
125             sub match_uploads {
126 1     1 0 4 _matcher(_param_matcher(uploads => $_[0]));
127             }
128              
129             sub _param_matcher {
130 89     89   140 my ($type, $spec) = @_;
131             # We're probably parsing a match spec while building the parser, and
132             # on 5.8.8, loading ParamParser loads Encode which blows away $_ and pos.
133             # Furthermore, localizing $_ doesn't restore pos afterwards. So do this
134             # stupid thing instead to work on 5.8.8
135 89         131 my $saved_pos = pos;
136             {
137 89         102 local $_;
  89         108  
138 89         3997 require Web::Dispatch::ParamParser;
139             }
140 89         431 pos = $saved_pos;
141 89         508 my $unpack = Web::Dispatch::ParamParser->can("get_unpacked_${type}_from");
142             sub {
143 125     125   1669 _extract_params($unpack->($_[0]), $spec)
144 89         436 };
145             }
146              
147             sub _extract_params {
148 125     125   428 my ($raw, $spec) = @_;
149 125 100       146 foreach my $name (@{$spec->{required}||[]}) {
  125         685  
150 115 100       424 return unless exists $raw->{$name};
151             }
152             my @ret = (
153             {},
154             map {
155 40 100 100     261 $_->{multi} ? $raw->{$_->{name}}||[] : $raw->{$_->{name}}->[-1]
156 88 100       150 } @{$spec->{positional}||[]}
  88         365  
157             );
158             # separated since 'or' is short circuit
159 88         203 my ($named, $star) = ($spec->{named}, $spec->{star});
160 88 100 100     321 if ($named or $star) {
161 57         72 my %kw;
162 57 100       129 if ($star) {
163             @kw{keys %$raw} = (
164             $star->{multi}
165 19 100       245 ? values %$raw
166             : map $_->[-1], values %$raw
167             );
168             }
169 57 100       83 foreach my $n (@{$named||[]}) {
  57         209  
170 55 100 66     212 next if !$n->{multi} and !exists $raw->{$n->{name}};
171             $kw{$n->{name}} =
172 51 100 100     310 $n->{multi} ? $raw->{$n->{name}}||[] : $raw->{$n->{name}}->[-1];
173             }
174 57         117 push @ret, \%kw;
175             }
176 88         432 @ret;
177             }
178              
179             1;