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 14     14   2050 use strictures 1;
  14         4189  
  14         980  
4 14     14   1710 use Exporter 'import';
  14         28  
  14         28191  
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 349     349   2696 sub _matcher { bless shift, 'Web::Dispatch::Matcher' }
12              
13             sub match_true {
14 8     8 0 75 _matcher(sub { {} });
  4     4   546  
15             }
16              
17             sub match_false {
18 0     0 0 0 _matcher(sub {});
        0      
19             }
20              
21             sub match_and {
22 72     72 0 163 my @match = @_;
23             _matcher(sub {
24 105     105   446 my ($env) = @_;
25 105         797 my $my_env = { 'Web::Dispatch.original_env' => $env, %$env };
26 105         181 my $new_env;
27             my @got;
28 105         202 foreach my $match (@match) {
29 201 100       394 if (my @this_got = $match->($my_env)) {
30 161         179 my %change_env = %{shift(@this_got)};
  161         472  
31 161         279 @{$my_env}{keys %change_env} = values %change_env;
  161         288  
32 161         191 @{$new_env}{keys %change_env} = values %change_env;
  161         209  
33 161         498 push @got, @this_got;
34             } else {
35 40         299 return;
36             }
37             }
38 65         517 return ($new_env, @got);
39             })
40 72         384 }
41              
42             sub match_or {
43 4     4 0 15 my @match = @_;
44             _matcher(sub {
45 16     16   8650 foreach my $try (@match) {
46 30 100       95 if (my @ret = $try->(@_)) {
47 9         101 return @ret;
48             }
49             }
50 7         60 return;
51             })
52 4         36 }
53              
54             sub match_not {
55 1     1 0 3 my ($match) = @_;
56             _matcher(sub {
57 3 100   3   10 if (my @discard = $match->($_[0])) {
58 1         2 ();
59             } else {
60 2         8 ({});
61             }
62             })
63 1         8 }
64              
65             sub match_method {
66 64     64 0 153 my ($method) = @_;
67             _matcher(sub {
68 90     90   148 my ($env) = @_;
69 90 100       605 $env->{REQUEST_METHOD} eq $method ? {} : ()
70             })
71 64         359 }
72              
73             sub match_path {
74 98     98 0 186 my ($re, $names) = @_;
75             _matcher(sub {
76 182     182   289 my ($env) = @_;
77 182 100       1882 if (my @cap = ($env->{PATH_INFO} =~ /$re/)) {
78 129         229 $cap[0] = {};
79 129 100       343 $cap[1] = do { my %c; @c{@$names} = splice @cap, 1; \%c } if $names;
  4         10  
  4         147  
  4         26  
80 129         588 return @cap;
81             }
82 53         210 return;
83             })
84 98         658 }
85              
86             sub match_path_strip {
87 12     12 0 27 my ($re, $names) = @_;
88             _matcher(sub {
89 30     30   57 my ($env) = @_;
90 30 100       301 if (my @cap = ($env->{PATH_INFO} =~ /$re/)) {
91             $cap[0] = {
92 25   100     174 SCRIPT_NAME => ($env->{SCRIPT_NAME}||'').$cap[0],
93             PATH_INFO => pop(@cap),
94             };
95 25 50       67 $cap[1] = do { my %c; @c{@$names} = splice @cap, 1; \%c } if $names;
  0         0  
  0         0  
  0         0  
96 25         126 return @cap;
97             }
98 5         22 return;
99             })
100 12         87 }
101              
102             sub match_extension {
103 5     5 0 16 my ($extension) = @_;
104 5   66     39 my $wild = (!$extension or $extension eq '*');
105 5 100       99 my $re = $wild
106             ? qr/\.(\w+)$/
107             : qr/\.(\Q${extension}\E)$/;
108             _matcher(sub {
109 10 100   10   118 if ($_[0]->{PATH_INFO} =~ $re) {
110 5 100       40 ($wild ? ({}, $1) : {});
111             } else {
112 5         72 ();
113             }
114 5         36 });
115             }
116              
117             sub match_query {
118 82     82 0 161 _matcher(_param_matcher(query => $_[0]));
119             }
120              
121             sub match_body {
122 6     6 0 25 _matcher(_param_matcher(body => $_[0]));
123             }
124              
125             sub match_uploads {
126 1     1 0 6 _matcher(_param_matcher(uploads => $_[0]));
127             }
128              
129             sub _param_matcher {
130 89     89   124 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         111 my $saved_pos = pos;
136             {
137 89         74 local $_;
  89         99  
138 89         4650 require Web::Dispatch::ParamParser;
139             }
140 89         224 pos = $saved_pos;
141 89         532 my $unpack = Web::Dispatch::ParamParser->can("get_unpacked_${type}_from");
142             sub {
143 125     125   506 _extract_params($unpack->($_[0]), $spec)
144 89         529 };
145             }
146              
147             sub _extract_params {
148 125     125   168 my ($raw, $spec) = @_;
149 125 100       136 foreach my $name (@{$spec->{required}||[]}) {
  125         468  
150 115 100       414 return unless exists $raw->{$name};
151             }
152             my @ret = (
153             {},
154             map {
155 40 100 100     242 $_->{multi} ? $raw->{$_->{name}}||[] : $raw->{$_->{name}}->[-1]
156 88 100       157 } @{$spec->{positional}||[]}
  88         362  
157             );
158             # separated since 'or' is short circuit
159 88         170 my ($named, $star) = ($spec->{named}, $spec->{star});
160 88 100 100     314 if ($named or $star) {
161 57         66 my %kw;
162 57 100       125 if ($star) {
163             @kw{keys %$raw} = (
164             $star->{multi}
165 19 100       228 ? values %$raw
166             : map $_->[-1], values %$raw
167             );
168             }
169 57 100       84 foreach my $n (@{$named||[]}) {
  57         207  
170 55 100 66     208 next if !$n->{multi} and !exists $raw->{$n->{name}};
171             $kw{$n->{name}} =
172 51 100 100     267 $n->{multi} ? $raw->{$n->{name}}||[] : $raw->{$n->{name}}->[-1];
173             }
174 57         117 push @ret, \%kw;
175             }
176 88         429 @ret;
177             }
178              
179             1;