File Coverage

blib/lib/Path/Dispatcher/Rule/Tokens.pm
Criterion Covered Total %
statement 55 69 79.7
branch 15 30 50.0
condition 13 15 86.6
subroutine 9 10 90.0
pod 0 3 0.0
total 92 127 72.4


line stmt bran cond sub pod time code
1             package Path::Dispatcher::Rule::Tokens;
2 32     32   18611 use Any::Moose;
  32         23026  
  32         165  
3             extends 'Path::Dispatcher::Rule';
4              
5             has tokens => (
6             is => 'ro',
7             isa => 'ArrayRef',
8             auto_deref => 1,
9             required => 1,
10             );
11              
12             has delimiter => (
13             is => 'ro',
14             isa => 'Str',
15             default => ' ',
16             );
17              
18             has case_sensitive => (
19             is => 'ro',
20             isa => 'Bool',
21             default => 1,
22             );
23              
24             sub _match_as_far_as_possible {
25 84     84   68 my $self = shift;
26 84         81 my $path = shift;
27              
28 84         176 my @got = $self->tokenize($path->path);
29 84         194 my @expected = $self->tokens;
30 84         84 my @matched;
31              
32 84   100     292 while (@got && @expected) {
33 102         95 my $expected = $expected[0];
34 102         92 my $got = $got[0];
35              
36 102 100       151 last unless $self->_match_token($got, $expected);
37              
38 62         76 push @matched, $got;
39 62         52 shift @expected;
40 62         177 shift @got;
41             }
42              
43 84         178 return (\@matched, \@got, \@expected);
44             }
45              
46             sub _match {
47 84     84   75 my $self = shift;
48 84         75 my $path = shift;
49              
50 84         132 my ($matched, $got, $expected) = $self->_match_as_far_as_possible($path);
51              
52 84 100       223 return if @$expected; # didn't provide everything necessary
53 43 100 100     154 return if @$got && !$self->prefix; # had tokens left over
54              
55 39         77 my $leftover = $self->untokenize(@$got);
56              
57 39 50       73 return if !$matched;
58              
59             return {
60 39         159 positional_captures => $matched,
61             leftover => $leftover,
62             };
63             }
64              
65             sub complete {
66 0     0 0 0 my $self = shift;
67 0         0 my $path = shift;
68              
69 0         0 my ($matched, $got, $expected) = $self->_match_as_far_as_possible($path);
70 0 0       0 return if @$got > 1; # had tokens leftover
71 0 0       0 return if !@$expected; # consumed all tokens
72              
73 0         0 my $next = shift @$expected;
74 0 0       0 my $part = @$got ? shift @$got : '';
75 0         0 my @completions;
76              
77 0 0       0 for my $completion (ref($next) eq 'ARRAY' ? @$next : $next) {
78 0 0       0 next if ref($completion);
79              
80 0 0       0 next unless substr($completion, 0, length($part)) eq $part;
81 0         0 push @completions, $self->untokenize(@$matched, $completion);
82             }
83              
84 0         0 return @completions;
85             }
86              
87             sub _each_token {
88 118     118   94 my $self = shift;
89 118         95 my $got = shift;
90 118         87 my $expected = shift;
91 118         85 my $callback = shift;
92              
93 118 100 66     345 if (ref($expected) eq 'ARRAY') {
    50          
94 11         12 for my $alternative (@$expected) {
95 16         27 $self->_each_token($got, $alternative, $callback);
96             }
97             }
98             elsif (!ref($expected) || ref($expected) eq 'Regexp') {
99 107         159 $callback->($got, $expected);
100             }
101             else {
102 0         0 die "Unexpected token '$expected'"; # the irony is not lost on me :)
103             }
104             }
105              
106             sub _match_token {
107 102     102   79 my $self = shift;
108 102         94 my $got = shift;
109 102         82 my $expected = shift;
110              
111 102         72 my $matched = 0;
112             $self->_each_token($got, $expected, sub {
113 107     107   127 my ($g, $e) = @_;
114 107 100       145 if (!ref($e)) {
    50          
115 102 100       215 ($g, $e) = (lc $g, lc $e) if !$self->case_sensitive;
116 102   100     334 $matched ||= $g eq $e;
117             }
118             elsif (ref($e) eq 'Regexp') {
119 5   66     34 $matched ||= $g =~ $e;
120             }
121 102         376 });
122              
123 102         523 return $matched;
124             }
125              
126             sub tokenize {
127 84     84 0 69 my $self = shift;
128 84         72 my $path = shift;
129 84         416 return grep { length } split $self->delimiter, $path;
  171         295  
130             }
131              
132             sub untokenize {
133 39     39 0 37 my $self = shift;
134 39         51 my @tokens = @_;
135 24         56 return join $self->delimiter,
136 24         95 grep { length }
137 39         97 map { split $self->delimiter, $_ }
138             @tokens;
139             }
140              
141             __PACKAGE__->meta->make_immutable;
142 32     32   31628 no Any::Moose;
  32         56  
  32         127  
143              
144             1;
145              
146             __END__