File Coverage

blib/lib/Router/Simple/Route.pm
Criterion Covered Total %
statement 76 76 100.0
branch 43 46 93.4
condition 11 13 84.6
subroutine 6 6 100.0
pod 0 2 0.0
total 136 143 95.1


line stmt bran cond sub pod time code
1             package Router::Simple::Route;
2 12     12   53 use strict;
  12         70  
  12         458  
3 12     12   84 use warnings;
  12         15  
  12         285  
4 12     12   44 use Carp ();
  12         15  
  12         426  
5              
6             use Class::Accessor::Lite 0.05 (
7 12         82 rw => [qw(name dest on_match method host pattern)],
8 12     12   6514 );
  12         12324  
9              
10             sub new {
11 41     41 0 49 my $class = shift;
12              
13             # connect([$name, ]$pattern[, \%dest[, \%opt]])
14 41 100 100     216 if (@_ == 1 || ref $_[1]) {
15 32         145 unshift(@_, undef);
16             }
17              
18 41         117 my ($name, $pattern, $dest, $opt) = @_;
19 41 50       82 Carp::croak("missing pattern") unless $pattern;
20 41         121 my $row = +{
21             name => $name,
22             dest => $dest,
23             on_match => $opt->{on_match},
24             };
25 41 100       99 if (my $method = $opt->{method}) {
26 22 50       59 $method = [$method] unless ref $method;
27 22         28 $row->{method} = $method;
28              
29 22         19 my $method_re = join '|', @{$method};
  22         49  
30 22         294 $row->{method_re} = qr{^(?:$method_re)$};
31             }
32 41 100       89 if (my $host = $opt->{host}) {
33 5         8 $row->{host} = $host;
34 5 50       51 $row->{host_re} = ref $host ? $host : qr(^\Q$host\E$);
35             }
36              
37 41         63 $row->{pattern} = $pattern;
38              
39             # compile pattern
40 41         41 my @capture;
41 41         35 $row->{pattern_re} = do {
42 41 100       71 if (ref $pattern) {
43 2         5 $row->{_regexp_capture} = 1;
44 2         5 $pattern;
45             } else {
46 39         262 $pattern =~ s!
47             \{((?:\{[0-9,]+\}|[^{}]+)+)\} | # /blog/{year:\d{4}}
48             :([A-Za-z0-9_]+) | # /blog/:year
49             (\*) | # /blog/*/*
50             ([^{:*]+) # normal string
51             !
52 103 100       311 if ($1) {
    100          
    100          
53 28         81 my ($name, $pattern) = split /:/, $1, 2;
54 28         42 push @capture, $name;
55 28 100       100 $pattern ? "($pattern)" : "([^/]+)";
56             } elsif ($2) {
57 7         17 push @capture, $2;
58 7         19 "([^/]+)";
59             } elsif ($3) {
60 4         5 push @capture, '__splat__';
61 4         8 "(.+)";
62             } else {
63 64         228 quotemeta($4);
64             }
65             !gex;
66             # for example, pattern '/comment/' will both match '/comment/' and '/comment'
67 39 100 66     143 $pattern .= '?' if $opt->{directory_slash} and $pattern =~ m{\/$};
68 39         678 qr{^$pattern$};
69             }
70             };
71 41         90 $row->{capture} = \@capture;
72 41   100     105 $row->{dest} ||= +{};
73              
74 41         178 return bless $row, $class;
75             }
76              
77             sub match {
78 123     123 0 138 my ($self, $env) = @_;
79              
80 123 100       282 if ($self->{host_re}) {
81 15 100       134 unless ($env->{HTTP_HOST} =~ $self->{host_re}) {
82 4         11 return undef;
83             }
84             }
85 119 100       755 if (my @captured = ($env->{PATH_INFO} =~ $self->{pattern_re})) {
86 44         42 my %args;
87             my @splat;
88 44 100       97 if ($self->{_regexp_capture}) {
89 1         4 push @splat, @captured;
90             } else {
91 43 100 100     43 if (@{$self->{capture}} > 0 && scalar(@{$self->{capture}}) != scalar(@captured)) {
  43         144  
  23         112  
92             # Should not contain parenthesis in regexp pattern
93             #
94             # Good: "/{date:(?:\d+)}"
95             # Bad: "/{date:(\d+)}"
96 1         222 Carp::carp("Path pattern should not contain paren. This code may not works in future version of Router::Simple. : " . $self->{pattern});
97             }
98              
99 43         161 for my $i (0..@{$self->{capture}}-1) {
  43         135  
100 47 100       106 if ($self->{capture}->[$i] eq '__splat__') {
101 4         7 push @splat, $captured[$i];
102             } else {
103 43         247 $args{$self->{capture}->[$i]} = $captured[$i];
104             }
105             }
106             }
107 44 100       112 if ($self->{method_re}) {
108 28 100 50     194 unless (($env->{REQUEST_METHOD} || '') =~ $self->{method_re}) {
109 8         12 $Router::Simple::_METHOD_NOT_ALLOWED = 1;
110 8         33 return undef;
111             }
112             }
113 36         206 my $match = +{
114 36 100       40 %{$self->{dest}},
115             %args,
116             ( @splat ? ( splat => \@splat ) : () ),
117             };
118 36 100       103 if ($self->{on_match}) {
119 3         10 my $ret = $self->{on_match}->($env, $match);
120 3 100       19 return undef unless $ret;
121             }
122 35         135 return $match;
123             }
124 75         145 return undef;
125             }
126              
127             1;
128             __END__