File Coverage

blib/lib/Cake/Dispatcher.pm
Criterion Covered Total %
statement 58 178 32.5
branch 11 64 17.1
condition 8 52 15.3
subroutine 7 11 63.6
pod 0 5 0.0
total 84 310 27.1


line stmt bran cond sub pod time code
1             package Cake::Dispatcher;
2 8     8   45 use strict;
  8         15  
  8         245  
3 8     8   40 use warnings;
  8         13  
  8         171  
4 8     8   44 use Carp;
  8         12  
  8         17839  
5            
6             #============================================================================
7             # setup
8             #============================================================================
9             sub setup {
10 3     3 0 1918 my $self = shift;
11             #match current route
12 3         27 $self->match();
13 3 50 33     16 if (defined $self->controller and $self->controller->can('begin')){
14 0         0 $self->controller->begin($self);
15             }
16            
17             ##dispatch & excute
18 3         34 $self->dispatch();
19 3 50 33     16 if (defined $self->controller and $self->controller->can('end')){
20 0         0 $self->controller->end($self);
21             }
22             }
23            
24             #============================================================================
25             # dispatch: sending to the match route and execute
26             #============================================================================
27             sub dispatch {
28 3     3 0 8 my $self = shift;
29 3         5 my $args = shift;
30            
31 3         22 my $actionclass = $self->ActionClass;
32 3         12 my $controller = $self->controller;
33 3         20 my $code = $self->code;
34 3   33     20 $args ||= $self->action->{args};
35            
36             ##running actions
37 3 50       11 if ($actionclass){
38 0         0 $actionclass->execute($controller,$self,$code,$args);
39             }
40            
41             ##running auto blocks
42 3 50       16 if (my $auto = $self->auto_chain->{ref $controller}){
43 0         0 my $line = $self->action->{line};
44 0 0       0 map { __PACKAGE__->execute($controller,$self,$_->{code},$args) if $_->{line} < $line } @{$auto};
  0         0  
  0         0  
45             }
46            
47 3         21 __PACKAGE__->execute($controller,$self,$code,$args);
48             }
49            
50            
51             #============================================================================
52             # match : match current request path with routes
53             #============================================================================
54             ##match sequence
55             ## 1) direct paths - 2) paths with defined arguments - 3) chained - 4) regex
56             sub match {
57 3     3 0 13 my $self = shift;
58 3   33     37 my $path = shift || $self->path;
59            
60 3         25 $self->log("Start Searching For $path path");
61 3         46 my $method = $self->method;
62 3         84 my $dispatch = $self->dispatcher;
63 3         6 my $match;
64             my @captures;
65            
66 3         13 $self->log("1- Trying Direct Path Match");
67            
68 3 0 33     31 if ($dispatch->{$path} && ($match = $dispatch->{$path}->{$method}) && !$dispatch->{$path}->{$method}->{chain}){
      33        
69 0         0 $self->addAction($match,"Direct Match");
70 0         0 return;
71             }
72            
73 3         12 $self->log("2- Trying PAths With Arguments");
74             ## nothing found in direct paths, lets try with args
75 3         16 (my $tpath = $path) =~ s/^\///;
76 3         14 my @args = split(/\//,$tpath);
77 3         84 my $i = $#args+1;
78            
79             ####get sequence of path - arg ... /first/second(1) /first(2) /(3)
80 3         6 my $t;
81 3         7 my @t = reverse (map { --$i; $t .= '/'.$_; $t.'('.$i.')' } @args);
  0         0  
  0         0  
  0         0  
82 3         13 push(@t,'/'.'('.($#args+1).')');
83 3         7 foreach my $this (@t){
84 3 0 33     15 if ($dispatch->{$this} && ($match = $dispatch->{$this}->{$method}) && !$dispatch->{$this}->{$method}->{chain}){
      33        
85             ##capture
86 0         0 @captures = splice(@args,-$i,$i);
87 0         0 $self->addAction($match,"Path with arguments Match", @captures);
88            
89 0         0 return;
90             }
91 3         8 $i++;
92             }
93            
94 3         11 $self->log("3- Trying Chained Actions");
95             ##lets try chains
96             ####start with chain indexes and search for best match
97 3 50       21 if ( my $indexes = $dispatch->{chains_index} ){
98            
99 0         0 local $self->{chain_action};
100 0         0 local $self->{chain_sequence};
101            
102 0         0 for my $index (@{$indexes}){
  0         0  
103             #die Dumper $chain->{$index_path};
104 0         0 my $return = $self->_loop_chains($path,$index,1);
105 0 0 0     0 $return && $return == 1 ? next : last;
106             }
107            
108 0 0       0 if ($self->{chain_action}){
109 0         0 my @chain = @{$self->{chain_action}};
  0         0  
110 0         0 my $lastaction = pop @chain;
111            
112 0         0 for my $action (@chain){
113 0         0 $self->addAction($action,"Chained Match");
114 0         0 $self->dispatch();
115             }
116            
117 0         0 $self->addAction($lastaction,"Last Chained Match");
118 0         0 return;
119            
120             }
121             }
122            
123 3         14 $self->log("4- Trying Regex Match");
124 3 100       13 if ($dispatch->{regex}){
125 1         3 my $oldstrength = '';
126 1         3 my $match;
127            
128 1         2 foreach my $this (@{$dispatch->{regex}}){
  1         3  
129 1 50       8 if ($path =~ m/$this->{regex}/){
130 0 0       0 next if (!grep(/$method/,@{$this->{methods}}));
  0         0  
131             ###select the strongest
132 0         0 my $localpath = $path; $localpath =~ s/$this->{regex}//;
  0         0  
133 0         0 my $strength = length $localpath;
134            
135 0 0 0     0 if ($oldstrength eq '' || $strength < $oldstrength){
136 0         0 $match = $this;
137             }
138            
139 0         0 $oldstrength = $strength;
140             #return;
141             }
142             }
143            
144 1 50       4 if ($match){
145             ##capture it
146 0         0 @captures = ($path =~ m/$match->{regex}$/);
147 0         0 @{$self->{capture}} = map { split('/',$_) } @captures;
  0         0  
  0         0  
148 0         0 $self->addAction($match->{action},"Regex Match",@{$self->{capture}});
  0         0  
149 0         0 return;
150             }
151             }
152            
153 3         27 $self->log("There is no matching route for $path");
154            
155             ##does the app defined a sub to process not found locations?
156 3 50       10 if ($self->app->can('notfound')){
157 0         0 $self->app->notfound($self);
158             } else {
159 3         24 $self->status_code('404');
160 3         16 $self->body('Not Found');
161             }
162             }
163            
164             sub _loop_chains {
165 0     0   0 my $self = shift;
166 0         0 my $path = shift;
167 0         0 my $dir = shift;
168 0         0 my $add_namespace = shift;
169 0         0 my $regex;
170 0         0 my $dispatch = $self->dispatcher;
171 0         0 my $chain = $dispatch->{chains};
172 0         0 my $thisChain = $chain->{$dir};
173            
174 0 0       0 my $route = $add_namespace ?
175             $dir :
176             $thisChain->{dir};
177            
178 0         0 my $args = $thisChain->{args};
179            
180 0 0       0 if ($args){
181 0         0 $regex = qr{^$route/(.*?)$};
182             } else {
183 0         0 $regex = qr{^$route(.*?)$};
184             }
185            
186 0 0       0 if (my ($newpath) = $path =~ m#$regex#){
187 0 0       0 my $match = $dispatch->{$thisChain->{path}}->{$self->method}
188             or return 1;
189            
190 0         0 my @captures;
191 0 0       0 if ($args){
192 0         0 @captures = split '/',$newpath;
193 0         0 my @args = splice @captures,0,$args;
194             ##re construct path
195 0         0 $newpath = '/'.join '/',@captures;
196 0         0 $match->{captures} = \@args;
197             }
198            
199 0         0 my $next_chain = $thisChain->{chained_by};
200 0 0 0     0 return 1 if !$next_chain && $newpath && (!$args || $args && @captures);
      0        
      0        
201 0         0 push @{$self->{action_sequence}},$match;
  0         0  
202            
203             ###last action in the chain
204 0 0 0     0 $self->{chain_action} = $self->{action_sequence} and return
205             if !$next_chain;
206            
207 0         0 for my $next (@{$next_chain}){
  0         0  
208 0         0 my $nextnamespace = $chain->{$next}->{namespace};
209 0 0       0 my $return = $self->_loop_chains($newpath,$next,$nextnamespace ne $thisChain->{namespace} ? 1 : 0);
210            
211 0 0 0     0 if ($return && $return == 1){
212 0         0 next;
213             }
214 0         0 return;
215             }
216            
217 0         0 @{$self->{action_sequence}} = splice @{$self->{action_sequence}},0,-1;
  0         0  
  0         0  
218             }
219            
220 0         0 return 1;
221             }
222            
223            
224            
225             sub addAction {
226 0     0 0 0 my $self = shift;
227 0         0 my $match = shift;
228 0         0 my $type = shift;
229            
230 0         0 $self->log("PINGO: $type Found in " . $match->{class} . " at line " . $match->{line} );
231            
232 0         0 my @captures;
233            
234 0 0       0 if (@_) {
    0          
235 0         0 @captures = @_;
236             } elsif ($match->{captures}){
237 0         0 @captures = @{$match->{captures}};
  0         0  
238             }
239            
240             $self->action({
241 0   0     0 ActionClass => $match->{ActionClass},
242             controller => bless($self->config->{$match->{class}} || {},$match->{class}),
243             code => _get_code($match->{class},$match->{code}),
244             line => $match->{line},
245             namespace => $match->{namespace},
246             args => \@captures
247             });
248            
249             ##log captures
250 0 0 0     0 if ($self->debug && @captures){
251             $self->log(sub {
252 0     0   0 my $msg = '';
253 0         0 $msg .= "=================\n";
254 0         0 $msg .= " Captured Args \n";
255 0         0 $msg .= "=================\n";
256 0         0 foreach my $arg (@captures){
257 0         0 $msg .= $arg."\n";
258             }
259 0         0 return $msg;
260 0         0 });
261             }
262            
263 0 0 0     0 if (@captures && ref $match->{args} eq 'ARRAY'){
264 0         0 my $count = -1;
265 0         0 my %capture = map { ++$count; $_ => $captures[$count] } @{$match->{args}};
  0         0  
  0         0  
  0         0  
266 0         0 $self->action->{args} = \%capture;
267             }
268            
269 0         0 return;
270             }
271            
272             #============================================================================
273             # excute : execute current action
274             #============================================================================
275             sub execute {
276 3     3 0 10 shift;
277 3         8 my ($controller,$c,$method,$args) = @_;
278 3 50       15 if (ref $method eq 'CODE'){
279 0 0         my @args = ref $args eq 'HASH' ? values %{$args} : @{$args};
  0            
  0            
280 0           return $method->($controller,$c,@args);
281             }
282             }
283            
284            
285             #============================================================================
286             # private method
287             #============================================================================
288             sub _get_code {
289 0     0     my $controller = shift;
290 0           my $method = shift;
291             ###convert method to CODE ref
292 0 0         if (ref $method ne 'CODE'){
293 0           $method = $controller.'::'.$method;
294 0           $method = \&$method;
295             }
296 0           return $method;
297             }
298            
299             1;
300            
301             __END__