File Coverage

blib/lib/Path/Dispatcher/Rule/Under.pm
Criterion Covered Total %
statement 25 38 65.7
branch 3 8 37.5
condition 3 3 100.0
subroutine 6 7 85.7
pod 1 2 50.0
total 38 58 65.5


line stmt bran cond sub pod time code
1             package Path::Dispatcher::Rule::Under;
2             # ABSTRACT: rules under a predicate
3              
4             our $VERSION = '1.08';
5              
6 31     31   224 use Moo;
  31         89  
  31         206  
7 31     31   11094 use MooX::TypeTiny;
  31         77  
  31         178  
8 31     31   22121 use Type::Tiny;
  31         77  
  31         850  
9 31     31   230 use Type::Utils qw(class_type);
  31         89  
  31         262  
10              
11             extends 'Path::Dispatcher::Rule';
12             with 'Path::Dispatcher::Role::Rules';
13              
14             my $PREFIX_RULE_TYPE = "Type::Tiny"->new(
15             name => "PrefixRule",
16             parent => class_type("Path::Dispatcher::Rule"),
17             constraint => sub { return ( shift()->prefix ) ? 1 : 0 },
18             message => sub { "This rule ($_) does not match just prefixes!" },
19             );
20              
21             has predicate => (
22             is => 'ro',
23             isa => $PREFIX_RULE_TYPE
24             );
25              
26             sub match {
27 21     21 1 566 my $self = shift;
28 21         31 my $path = shift;
29              
30 21 100       114 my $prefix_match = $self->predicate->match($path)
31             or return;
32              
33 16         52 my $leftover = $prefix_match->leftover;
34 16 50       43 $leftover = '' if !defined($leftover);
35              
36 16         55 my $new_path = $path->clone_path($leftover);
37              
38             # Pop off @matches until we have a last rule that is not ::Chain
39             #
40             # A better technique than isa might be to use the concept of 'endpoint', 'midpoint', or 'anypoint' rules and
41             # add a method to ::Rule that lets evaluate whether any rule is of the right kind (i.e. ->is_endpoint)
42             #
43             # Because the checking for ::Chain endpointedness is here, this means that outside of an ::Under, ::Chain behaves like
44             # an ::Always (one that will always trigger next_rule if it's block is ran)
45             #
46             my @matches = map {
47 16         631 $_->match(
  40         151  
48             $new_path,
49             extra_constructor_args => {
50             parent => $prefix_match,
51             },
52             )
53             } $self->rules;
54 16   100     147 pop @matches while @matches && $matches[-1]->rule->isa('Path::Dispatcher::Rule::Chain');
55 16         97 return @matches;
56             }
57              
58             sub complete {
59 0     0 0   my $self = shift;
60 0           my $path = shift;
61              
62 0           my $predicate = $self->predicate;
63              
64 0 0         my $prefix_match = $predicate->match($path)
65             or return $predicate->complete($path);
66              
67 0           my $new_path = $path->clone_path($prefix_match->leftover);
68              
69 0           my $prefix = substr($path->path, 0, length($path->path) - length($new_path->path));
70              
71 0           my @completions = map { $_->complete($new_path) } $self->rules;
  0            
72              
73 0 0         if ($predicate->can('untokenize')) {
74 0           return map { $predicate->untokenize($prefix, $_) } @completions;
  0            
75             }
76             else {
77 0           return map { "$prefix$_" } @completions;
  0            
78             }
79             }
80              
81             __PACKAGE__->meta->make_immutable;
82 31     31   28782 no Moo;
  31         78  
  31         140  
83              
84             1;
85              
86             __END__
87              
88             =pod
89              
90             =encoding UTF-8
91              
92             =head1 NAME
93              
94             Path::Dispatcher::Rule::Under - rules under a predicate
95              
96             =head1 VERSION
97              
98             version 1.08
99              
100             =head1 SYNOPSIS
101              
102             my $ticket = Path::Dispatcher::Rule::Tokens->new(
103             tokens => [ 'ticket' ],
104             prefix => 1,
105             );
106              
107             my $create = Path::Dispatcher::Rule::Tokens->new(
108             tokens => [ 'create' ],
109             block => sub { create_ticket() },
110             );
111              
112             my $delete = Path::Dispatcher::Rule::Tokens->new(
113             tokens => [ 'delete', qr/^\d+$/ ],
114             block => sub { delete_ticket(shift->pos(2)) },
115             );
116              
117             my $rule = Path::Dispatcher::Rule::Under->new(
118             predicate => $ticket,
119             rules => [ $create, $delete ],
120             );
121              
122             $rule->match("ticket create");
123             $rule->match("ticket delete 3");
124              
125             =head1 DESCRIPTION
126              
127             Rules of this class have two-phase matching: if the predicate is matched, then
128             the contained rules are matched. The benefit of this is less repetition of the
129             predicate, both in terms of code and in matching it.
130              
131             =head1 ATTRIBUTES
132              
133             =head2 predicate
134              
135             A rule (which I<must> match prefixes) whose match determines whether the
136             contained rules are considered. The leftover path of the predicate is used
137             as the path for the contained rules.
138              
139             =head2 rules
140              
141             A list of rules that will be try to be matched only if the predicate is
142             matched.
143              
144             =head1 SUPPORT
145              
146             Bugs may be submitted through L<the RT bug tracker|https://rt.cpan.org/Public/Dist/Display.html?Name=Path-Dispatcher>
147             (or L<bug-Path-Dispatcher@rt.cpan.org|mailto:bug-Path-Dispatcher@rt.cpan.org>).
148              
149             =head1 AUTHOR
150              
151             Shawn M Moore, C<< <sartak at bestpractical.com> >>
152              
153             =head1 COPYRIGHT AND LICENSE
154              
155             This software is copyright (c) 2020 by Shawn M Moore.
156              
157             This is free software; you can redistribute it and/or modify it under
158             the same terms as the Perl 5 programming language system itself.
159              
160             =cut