File Coverage

blib/lib/Router/PathInfo.pm
Criterion Covered Total %
statement 62 69 89.8
branch 21 36 58.3
condition 15 25 60.0
subroutine 10 12 83.3
pod 3 5 60.0
total 111 147 75.5


line stmt bran cond sub pod time code
1             package Router::PathInfo;
2 1     1   3217 use strict;
  1         3  
  1         44  
3 1     1   6 use warnings;
  1         2  
  1         57  
4              
5             our $VERSION = '0.05';
6              
7 1     1   955 use namespace::autoclean;
  1         24035  
  1         10  
8 1     1   63 use Carp;
  1         2  
  1         81  
9              
10 1     1   846 use Router::PathInfo::Controller;
  1         3  
  1         32  
11 1     1   769 use Router::PathInfo::Static;
  1         3  
  1         807  
12              
13             =head1 NAME
14              
15             B - PATH_INFO router, based on search trees
16              
17             =head1 DESCRIPTION
18              
19             Allows balancing PATH_INFO to static and controllers.
20             It has a simple and intuitive interface.
21              
22             =head1 WARNING
23              
24             Version less than 0.05 is depricated.
25              
26             =head1 SYNOPSIS
27              
28             use Router::PathInfo;
29            
30             # or
31             use Router::PathInfo as => singletone;
32             # this allow to call after new: instance, clear_singleton
33            
34             my $r = Router::PathInfo->new(
35             static => {
36             allready => {
37             path => '/path/to/static',
38             first_uri_segment => 'static'
39             }
40             },
41             cache_limit => 300
42             );
43            
44             $r->add_rule(
45             connect => '/foo/:enum(bar|baz)/:name(year):re(^\d{4}$)/:any',
46             action => $some_thing,
47             mthods => ['GET','DELETE'],
48             match_callback => $code_ref
49             );
50            
51             my $env = {PATH_INFO => '/foo/bar/2011/baz', REQUEST_METHOD => 'GET'};
52            
53             my $res = $r->match($env);
54             # or
55             my $res = $r->match('/foo/bar/2011/baz'); # GET by default
56            
57             # $res = {
58             # type => 'controller',
59             # action => $some, # result call $code_ref->($match, $env)
60             # name_segments => {'year' => 2011}
61             # }
62            
63            
64             $env = {PATH_INFO => '/static/img/some.jpg'};
65            
66             $res = $r->match($env);
67            
68             # $res = {
69             # type => 'static',
70             # file => '/path/to/static/img/some.jpg',
71             # mime => 'image/jpeg'
72             # }
73              
74             See more details L, L
75              
76             =head1 PACKAGE VARIABLES
77              
78             =head2 $Router::PathInfo::as_singleton
79              
80             Mode as singletone. By default - 0.
81             You can pick up directly, or:
82              
83             use Router::PathInfo as => singletone;
84             # or
85             require Router::PathInfo;
86             Router::PathInfo->import(as => singletone);
87             # or
88             $Router::PathInfo::as_singleton = 1
89            
90             If you decide to work in singletone mode, raise the flag before the call to C.
91              
92             =cut
93              
94             my $as_singletone = 0;
95              
96             sub import {
97 1     1   10 my ($class, %param) = @_;
98 1 50 33     7 $as_singletone = 1 if ($param{as} and $param{as} eq 'singletone');
99 1         13 return;
100             }
101              
102             =head1 SINGLETON
103              
104             When you work in a mode singletone, you have access to methods: C and C
105              
106             =cut
107              
108              
109             =head1 METHODS
110              
111             =head2 new(static => $static, cache_limit => $cache_limit)
112              
113             Constructor. All arguments optsioanlny.
114              
115             static - it hashref arguments for the constructor L
116              
117             cache_limit - limit of matches stored by the rules contain tokens C<:re> and C<:any>, statics and errors. By default - 200.
118             All matches (that occur on an accurate description) cached without limit.
119              
120             =cut
121              
122             my $singleton = undef;
123              
124             sub new {
125 1 50 33 1 1 1182 return $singleton if ($as_singletone and $singleton);
126            
127 1         2 my $class = shift;
128 1         4 my $param = {@_};
129            
130 1         6 my $self = bless {
131 0         0 static => UNIVERSAL::isa($param->{static}, 'HASH') ? Router::PathInfo::Static->new(%{delete $param->{static}}) : undef,
132 1 50 33     6 controller => UNIVERSAL::isa($param->{controller}, 'HASH') ? Router::PathInfo::Controller->new(%{delete $param->{controller}}) : Router::PathInfo::Controller->new(),
    50          
    50          
133             cache => {},
134             _hidden_cache => {},
135             cache_limit => (defined $param->{cache_limit} and $param->{cache_limit}) =~ /^\d+$/ ? $param->{cache_limit} : 200,
136             cache_cnt => 0
137             }, $class;
138            
139 1 50       4 $singleton = $self if $as_singletone;
140            
141 1         3 return $self;
142             }
143              
144             =head2 add_rule
145              
146             See C from L
147              
148             =cut
149             sub add_rule {
150 3     3 1 6355 my $self = shift;
151 3         6 my $ret = 0;
152 3 50       21 if ($self->{controller}) {
153 3         4 $self->{cache_cnt} = 0;
154 3         7 $self->{cache} = {};
155 3         20 $self->{controller}->add_rule(@_);
156             } else {
157 0         0 carp "controller not defined";
158             }
159             }
160              
161 0 0   0 0 0 sub instance {$as_singletone ? $singleton : carp "singletone not allowed"}
162 0     0 0 0 sub clear_singleton {undef $singleton}
163              
164             =head2 match({PATH_INFO => $path_info, REQUEST_METHOD => $method})
165              
166             Search match. Initially checked for matches on static, then according to the rules of the controllers.
167             In any event returns hashref coincidence or an error.
168              
169             Example:
170              
171             {
172             type => 'error',
173             code => 400,
174             desc => '$env->{PATH_INFO} not defined'
175             }
176            
177             {
178             type => 'error',
179             code => 404,
180             desc => sprintf('not found for PATH_INFO = %s with REQUEST_METHOD = %s', $env->{PATH_INFO}, $env->{REQUEST_METHOD})
181             }
182            
183             {
184             type => 'controller',
185             action => $action,
186             name_segments => $hashref_of_names_segments
187             }
188            
189             {
190             type => 'static',
191             file => $serch_file,
192             mime => $mime_type
193             }
194              
195             =cut
196             sub match {
197 7     7 1 7487 my $self = shift;
198 7         11 my $env = shift;
199            
200 7 50       21 unless (ref $env) {
201 0         0 $env = {PATH_INFO => $env, REQUEST_METHOD => 'GET'};
202             } else {
203 7   100     30 $env->{REQUEST_METHOD} ||= 'GET';
204             }
205            
206 7         10 my $match = undef;
207            
208 7 50       18 $match = {
209             type => 'error',
210             code => 400,
211             desc => '$env->{PATH_INFO} not defined'
212             } unless $env->{PATH_INFO};
213            
214             # find in cache
215 7         22 my $cache_key = join('#',$env->{PATH_INFO}, $env->{REQUEST_METHOD});
216 7   66     32 my $cache_match = $self->{cache}->{$cache_key} || $self->{_hidden_cache}->{$cache_key};
217 7 100       15 if ($cache_match) {
218             # only for controller
219 1 50       9 $cache_match = $cache_match->{_callback}->({%$cache_match},$env) if exists $cache_match->{_callback};
220 1         6 return $cache_match;
221             };
222            
223 6         23 my @segment = split '/', $env->{PATH_INFO}, -1; shift @segment;
  6         9  
224 6         26 $env->{'psgix.tmp.RouterPathInfo'} = {
225             segments => [@segment],
226             depth => scalar @segment
227             };
228            
229             # check in static
230 6 50 33     37 if (not $match and $self->{static}) {
231 6         27 $match = $self->{static}->match($env);
232             }
233            
234             # check in controllers
235             # $not_exactly - match with regexp
236 6         47 my $not_exactly = 0;
237 6 100 66     29 if (not $match and $self->{controller}) {
238 4         16 ($not_exactly, $match) = $self->{controller}->match($env);
239             }
240            
241             # not found?
242             $match ||= {
243 6   100     18 type => 'error',
244             code => 404,
245             desc => sprintf('not found for PATH_INFO = %s with REQUEST_METHOD = %s', $env->{PATH_INFO}, $env->{REQUEST_METHOD})
246             };
247            
248 6         19 delete $env->{'psgix.tmp.RouterPathInfo'};
249            
250             # cache!
251 6 100 100     32 if (not $not_exactly and $match->{type} eq 'controller') {
    50          
252 1         3 $self->{_hidden_cache}->{$cache_key} = $match;
253             } elsif ($self->{cache_limit}) {
254 5 50       9 if ($self->{cache_cnt} > $self->{cache_limit}) {
255 0         0 $self->{cache_cnt} = 0;
256 0         0 $self->{cache} = {};
257             } else {
258 5         10 $self->{cache_cnt}++;
259             }
260 5         13 $self->{cache}->{$cache_key} = $match;
261             }
262            
263             # only for controller
264 6 100       21 $match = $match->{_callback}->({%$match},$env) if exists $match->{_callback};
265            
266             # match is done
267 6         27 return $match;
268             }
269              
270             =head1 SOURSE
271              
272             git@github.com:mrRico/p5-Router-Path-Info.git
273              
274             =head1 SEE ALSO
275              
276             L, L
277              
278             =head1 AUTHOR
279              
280             mr.Rico
281              
282             =cut
283             1;
284             __END__