File Coverage

lib/Egg/Dispatch/Standard.pm
Criterion Covered Total %
statement 33 167 19.7
branch 0 72 0.0
condition 0 40 0.0
subroutine 11 28 39.2
pod 2 2 100.0
total 46 309 14.8


line stmt bran cond sub pod time code
1             package Egg::Dispatch::Standard;
2             #
3             # Masatoshi Mizuno E<lt>lusheE<64>cpan.orgE<gt>
4             #
5             # $Id: Standard.pm 342 2008-05-29 16:05:06Z lushe $
6             #
7 1     1   687 use strict;
  1         3  
  1         52  
8 1     1   6 use warnings;
  1         1  
  1         37  
9 1     1   2239 use Tie::RefHash;
  1         5968  
  1         9  
10 1     1   27 use base qw/ Egg::Dispatch /;
  1         1  
  1         94  
11              
12             our $VERSION= '3.07';
13              
14             {
15 1     1   5 no strict 'refs'; ## no critic
  1         2  
  1         28  
16 1     1   4 no warnings 'redefine';
  1         1  
  1         189  
17             sub mode_param {
18 0 0   0 1   my $class= shift; return 0 if ref($class);
  0            
19 0   0       my $pname= shift || croak(q{ I want param name. });
20 0           my $uc_class= uc($class);
21 0           *{"$class\::_get_mode"}= sub {
22 0   0 0     my $snip= $_[0]->request->param($pname) || return [];
23 0           $snip=~tr/\t\r\n//d; $snip=~s/ +//g;
  0            
24 0 0         $snip ? [ split /[\/\:\-]+/, $snip ]: [];
25 0           };
26 0           $class;
27             }
28             };
29              
30             sub import {
31 0     0     my($class)= @_;
32 1     1   4 no strict 'refs'; ## no critic
  1         5  
  1         92  
33 0           my $p_class= caller(0);
34 0           my($p_name)= $p_class=~m{(.+?)\:+Dispatch$};
35 0 0         if ( Tie::RefHash->require ) {
36             my $refhash= sub {
37 0     0     my %refhash;
38 0           tie %refhash, 'Tie::RefHash', @_;
39 0           \%refhash;
40 0           };
41 1     1   4 no warnings 'redefine';
  1         2  
  1         321  
42 0 0 0       if (($p_name and $p_name eq 'Egg')
    0 0        
43             or $p_class->can('project_name')) {
44 0           *{"${p_name}::refhash"}= $refhash;
  0            
45             } elsif ($p_class ne __PACKAGE__) {
46 0 0         *{"${p_name}::refhash"}= $refhash if $p_name;
  0            
47 0           *{"${p_class}::refhash"}= $refhash;
  0            
48             }
49             } else {
50 0           warn q{ 'Tie::RefHash' is not installed. };
51             }
52 0           $class->SUPER::import;
53             }
54             #sub _setup {
55             # my($e)= @_;
56             # my $default= $e->config->{deispath_default_name} ||= '_default';
57             # $e->dispatch_map->{$default}= sub {} unless $e->dispatch_map->{$default};
58             # $e->next::method;
59             #}
60             sub dispatch {
61 0   0 0 1   $_[0]->{Dispatch} ||= Egg::Dispatch::Standard::handler->new(@_);
62             }
63             sub _dispatch_map_check {
64 0     0     my($self, $hash, $myname)= @_;
65 0           while (my($key, $value)= each %$hash) {
66 0 0 0       if (! ref($key) and $key=~/^ARRAY\(0x[0-9a-f]+\)/) {
67 0           warn
68             qq{ Please use the refhash function. '$myname' \n}
69             . qq{ The key not recognized as ARRAY is included. };
70             }
71 0 0         if (ref($value) eq 'HASH') {
72 0 0         my $name= ref($key) eq 'ARRAY' ? do {
73 0 0         $key->[0] || die qq{It is a setting of '$myname'}
74             . qq{ and there is an action name undefinition.};
75             }: $key;
76 0           $self->_dispatch_map_check($value, $name);
77             }
78             }
79 0           $hash;
80             }
81              
82              
83             package Egg::Dispatch::Standard::handler;
84 1     1   5 use strict;
  1         2  
  1         26  
