| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Router::PathInfo::Controller; | 
| 2 | 2 |  |  | 2 |  | 4857 | use strict; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 113 |  | 
| 3 | 2 |  |  | 2 |  | 11 | use warnings; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 121 |  | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  | =head1 NAME | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | B provides a mapping PATH_INFO to controllers. | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 10 |  |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  | # create instance | 
| 12 |  |  |  |  |  |  | my $r = Router::PathInfo::Controller->new(); | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | # describe connect | 
| 15 |  |  |  |  |  |  | $r->add_rule(connect => '/foo/:enum(bar|baz)/:any', action => ['some','bar']); | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | # prepare arguments (this action to prepare $env hidden from you in the module Router::PathInfo) | 
| 18 |  |  |  |  |  |  | my $env = {PATH_INFO => '/foo/baz/bar', REQUEST_METHOD => 'GET'}; | 
| 19 |  |  |  |  |  |  | my @segment = split '/', $env->{PATH_INFO}, -1; | 
| 20 |  |  |  |  |  |  | shift @segment; | 
| 21 |  |  |  |  |  |  | $env->{'psgix.tmp.RouterPathInfo'} = { | 
| 22 |  |  |  |  |  |  | segments => [@segment], | 
| 23 |  |  |  |  |  |  | depth => scalar @segment | 
| 24 |  |  |  |  |  |  | }; | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | # match | 
| 27 |  |  |  |  |  |  | my $res = $r->match($env); | 
| 28 |  |  |  |  |  |  | #  $res =  HASH(0x93d74d8) | 
| 29 |  |  |  |  |  |  | #   'action' => ARRAY(0x99294e8) | 
| 30 |  |  |  |  |  |  | #      0  'some' | 
| 31 |  |  |  |  |  |  | #      1  'bar' | 
| 32 |  |  |  |  |  |  | #   'segment' => ARRAY(0x93d8038) | 
| 33 |  |  |  |  |  |  | #      0  'baz' | 
| 34 |  |  |  |  |  |  | #      1  'bar' | 
| 35 |  |  |  |  |  |  | #   'type' => 'controller' | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  | # or $res may by undef | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | C is used for matching sets of trees. | 
| 42 |  |  |  |  |  |  | Therefore, search matching is faster and more efficient, | 
| 43 |  |  |  |  |  |  | than a simple enumeration of regular expressions to search for a suitable result. | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | In the descriptions of 'C' by adding rules, you can use these tokens: | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  | :any                                 - match with any segment | 
| 48 |  |  |  |  |  |  | :re(...some regular expression...)   - match with the specified regular expression | 
| 49 |  |  |  |  |  |  | :enum(...|...)                       - match with a segment from the set | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | and sub-attribute for rules | 
| 52 |  |  |  |  |  |  |  | 
| 53 |  |  |  |  |  |  | :name(...) | 
| 54 |  |  |  |  |  |  |  | 
| 55 |  |  |  |  |  |  | For example | 
| 56 |  |  |  |  |  |  |  | 
| 57 |  |  |  |  |  |  | '/foo/:name(some_name)bar/:any' | 
| 58 |  |  |  |  |  |  | '/foo/:re(^\d{4}\w{4}$)/:name(my_token):any' | 
| 59 |  |  |  |  |  |  | '/:enum(foo|bar|baz)/:re(^\d{4}\w{4}$)/:any' | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | All descriptions of the segments have a certain weight. | 
| 62 |  |  |  |  |  |  | Thus, the description C<:enum> has the greatest weight, a description of C<:re> weighs even less. Weakest coincidence is C<:any>. | 
| 63 |  |  |  |  |  |  |  | 
| 64 |  |  |  |  |  |  | For all descriptions 'C' using these tokens in the match will be returned to a special key 'C' | 
| 65 |  |  |  |  |  |  | in which stores a list of all segments C they are responsible. | 
| 66 |  |  |  |  |  |  |  | 
| 67 |  |  |  |  |  |  | An important point: description 'C' dominates over http method. Example: | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  | $r->add_rule(connect => '/foo/:any/baz', action => 'one', methods => ['GET','DELETE']); | 
| 70 |  |  |  |  |  |  | $r->add_rule(connect => '/foo/bar/:any', action => 'two'); | 
| 71 |  |  |  |  |  |  |  | 
| 72 |  |  |  |  |  |  | for '/foo/bar/baz' with GET -> 'two' | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  | In C you can pass any value: object, arrayref, hashref or a scalar. | 
| 75 |  |  |  |  |  |  |  | 
| 76 |  |  |  |  |  |  | =head1 METHODS | 
| 77 |  |  |  |  |  |  |  | 
| 78 |  |  |  |  |  |  | =cut | 
| 79 |  |  |  |  |  |  |  | 
| 80 | 2 |  |  | 2 |  | 5455 | use namespace::autoclean; | 
|  | 2 |  |  |  |  | 45627 |  | 
|  | 2 |  |  |  |  | 12 |  | 
| 81 | 2 |  |  | 2 |  | 112 | use Carp; | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 3127 |  | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  | my $http_methods = { | 
| 84 |  |  |  |  |  |  | GET     => 1, | 
| 85 |  |  |  |  |  |  | POST    => 1, | 
| 86 |  |  |  |  |  |  | PUT     => 1, | 
| 87 |  |  |  |  |  |  | OPTIONS => 1, | 
| 88 |  |  |  |  |  |  | DELETE  => 1, | 
| 89 |  |  |  |  |  |  | HEAD    => 1 | 
| 90 |  |  |  |  |  |  | }; | 
| 91 |  |  |  |  |  |  |  | 
| 92 |  |  |  |  |  |  | =head2 new() | 
| 93 |  |  |  |  |  |  |  | 
| 94 |  |  |  |  |  |  | Simple constructor | 
| 95 |  |  |  |  |  |  |  | 
| 96 |  |  |  |  |  |  | =cut | 
| 97 |  |  |  |  |  |  | sub new { | 
| 98 | 3 |  |  | 3 | 1 | 1169 | bless { | 
| 99 |  |  |  |  |  |  | rule => {}, | 
| 100 |  |  |  |  |  |  | re_compile => {}, | 
| 101 |  |  |  |  |  |  | }, shift; | 
| 102 |  |  |  |  |  |  | } | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | =head2 add_rule(connect => $describe_connect, action => $action_token[, methods => $arrayref, match_callback => $code_ref]) | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | Add your description to match. | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  | 'C' - arrayref of items GET, POST, PUT, OPTIONS, DELETE, HEAD | 
| 109 |  |  |  |  |  |  |  | 
| 110 |  |  |  |  |  |  | 'C' - coderef is called after match found. It takes two arguments: a match found and heshref passed parameters (see method C). | 
| 111 |  |  |  |  |  |  | Example: | 
| 112 |  |  |  |  |  |  |  | 
| 113 |  |  |  |  |  |  | $r->add_rule( | 
| 114 |  |  |  |  |  |  | connect => '/foo/:enum(bar|baz)/:any', | 
| 115 |  |  |  |  |  |  | action => ['any thing'], | 
| 116 |  |  |  |  |  |  | methods => ['POST'], | 
| 117 |  |  |  |  |  |  | match_callback => sub { | 
| 118 |  |  |  |  |  |  | my ($match, $env) = @_; | 
| 119 |  |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  | if ($env->{...} == ..) { | 
| 121 |  |  |  |  |  |  | # $match->{action}->[0] eq 'any thing' | 
| 122 |  |  |  |  |  |  | return $match; | 
| 123 |  |  |  |  |  |  | } else { | 
| 124 |  |  |  |  |  |  | return { | 
| 125 |  |  |  |  |  |  | type  => 'error', | 
| 126 |  |  |  |  |  |  | code => 403, | 
| 127 |  |  |  |  |  |  | desc  => 'blah-blah' | 
| 128 |  |  |  |  |  |  | }; | 
| 129 |  |  |  |  |  |  | } | 
| 130 |  |  |  |  |  |  | } | 
| 131 |  |  |  |  |  |  | ); | 
| 132 |  |  |  |  |  |  |  | 
| 133 |  |  |  |  |  |  | =cut | 
| 134 |  |  |  |  |  |  | sub add_rule { | 
| 135 | 7 |  |  | 7 | 1 | 3792 | my ($self, %args) = @_; | 
| 136 |  |  |  |  |  |  |  | 
| 137 | 7 |  |  |  |  | 20 | for ( ('connect', 'action') ) { | 
| 138 | 14 | 50 |  |  |  | 55 | unless ($args{$_}) { | 
| 139 | 0 |  |  |  |  | 0 | carp "missing '$_'"; | 
| 140 | 0 |  |  |  |  | 0 | return; | 
| 141 |  |  |  |  |  |  | }; | 
| 142 |  |  |  |  |  |  | } | 
| 143 | 7 | 50 |  |  |  | 39 | $args{methods} = $args{methods} ? [grep {$http_methods->{$_}} (ref $args{methods} eq 'ARRAY' ? @{$args{methods}} : $args{methods})] : []; | 
|  | 3 | 100 |  |  |  | 13 |  | 
|  | 2 |  |  |  |  | 7 |  | 
| 144 | 7 | 100 |  |  |  | 46 | my @methods =   $args{methods}->[0] ? @{$args{methods}} : keys %$http_methods; | 
|  | 2 |  |  |  |  | 8 |  | 
| 145 | 7 |  |  |  |  | 17 | my $methods_weight = $#methods; | 
| 146 |  |  |  |  |  |  |  | 
| 147 | 7 | 100 |  |  |  | 29 | my $sub_after_match = $args{match_callback} if ref $args{match_callback} eq 'CODE'; | 
| 148 |  |  |  |  |  |  |  | 
| 149 | 7 |  |  |  |  | 39 | my @depth = split '/',$args{connect},-1; | 
| 150 |  |  |  |  |  |  |  | 
| 151 | 7 |  |  |  |  | 17 | my $named_segment = {}; my $i = 0; | 
|  | 7 |  |  |  |  | 11 |  | 
| 152 |  |  |  |  |  |  |  | 
| 153 | 7 |  |  |  |  | 12 | my $res = []; | 
| 154 | 7 |  |  |  |  | 14 | for (@methods) { | 
| 155 | 33 |  | 100 |  |  | 183 | $self->{rule}->{$_}->{$#depth} ||= {}; | 
| 156 | 33 |  |  |  |  | 108 | push @$res, $self->{rule}->{$_}->{$#depth}; | 
| 157 |  |  |  |  |  |  | } | 
| 158 |  |  |  |  |  |  |  | 
| 159 | 7 |  |  |  |  | 100 | (my $tmp = $args{connect}) =~ s! | 
| 160 |  |  |  |  |  |  | (/)(?=/)                    | # double slash | 
| 161 |  |  |  |  |  |  | (/$)                        | # end slash | 
| 162 |  |  |  |  |  |  | /(:name\(["']?(.*?)["']?\))?:enum\(([^/]+)\)(?= $|/)   | # enum | 
| 163 |  |  |  |  |  |  | /(:name\(["']?(.*?)["']?\))?:re\(([^/]+)\)(?= $|/)     | # re | 
| 164 |  |  |  |  |  |  | /(:name\(["']?(.*?)["']?\))?(:any)(?= $|/)             | # any | 
| 165 |  |  |  |  |  |  | /(:name\(["']?(.*?)["']?\))?([^/]+)(?= $|/)              # eq | 
| 166 |  |  |  |  |  |  | ! | 
| 167 | 20 | 50 | 33 |  |  | 175 | if ($1 or $2) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 168 | 0 |  | 0 |  |  | 0 | $_->{exactly}->{''} ||= {} for @$res; | 
| 169 | 0 |  |  |  |  | 0 | $res = [map {$_->{exactly}->{''}} @$res]; | 
|  | 0 |  |  |  |  | 0 |  | 
| 170 |  |  |  |  |  |  | } elsif ($5) { | 
| 171 | 6 |  |  |  |  | 25 | my @val = split('\|',$5); | 
| 172 | 6 |  |  |  |  | 22 | my @tmp; | 
| 173 | 6 |  |  |  |  | 15 | for my $val (@val) { | 
| 174 | 12 |  |  |  |  | 23 | for (@$res) { | 
| 175 | 54 |  | 100 |  |  | 187 | $_->{exactly}->{$val} ||= {}; | 
| 176 | 54 |  |  |  |  | 156 | push @tmp, $_->{exactly}->{$val}; | 
| 177 |  |  |  |  |  |  | }; | 
| 178 |  |  |  |  |  |  | } | 
| 179 | 6 |  |  |  |  | 20 | $res = [@tmp]; | 
| 180 | 6 | 100 |  |  |  | 41 | $named_segment->{$i} = $4 if $4; | 
| 181 |  |  |  |  |  |  | } elsif ($8) { | 
| 182 | 1 |  |  |  |  | 43 | $self->{re_compile}->{$8} = qr{$8}s; | 
| 183 | 1 |  | 50 |  |  | 59 | $_->{regexp}->{$8} ||= {} for @$res; | 
| 184 | 1 |  |  |  |  | 3 | $res = [map {$_->{regexp}->{$8}} @$res]; | 
|  | 12 |  |  |  |  | 30 |  | 
| 185 | 1 | 50 |  |  |  | 9 | $named_segment->{$i} = $7 if $7; | 
| 186 |  |  |  |  |  |  | } elsif ($11) { | 
| 187 | 5 |  | 100 |  |  | 139 | $_->{default}->{''} ||= {} for @$res; | 
| 188 | 5 |  |  |  |  | 10 | $res = [map {$_->{default}->{''}} @$res]; | 
|  | 42 |  |  |  |  | 70 |  | 
| 189 | 5 | 100 |  |  |  | 24 | $named_segment->{$i} = $10 if $10; | 
| 190 |  |  |  |  |  |  | } elsif ($14) { | 
| 191 | 8 |  | 100 |  |  | 164 | $_->{exactly}->{$14} ||= {} for @$res; | 
| 192 | 8 |  |  |  |  | 18 | $res = [map {$_->{exactly}->{$14}} @$res]; | 
|  | 39 |  |  |  |  | 88 |  | 
| 193 | 8 | 50 |  |  |  | 30 | $named_segment->{$i} = $13 if $13; | 
| 194 |  |  |  |  |  |  | } else { | 
| 195 |  |  |  |  |  |  | # default as word | 
| 196 | 0 |  |  |  |  | 0 | croak "cant't resolve connect '$args{connect}'" | 
| 197 |  |  |  |  |  |  | } | 
| 198 | 20 |  |  |  |  | 142 | $i++; | 
| 199 |  |  |  |  |  |  | !gex; | 
| 200 |  |  |  |  |  |  |  | 
| 201 | 7 |  |  |  |  | 17 | for (@$res) { | 
| 202 | 60 | 50 | 66 |  |  | 162 | if (not $_->{match} or $_->{match}->[3] >= $methods_weight) { | 
| 203 |  |  |  |  |  |  | # set only if no match or a match for a more accurate description | 
| 204 | 60 | 100 |  |  |  | 359 | $_->{match} = [$args{action}, keys %$named_segment ? $named_segment : undef, $sub_after_match, $methods_weight]; | 
| 205 |  |  |  |  |  |  | } | 
| 206 |  |  |  |  |  |  | } | 
| 207 |  |  |  |  |  |  |  | 
| 208 | 7 |  |  |  |  | 58 | return 1; | 
| 209 |  |  |  |  |  |  | } | 
| 210 |  |  |  |  |  |  |  | 
| 211 |  |  |  |  |  |  | sub _match { | 
| 212 | 22 |  |  | 22 |  | 64 | my ($self, $reserch, $size_el, @el) = @_; | 
| 213 | 22 |  |  |  |  | 24 | my $ret; | 
| 214 | 22 |  |  |  |  | 27 | my $not_exactly = 0; | 
| 215 | 22 |  |  |  |  | 33 | my $segment = shift @el; | 
| 216 | 22 |  |  |  |  | 24 | $size_el--; | 
| 217 | 22 |  |  |  |  | 44 | my $exactly = $reserch->{exactly}->{$segment}; | 
| 218 | 22 | 100 |  |  |  | 51 | if (defined $exactly) { | 
| 219 | 14 | 100 |  |  |  | 82 | ($ret, $not_exactly) = $size_el ? $self->_match($exactly, $size_el, @el) : $exactly->{match}; | 
| 220 | 14 | 50 |  |  |  | 57 | return ($ret, $not_exactly) if $ret; | 
| 221 |  |  |  |  |  |  | }; | 
| 222 |  |  |  |  |  |  |  | 
| 223 | 8 | 100 |  |  |  | 35 | if ($reserch->{regexp}) { | 
| 224 | 1 |  |  |  |  | 4 | for (keys %{$reserch->{regexp}}) { | 
|  | 1 |  |  |  |  | 17 |  | 
| 225 | 1 | 50 |  |  |  | 9 | if ($segment =~ $self->{re_compile}->{$_}) { | 
| 226 | 1 | 50 |  |  |  | 5 | ($ret) = $size_el ? $self->_match($reserch->{regexp}->{$_}, $size_el, @el) : $reserch->{regexp}->{$_}->{match}; | 
| 227 | 1 | 50 |  |  |  | 7 | return ($ret, 1) if $ret; | 
| 228 |  |  |  |  |  |  | }; | 
| 229 |  |  |  |  |  |  | } | 
| 230 |  |  |  |  |  |  | }; | 
| 231 |  |  |  |  |  |  |  | 
| 232 | 7 | 100 |  |  |  | 21 | if ($reserch->{default}) { | 
| 233 | 5 | 50 |  |  |  | 17 | ($ret) = $size_el ? $self->_match($reserch->{default}->{''}, $size_el, @el) : $reserch->{default}->{''}->{match}; | 
| 234 | 5 | 50 |  |  |  | 35 | return ($ret, 1) if $ret; | 
| 235 |  |  |  |  |  |  | } | 
| 236 |  |  |  |  |  |  |  | 
| 237 | 2 |  |  |  |  | 8 | return; | 
| 238 |  |  |  |  |  |  | } | 
| 239 |  |  |  |  |  |  |  | 
| 240 |  |  |  |  |  |  | =head2 match({REQUEST_METHOD => ..., 'psgix.tmp.RouterPathInfo' => ...}) | 
| 241 |  |  |  |  |  |  |  | 
| 242 |  |  |  |  |  |  | Search match. See SYNOPSIS. | 
| 243 |  |  |  |  |  |  |  | 
| 244 |  |  |  |  |  |  | If a match is found, it returns hashref: | 
| 245 |  |  |  |  |  |  |  | 
| 246 |  |  |  |  |  |  | { | 
| 247 |  |  |  |  |  |  | type => 'controller', | 
| 248 |  |  |  |  |  |  | action => $action, | 
| 249 |  |  |  |  |  |  | name_segments => $arrayref | 
| 250 |  |  |  |  |  |  | } | 
| 251 |  |  |  |  |  |  |  | 
| 252 |  |  |  |  |  |  | Otherwise, undef. | 
| 253 |  |  |  |  |  |  |  | 
| 254 |  |  |  |  |  |  | =cut | 
| 255 |  |  |  |  |  |  | sub match { | 
| 256 | 9 |  |  | 9 | 1 | 2740 | my $self = shift; | 
| 257 | 9 |  |  |  |  | 16 | my $env = shift; | 
| 258 |  |  |  |  |  |  |  | 
| 259 | 9 |  |  |  |  | 18 | my $depth = $env->{'psgix.tmp.RouterPathInfo'}->{depth}; | 
| 260 |  |  |  |  |  |  |  | 
| 261 | 9 |  |  |  |  | 31 | my ($match, $not_exactly) = $self->_match( | 
| 262 |  |  |  |  |  |  | $self->{rule}->{$env->{REQUEST_METHOD}}->{$depth}, | 
| 263 |  |  |  |  |  |  | $depth, | 
| 264 | 9 |  |  |  |  | 33 | @{$env->{'psgix.tmp.RouterPathInfo'}->{segments}} | 
| 265 |  |  |  |  |  |  | ); | 
| 266 |  |  |  |  |  |  |  | 
| 267 | 9 | 100 |  |  |  | 26 | if ($match) { | 
| 268 | 7 |  |  |  |  | 22 | my $ret = { | 
| 269 |  |  |  |  |  |  | type => 'controller', | 
| 270 |  |  |  |  |  |  | action => $match->[0] | 
| 271 |  |  |  |  |  |  | }; | 
| 272 | 7 | 100 |  |  |  | 20 | unless ($match->[1]) { | 
| 273 | 5 |  |  |  |  | 11 | $ret->{name_segments} = {}; | 
| 274 |  |  |  |  |  |  | } else { | 
| 275 | 2 |  |  |  |  | 3 | $ret->{name_segments}->{$match->[1]->{$_}} = $env->{'psgix.tmp.RouterPathInfo'}->{segments}->[$_] for keys %{$match->[1]}; | 
|  | 2 |  |  |  |  | 22 |  | 
| 276 |  |  |  |  |  |  | } | 
| 277 | 7 | 100 |  |  |  | 24 | $ret->{_callback} = $match->[2] if $match->[2]; | 
| 278 | 7 |  |  |  |  | 22 | return ($not_exactly, $ret); | 
| 279 |  |  |  |  |  |  | } else { | 
| 280 | 2 |  |  |  |  | 7 | return; | 
| 281 |  |  |  |  |  |  | } | 
| 282 |  |  |  |  |  |  |  | 
| 283 |  |  |  |  |  |  | #    if ($match) { | 
| 284 |  |  |  |  |  |  | #    	my $ret = { | 
| 285 |  |  |  |  |  |  | #            type => 'controller', | 
| 286 |  |  |  |  |  |  | #            action => $match->[0], | 
| 287 |  |  |  |  |  |  | #            segment => $match->[1] ? [map {$env->{'psgix.tmp.RouterPathInfo'}->{segments}->[$_]} @{$match->[1]}] : [] | 
| 288 |  |  |  |  |  |  | #        }; | 
| 289 |  |  |  |  |  |  | #    	if ($match->[2]) { | 
| 290 |  |  |  |  |  |  | #    		return ($not_exactly, $match->[2]->($ret,$env)); | 
| 291 |  |  |  |  |  |  | #    	} else { | 
| 292 |  |  |  |  |  |  | #    		return ($not_exactly, $ret); | 
| 293 |  |  |  |  |  |  | #    	} | 
| 294 |  |  |  |  |  |  | #    } else { | 
| 295 |  |  |  |  |  |  | #    	return; | 
| 296 |  |  |  |  |  |  | #    } | 
| 297 |  |  |  |  |  |  |  | 
| 298 |  |  |  |  |  |  | } | 
| 299 |  |  |  |  |  |  |  | 
| 300 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 301 |  |  |  |  |  |  |  | 
| 302 |  |  |  |  |  |  | L, L | 
| 303 |  |  |  |  |  |  |  | 
| 304 |  |  |  |  |  |  | =head1 AUTHOR | 
| 305 |  |  |  |  |  |  |  | 
| 306 |  |  |  |  |  |  | mr.Rico | 
| 307 |  |  |  |  |  |  |  | 
| 308 |  |  |  |  |  |  | =cut | 
| 309 |  |  |  |  |  |  |  | 
| 310 |  |  |  |  |  |  | 1; | 
| 311 |  |  |  |  |  |  | __END__ |