85 1     1   6 use base qw/ Egg::Dispatch::handler /;
  1         2  
  1         8166  
86              
87             __PACKAGE__->mk_accessors(qw/ parts _backup_action /);
88              
89             sub _initialize {
90 0     0     my $self= shift->SUPER::_initialize;
91 0   0       $self->parts( $self->e->_get_mode || [@{$self->e->snip}] );
92 0           $self;
93             }
94             sub mode_now {
95 0     0     my $self = shift;
96 0           my $now = $self->action;
97 0           my $label= $self->label;
98 0   0       my $num = $#{$label}- (shift || 0);
  0            
99 0 0 0       $num< 0 ? "": (join('/', @{$now}[0..$num]) || "");
100             }
101             sub label {
102 0     0     my $self= shift;
103 0 0         return $self->{label} unless @_;
104 0 0         $self->{label}->[$_[0]] || "";
105             }
106             sub _start {
107 0     0     my($self)= @_;
108 0           my $e= $self->e;
109 0           my $begins= [];
110 0           my $ends = [];
111 0           $self->{__parts_num}= 0;
112 0   0       $self->_scan_mode(
113             $e, $begins, $ends, $self->parts, 0,
114             $e->_dispatch_map, $self->default_mode,
115             ($self->e->request->is_post || 0),
116             );
117 0 0         return 0 if $self->e->finished;
118 0 0         if (exists($self->{__parts_num})) {
119 0           $self->action([ @{$self->parts}[0..$self->{__parts_num}] ]);
  0            
120 0           for my $code (@$begins) {
121 0 0         last if $e->{finished};
122 0           $code->($e, $self);
123             }
124 0 0         if (my $title= $self->{label}[$#{$self->{label}}]) {
  0            
125 0           $self->page_title($title);
126             };
127             } else {
128             # $self->action([]);
129 0           $e->finished('404 Not Found');
130             }
131 0           $self->{_end_code}= $ends;
132 0           1;
133             }
134             sub _action {
135 0     0     my($self)= @_;
136 0 0         return 0 if $self->e->{finished};
137 0           my $action= tied($self->parts->[$self->{__parts_num}]);
138 0           $action->[1]->($self->e, $self, $action->[2]);
139 0           1;
140             }
141             sub _finish {
142 0     0     my($self)= @_;
143 0           $_->($self->e, $self) for @{$self->{_end_code}};
  0            
144 0           1;
145             }
146             sub _scan_mode {
147 0     0     my($self, $e, $begins, $ends, $parts, $num, $map, $default, $is_post)= @_;
148 0           $self->{_action_now}= $num;
149 0   0       my $wanted= $parts->[$num] ||= "";
150 0           $wanted=~s{\.[^\.]{1,4}$} [];
151 0           my $default_code;
152 0           for my $key (keys %$map) {
153 0   0       my $value= $map->{$key} || next;;
154 0           my $page_title= "";
155 0 0         my $point= ref($key) eq 'ARRAY' ? do {
156 0 0         if ($key->[1]) {
157 0 0         if ($is_post) {
158 0 0         if ($key->[1]< 2) {
159 0           $self->e->finished('405 Method Not Allowed.');
160 0           return 0;
161             }
162             } else {
163 0 0         if ($key->[1]> 1) {
164 0           $self->e->finished('405 Method Not Allowed.');
165 0           return 0;
166             }
167             }
168             }
169 0   0       $page_title= $key->[2] || "";
170 0 0         $key->[0] || next;
171             }: $key;
172 0 0         if (my @piece= $wanted=~m{^$point$}) {
    0          
173 0 0         next if $point=~/^_/;
174 0   0       $page_title ||= $wanted;
175 0           push @{$self->{label}}, $page_title;
  0            
176 0 0         if ($map->{_begin}) { push @$begins, $map->{_begin} }
  0            
177 0 0         if ($map->{_end}) { unshift @$ends, $map->{_end} }
  0            
178 0 0         if (@piece) {
179 0           tie $parts->[$num],
180             'Egg::Dispatch::Standard::TieScalar',
181             $wanted, $value, \@piece;
182             } else {
183 0           $parts->[$num]= $wanted;
184             }
185 0 0         if (ref($value) eq 'HASH') {
186 0 0         return $self->_scan_mode($e, $begins, $ends, $parts,
187             ($num+ 1), $value, $default, $is_post) ? 1: 0;
188             } else {
189 0           $self->page_title($page_title);
190 0           $self->{__parts_num}= $num;
191 0           return 1;
192             }
193             } elsif ($point eq $default) {
194 0           $default_code= [$value, $page_title];
195             }
196             }
197 0 0         return 0 if $self->e->finished;
198 0 0 0       if (! $self->{__parts_num} and $default_code) {
199 0 0         if ($map->{_begin}) { push @$begins, $map->{_begin} }
  0            
200 0 0         if ($map->{_end}) { unshift @$ends, $map->{_end} }
  0            
201 0   0       push @{$self->{label}}, ($default_code->[1] || $self->default_name);
  0            
202 0           tie $parts->[$num], 'Egg::Dispatch::Standard::TieScalar',
203             $self->default_name, $default_code->[0], [];
204 0           $self->{__parts_num}= $num;
205 0           return 1;
206             }
207 0           0;
208             }
209             sub _example_code {
210 0     0     my($self)= @_;
211 0           my $a= { project_name=> $self->e->namespace };
212              
213 0           <<END_OF_EXAMPLE;
214             #
215             # Example of controller and dispatch.
216             #
217             package $a->{project_name}::Dispatch;
218             use strict;
219             use warnings;
220              
221             $a->{project_name}-&gt;dispatch_map( refhash (
222            
223             # 'ANY' matches to the method of requesting all.
224             # The value of label is used with page_title.
225             { ANY => '_default', label => 'index page.' }=> sub {
226             my(\$e, \$dispatch)= \@_;
227             \$e->template('document/default.tt');
228             },
229            
230             # Empty CODE decides the template from the mode name that becomes a hit.
231             # In this case, it is 'Help.tt'.
232             help => sub { },
233            
234             # When the request method is only GET, 'GET' is matched.
235             { GET => 'bbs_view', label => 'BBS' } => sub {
236             my(\$e, \$dispatch)= \@_;
237             .... bbs view code.
238             },
239            
240             # When the request method is only POST, 'POST' is matched.
241             { POST => 'bbs_post', label => 'BBS Contribution.' } => sub {
242             my(\$e, \$dispatch)= \@_;
243             .... bbs post code.
244             },
245            
246             # 'A' is an alias of 'ANY'.
247             { A => 'blog', label => 'My BLOG' }=>
248            
249             # The refhash function for remembrance' sake when you use HASH for the key.
250             refhash (
251            
252             # Prior processing can be defined.
253             _begin => sub {
254             my(\$e, \$dispatch)= \@_;
255             ... blog begin code.
256             },
257            
258             # 'G' is an alias of 'GET'.
259             # The regular expression can be used for the action. A rear reference is
260             # the third argument that extends to CODE.
261             { G => qr{^article_(&yen;d{4}/&yen;d{2}/&yen;d{2})}, label => 'Article' } => sub {
262             my(\$dispatch, \$e, \$parts)= \@_;
263             ... data search ( \$parts->[0] ).
264             },
265            
266             # 'P' is an alias of 'POST'.
267             { 'P' => 'edit', label => 'BLOG Edit Form.' } => sub {
268             my(\$e, \$dispatch)= \@_;
269             ... edit code.
270             },
271            
272             # Processing can be defined after the fact.
273             _end => sub {
274             my(\$e, \$dispatch)= \@_;
275             ... blog begin code.
276             },
277            
278             ),
279              
280             ) );
281              
282             1;
283             END_OF_EXAMPLE
284             }
285              
286              
287             package Egg::Dispatch::Standard::TieScalar;
288 1     1   11 use strict;
  1         2  
  1         154  
289              
290             sub TIESCALAR {
291 0     0     my($class, $orign)= splice @_, 0, 2;
292 0           bless [$orign, @_], $class;
293             }
294 0     0     sub FETCH { $_[0][0] }
295 0     0     sub STORE { $_[0][0]= $_[1] }
296              
297             1;
298              
299             __END__
300              
301             =head1 NAME
302              
303             Egg::Dispatch::Standard - Dispatch of Egg standard.
304              
305             =head1 SYNOPSIS
306              
307             package MyApp::Dispatch;
308             use base qw/ Egg::Dispatch::Standard /;
309            
310             # If the attribute is applied to the key, it sets it with ARRAY by using the
311             # refhash function.
312             Egg->dispatch_map( refhash(
313            
314             # The content of ARRAY of the key from the left. 'Action name', 'Permission
315             # -method', 'Title name'
316             # * When 0 is set, the permission method passes everything.
317             [qw/ _default 0 /, 'index page.']=> sub {
318             my($e, $dispatch)= @_;
319             $e->template('document/default.tt');
320             },
321            
322             # The second element of key ARRAY is set to one when permitting only at 'GET'
323             # request.
324             [qw/ bbs_view 1 /, 'BBS']=> sub {
325             my($e, $dispatch)= @_;
326             .... bbs view code.
327             },
328            
329             # The second element of key ARRAY is set to two when permitting only at 'POST'
330             # request.
331             [qw/ bbs_post 2 /, 'Contribution.']=> sub {
332             .... bbs post code.
333             },
334            
335             # Empty CODE decides the template from the list of the action name that becomes
336             # a hit. In this case, it is 'help.tt'.
337             help => sub { },
338            
339             [qw/ blog 0 /, 'My BLOG']=>
340            
341             # The refhash function for remembrance' sake when you use ARRAY for the key.
342             refhash(
343            
344             # Prior processing can be defined by '_begin'.
345             _begin => sub {
346             my($e, $dispatch)= @_;
347             ... blog begin code.
348             },
349            
350             # The regular expression can be used for the action. A rear reference is the
351             # third argument over CODE.
352             [qr{^article_(\d{4}/\d{2}/\d{2})}, 0, 'Article']=> sub {
353             my($e, $dispatch, $parts)= @_;
354             ... data search ( $parts->[0] ).
355             },
356            
357             # A rear reference for a shallower hierarchy extracts the value of
358             # $e->dispatch->parts with 'tied'.
359             qr{^[A-Z]([a-z0-9]+)}=> {
360             qr{^User([A-Z][a-z0-9_]+)}=> {
361             my($e, $dispatch, $match)= @_;
362             my $low_match= tied($dispatch->parts->[0])->[2];
363             ... other code.
364             },
365            
366             [qw/ edit 0 /, 'BLOG Edit Form.']=> sub {
367             my($e, $dispatch)= @_;
368             ... edit code.
369             },
370            
371             # Processing can be defined by '_end' after the fact.
372             _end => sub {
373             my($e, $dispatch)= @_;
374             ... blog begin code.
375             },
376            
377             # Time when 11 dispatch is set can be saved if it combines with $e->snip2template.
378             # Refer to L<Egg::Util> for 'snip2template' method.
379             help => {
380             _default=> sub {},
381             qr{^[a-z][a-z0-9_]+}=> sub {
382             my($e, $dispatch)= @_;
383             $e->snip2template(1) || return $e->finished('404 Not Found.');
384             },
385             },
386            
387             ),
388            
389             ) );
390              
391             =head1 DESCRIPTION
392              
393             It is dispatch of the Egg standard.
394              
395             Dispatch is processed according to the content defined in 'dispatch_map'.
396              
397             Dipatti of the layered structure is treatable.
398              
399             The value of the point where the action the final reaches should be CODE
400             reference.
401              
402             Objec of the project and the handler object of dispatch are passed for the CODE
403             reference.
404              
405             It corresponds to the key to the ARRAY form by using the refhash function.
406             see L<Tie::RefHash>.
407              
408             Page title corresponding to the matched place is set, and the request method
409             can be limited and it match it by using the key to the ARRAY form.
410              
411             The regular expression can be used for the key. As a result, it is possible to
412             correspond to a flexible request pattern.
413             Moreover, because a rear reference can be received, it is treatable in the CODE
414             reference.
415              
416             # 1.
417             qr{^baz_(.+)}=> {
418             # 2.
419             qr{^boo_(.+)}=> sub {
420             my($d, $e, $p)= @_;
421             },
422             },
423              
424             As for such dispatch, the rear reference obtained by '# 2' is passed to the third
425             argument of the CODE reference.
426             In a word, the value of $p is a rear reference corresponding to the regular
427             expression of '# 2' and the content is ARRAY reference.
428              
429             To process the rear reference obtained in the place of '# 1', tied is used and
430             extracted from the value of $e-E<gt>action.
431              
432             # Because the key to '# 1' is a regular expression that picks up a rear reference,
433             # piece zero element of $e->dispatch->parts is set with Tie SCALAR.
434             # When the value is done in tied, and the ARRAY object is acquired, the second
435             # element is a value of a rear reference.
436             my $p1_array= tied($e->dispatch->parts->[0])->[2];
437            
438             # And, the element number of a rear reference wanting it is specified.
439             my $match= $p1_array->[0];
440            
441             # By the way, '# 2' can be similarly acquired from the first element.
442             # my $p2_array= tied($e->dispatch->parts->[1])->[2];
443              
444             '_begin' is executed from the one of a shallower hierarchy. When $e->finished is
445             set on the way, '_begin' of a hierarchy that is deeper than it is not processed.
446              
447             It processes it after the fact when '_end' key is defined.
448             Even if it is executed from the one of the hierarchy with deeper matched action,
449             and $e-E<gt>finished is set on the way, '_end' processes '_end' of a shallower
450             hierarchy. Therefore, it is necessary to check $e-E<gt>finished on the code side.
451              
452             =over 4
453              
454             =item * mode_param, dispatch_map
455              
456             =back
457              
458             =head1 WARNING
459              
460             Some specifications have changed because of Egg::Response-3.12.
461              
462             The change part is as follows.
463              
464             =over 4
465              
466             =item * When a rear reference was obtained, the content of the action of the correspondence element was preserved with Tie Scalar.
467              
468             As a result, acquiring a rear reference for a shallower hierarchy became
469             possible.
470              
471             =item * It was made to do with ARRAY when the attribute was set to the key to dispatch.
472              
473             Because the referred attribute is few, it is not troublesomely seen to set it
474             with HASH easily.
475              
476             =item * The order of evaluating '_begin' reversed.
477              
478             Processing it from a deeper hierarchy before to a shallow hierarchy is still
479             strange.
480              
481             =back
482              
483             =head1 EXPORT FUNCTION
484              
485             It is a function exported to the controller and the dispatch class of the project.
486              
487             =head2 refhash ([HASH])
488              
489             Received HASH is returned and after Tie is done with L<Tie::RefHash>, the content is
490             returned by the HASH reference.
491              
492             When the key to the ARRAY form is set to 'dispatch_map', it is indispensable.
493              
494             It doesn't go well even if the reference is passed to this function.
495             Please pass it by a usual HASH form.
496              
497             # This is not good.
498             my $hashref = refhash ({
499             [qw/ _default 0 /, 'index page.']=> sub {},
500             [qw/ help 0 /, 'help page.' ]=> sub {},
501             });
502              
503             # It is OK.
504             my $hashref = refhash (
505             [qw/ _default 0 /, 'index page.']=> sub {},
506             [qw/ help 0 /, 'help page.' ]=> sub {},
507             );
508              
509             =head1 METHODS
510              
511             L<Egg::Dispatch> has been succeeded to.
512              
513             =head2 dispatch
514              
515             The 'Egg::Dispatch::Standard::handler' object is returned.
516              
517             my $d= $e->dispatch;
518              
519             =head1 HANDLER METHODS
520              
521             =head2 mode_now
522              
523             The value in which the list of the matched action ties by '/' delimitation is
524             returned.
525              
526             =head2 label ( [NUMBER] )
527              
528             The list of the matched action is returned by the ARRAY reference.
529              
530             When the figure is given, the corresponding value is returned.
531              
532             =head1 SEE ALSO
533              
534             L<Egg::Release>,
535             L<Egg::Dispatch>,
536             L<Tie::RefHash>,
537              
538             =head1 AUTHOR
539              
540             Masatoshi Mizuno E<lt>lusheE<64>cpan.orgE<gt>
541              
542             =head1 COPYRIGHT AND LICENSE
543              
544             Copyright (C) 2008 Bee Flag, Corp. E<lt>L<http://egg.bomcity.com/>E<gt>.
545              
546             This library is free software; you can redistribute it and/or modify
547             it under the same terms as Perl itself, either Perl version 5.8.6 or,
548             at your option, any later version of Perl 5 you may have available.
549              
550             =cut
551