| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  |  | 
| 2 |  |  |  |  |  |  | use Carp; | 
| 3 | 35 |  |  | 35 |  | 361416 |  | 
|  | 35 |  |  |  |  | 100 |  | 
|  | 35 |  |  |  |  | 1940 |  | 
| 4 |  |  |  |  |  |  | use Kelp::Base; | 
| 5 | 35 |  |  | 35 |  | 1484 | use Kelp::Routes::Pattern; | 
|  | 35 |  |  |  |  | 73 |  | 
|  | 35 |  |  |  |  | 161 |  | 
| 6 | 35 |  |  | 35 |  | 15287 | use Plack::Util; | 
|  | 35 |  |  |  |  | 81 |  | 
|  | 35 |  |  |  |  | 197 |  | 
| 7 | 35 |  |  | 35 |  | 1839 | use Class::Inspector; | 
|  | 35 |  |  |  |  | 42614 |  | 
|  | 35 |  |  |  |  | 798 |  | 
| 8 | 35 |  |  | 35 |  | 1829 |  | 
|  | 35 |  |  |  |  | 12081 |  | 
|  | 35 |  |  |  |  | 51065 |  | 
| 9 |  |  |  |  |  |  | attr base    => ''; | 
| 10 |  |  |  |  |  |  | attr routes  => sub { [] }; | 
| 11 |  |  |  |  |  |  | attr names   => sub { {} }; | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | # Cache | 
| 14 |  |  |  |  |  |  | attr _CACHE => sub { {} }; | 
| 15 |  |  |  |  |  |  | attr cache => sub { | 
| 16 |  |  |  |  |  |  | my $self = shift; | 
| 17 |  |  |  |  |  |  | Plack::Util::inline_object( | 
| 18 |  |  |  |  |  |  | get   => sub { $self->_CACHE->{ $_[0] } }, | 
| 19 |  |  |  |  |  |  | set   => sub { $self->_CACHE->{ $_[0] } = $_[1] }, | 
| 20 |  |  |  |  |  |  | clear => sub { $self->_CACHE( {} ) } | 
| 21 |  |  |  |  |  |  | ); | 
| 22 |  |  |  |  |  |  | }; | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | my ( $self, $pattern, $descr ) = @_; | 
| 25 |  |  |  |  |  |  | $self->_parse_route( {}, $pattern, $descr ); | 
| 26 | 178 |  |  | 178 | 1 | 597 | } | 
| 27 | 178 |  |  |  |  | 401 |  | 
| 28 |  |  |  |  |  |  | $_[0]->routes( [] ); | 
| 29 |  |  |  |  |  |  | $_[0]->cache->clear; | 
| 30 |  |  |  |  |  |  | $_[0]->names( {} ); | 
| 31 | 22 |  |  | 22 | 1 | 11976 | } | 
| 32 | 22 |  |  |  |  | 49 |  | 
| 33 | 22 |  |  |  |  | 56 | my ( $string, $base ) = @_; | 
| 34 |  |  |  |  |  |  | return $string unless $string; | 
| 35 |  |  |  |  |  |  | my @parts = split( /\#/, $string ); | 
| 36 |  |  |  |  |  |  | my $sub = pop @parts; | 
| 37 | 97 |  |  | 97 |  | 7780 | @parts = map { | 
| 38 | 97 | 100 |  |  |  | 185 | join '', map { ucfirst lc } split /\_/ | 
| 39 | 95 |  |  |  |  | 269 | } @parts; | 
| 40 | 95 |  |  |  |  | 150 | unshift @parts, $base if $base; | 
| 41 |  |  |  |  |  |  | return join( '::', @parts, $sub ); | 
| 42 | 95 |  |  |  |  | 163 | } | 
|  | 45 |  |  |  |  | 83 |  | 
|  | 61 |  |  |  |  | 239 |  | 
| 43 |  |  |  |  |  |  |  | 
| 44 | 95 | 100 |  |  |  | 194 | my ( $self, $parent, $key, $val ) = @_; | 
| 45 | 95 |  |  |  |  | 314 |  | 
| 46 |  |  |  |  |  |  | # Scalar, e.g. path => 'bar#foo' | 
| 47 |  |  |  |  |  |  | # CODE, e.g. path => sub { ... } | 
| 48 |  |  |  |  |  |  | if ( !ref($val) || ref($val) eq 'CODE' ) { | 
| 49 | 190 |  |  | 190 |  | 336 | $val = { to => $val }; | 
| 50 |  |  |  |  |  |  | } | 
| 51 |  |  |  |  |  |  |  | 
| 52 |  |  |  |  |  |  | # Sanity check | 
| 53 | 190 | 100 | 100 |  |  | 735 | if ( ref($val) ne 'HASH' ) { | 
| 54 | 148 |  |  |  |  | 316 | carp "Route description must be a SCALAR, CODE or HASH. Skipping."; | 
| 55 |  |  |  |  |  |  | return; | 
| 56 |  |  |  |  |  |  | } | 
| 57 |  |  |  |  |  |  |  | 
| 58 | 190 | 100 |  |  |  | 431 | # 'to' is required | 
| 59 | 1 |  |  |  |  | 76 | if ( !exists $val->{to} ) { | 
| 60 | 1 |  |  |  |  | 52 | carp "Route is missing destination. Skipping."; | 
| 61 |  |  |  |  |  |  | return; | 
| 62 |  |  |  |  |  |  | } | 
| 63 |  |  |  |  |  |  |  | 
| 64 | 189 | 100 |  |  |  | 375 | # Format destination | 
| 65 | 2 |  |  |  |  | 201 | if ( !ref $val->{to} ) { | 
| 66 | 2 |  |  |  |  | 109 | my $sigil = defined $val->{to} && $val->{to} =~ s/^(\+)// ? $1 : undef; | 
| 67 |  |  |  |  |  |  | $val->{to} = _camelize( $val->{to}, $sigil ? undef : $self->base ); | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  | # Load the class, if there is one and it is not 'main' | 
| 70 | 187 | 100 |  |  |  | 375 | if (   defined $val->{to} | 
| 71 | 72 | 100 | 100 |  |  | 344 | && $val->{to} =~ /^(.+)::(\w+)$/ | 
| 72 | 72 | 100 |  |  |  | 233 | && $1 ne 'main' | 
| 73 |  |  |  |  |  |  | && !Class::Inspector->loaded($1) ) { | 
| 74 |  |  |  |  |  |  | Plack::Util::load_class($1); | 
| 75 | 72 | 100 | 100 |  |  | 710 | } | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
| 76 |  |  |  |  |  |  | } | 
| 77 |  |  |  |  |  |  |  | 
| 78 |  |  |  |  |  |  | # Handle the value part | 
| 79 | 9 |  |  |  |  | 680 | if ( ref($key) eq 'ARRAY' ) { | 
| 80 |  |  |  |  |  |  | my ( $method, $pattern ) = @$key; | 
| 81 |  |  |  |  |  |  | if ( !grep { $method eq $_ } qw/GET POST PUT DELETE/ ) { | 
| 82 |  |  |  |  |  |  | carp "Using an odd method: $method"; | 
| 83 |  |  |  |  |  |  | } | 
| 84 | 186 | 100 |  |  |  | 3809 | $val->{method} = $method; | 
| 85 | 16 |  |  |  |  | 31 | $key = $pattern; | 
| 86 | 16 | 100 |  |  |  | 27 | } | 
|  | 64 |  |  |  |  | 113 |  | 
| 87 | 1 |  |  |  |  | 162 |  | 
| 88 |  |  |  |  |  |  | # Only SCALAR and Regexp allowed | 
| 89 | 16 |  |  |  |  | 114 | if ( ref($key) && ref($key) ne 'Regexp' ) { | 
| 90 | 16 |  |  |  |  | 26 | carp "Pattern $key can not be computed."; | 
| 91 |  |  |  |  |  |  | return; | 
| 92 |  |  |  |  |  |  | } | 
| 93 |  |  |  |  |  |  |  | 
| 94 | 186 | 100 | 100 |  |  | 442 | $val->{pattern} = $key; | 
| 95 | 2 |  |  |  |  | 145 |  | 
| 96 | 2 |  |  |  |  | 98 | my $tree; | 
| 97 |  |  |  |  |  |  | if ( $tree = delete $val->{tree} ) { | 
| 98 |  |  |  |  |  |  | if ( ref($tree) ne 'ARRAY' ) { | 
| 99 | 184 |  |  |  |  | 313 | carp "Tree must be an ARRAY. Skipping."; | 
| 100 |  |  |  |  |  |  | $tree = undef; | 
| 101 | 184 |  |  |  |  | 224 | } | 
| 102 | 184 | 100 |  |  |  | 386 | else { | 
| 103 | 7 | 50 |  |  |  | 21 | $val->{bridge} = 1; | 
| 104 | 0 |  |  |  |  | 0 | } | 
| 105 | 0 |  |  |  |  | 0 | } | 
| 106 |  |  |  |  |  |  | $tree //= []; | 
| 107 |  |  |  |  |  |  |  | 
| 108 | 7 |  |  |  |  | 15 | # Parrent defined? | 
| 109 |  |  |  |  |  |  | if (%$parent) { | 
| 110 |  |  |  |  |  |  | if ( $val->{name} && $parent->{name} ) { | 
| 111 | 184 |  | 100 |  |  | 657 | $val->{name} = $parent->{name} . '_' . $val->{name}; | 
| 112 |  |  |  |  |  |  | } | 
| 113 |  |  |  |  |  |  | $val->{pattern} = $parent->{pattern} . $val->{pattern}; | 
| 114 | 184 | 100 |  |  |  | 345 | } | 
| 115 | 12 | 100 | 100 |  |  | 78 |  | 
| 116 | 7 |  |  |  |  | 18 | # Create pattern object | 
| 117 |  |  |  |  |  |  | push @{ $self->routes }, Kelp::Routes::Pattern->new(%$val); | 
| 118 | 12 |  |  |  |  | 27 |  | 
| 119 |  |  |  |  |  |  | # Add route index to names | 
| 120 |  |  |  |  |  |  | if ( my $name = $val->{name} ) { | 
| 121 |  |  |  |  |  |  | if ( exists $self->names->{$name} ) { | 
| 122 | 184 |  |  |  |  | 252 | carp "Redefining route name $name"; | 
|  | 184 |  |  |  |  | 411 |  | 
| 123 |  |  |  |  |  |  | } | 
| 124 |  |  |  |  |  |  | $self->names->{$name} = scalar( @{ $self->routes } ) - 1; | 
| 125 | 184 | 100 |  |  |  | 560 | } | 
| 126 | 19 | 50 |  |  |  | 47 |  | 
| 127 | 0 |  |  |  |  | 0 | while (@$tree) { | 
| 128 |  |  |  |  |  |  | my ( $k, $v ) = splice( @$tree, 0, 2 ); | 
| 129 | 19 |  |  |  |  | 25 | $self->_parse_route( $val, $k, $v ); | 
|  | 19 |  |  |  |  | 33 |  | 
| 130 |  |  |  |  |  |  | } | 
| 131 |  |  |  |  |  |  | } | 
| 132 | 184 |  |  |  |  | 843 |  | 
| 133 | 12 |  |  |  |  | 31 | my $self = shift; | 
| 134 | 12 |  |  |  |  | 70 | my $name = shift // die "Route name is missing"; | 
| 135 |  |  |  |  |  |  | my %args = @_ == 1 ? %{ $_[0] } : @_; | 
| 136 |  |  |  |  |  |  |  | 
| 137 |  |  |  |  |  |  | return $name unless exists $self->names->{$name}; | 
| 138 |  |  |  |  |  |  | my $route = $self->routes->[ $self->names->{$name} ]; | 
| 139 | 14 |  |  | 14 | 1 | 25 | return $route->build(%args); | 
| 140 | 14 |  | 50 |  |  | 31 | } | 
| 141 | 14 | 50 |  |  |  | 38 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 142 |  |  |  |  |  |  | my ( $self, $path, $method ) = @_; | 
| 143 | 14 | 100 |  |  |  | 38 |  | 
| 144 | 9 |  |  |  |  | 20 | # Look for this path and method in the cache. If found, | 
| 145 | 9 |  |  |  |  | 51 | # return the array of routes that matched the previous time. | 
| 146 |  |  |  |  |  |  | # If not found, then return all routes. | 
| 147 |  |  |  |  |  |  | my $key = $path . ':' . ( $method // '' ); | 
| 148 |  |  |  |  |  |  | my $routes = $self->cache->get($key) // $self->routes; | 
| 149 | 238 |  |  | 238 | 1 | 20156 |  | 
| 150 |  |  |  |  |  |  | # Look through all routes, grep the ones that match | 
| 151 |  |  |  |  |  |  | # and sort them by 'bridge' and 'pattern' | 
| 152 |  |  |  |  |  |  | my @processed = | 
| 153 |  |  |  |  |  |  | sort { $b->bridge <=> $a->bridge || $a->pattern cmp $b->pattern } | 
| 154 | 238 |  | 100 |  |  | 729 | grep { $_->match( $path, $method ) } @$routes; | 
| 155 | 238 |  | 66 |  |  | 524 |  | 
| 156 |  |  |  |  |  |  | my $value = \@processed; | 
| 157 |  |  |  |  |  |  | $self->cache->set( $key, $value ); | 
| 158 |  |  |  |  |  |  | return $value; | 
| 159 |  |  |  |  |  |  | } | 
| 160 | 15 | 50 |  |  |  | 31 |  | 
| 161 | 238 |  |  |  |  | 537 | my ( $self, $app, $route ) = @_; | 
|  | 1108 |  |  |  |  | 2087 |  | 
| 162 |  |  |  |  |  |  | $app   || die "Application instance required"; | 
| 163 | 238 |  |  |  |  | 384 | $route || die "No route pattern instance supplied"; | 
| 164 | 238 |  |  |  |  | 523 |  | 
| 165 | 238 |  |  |  |  | 657 | # Shortcuts | 
| 166 |  |  |  |  |  |  | my $req = $app->req; | 
| 167 |  |  |  |  |  |  | my $to  = $route->to; | 
| 168 |  |  |  |  |  |  |  | 
| 169 | 193 |  |  | 193 | 1 | 334 | # Destination must be either a scalar, or a code reference | 
| 170 | 193 | 50 |  |  |  | 379 | if ( !$to || ref $to && ref $to ne 'CODE' ) { | 
| 171 | 193 | 50 |  |  |  | 342 | die 'Invalid destination for ' . $req->path; | 
| 172 |  |  |  |  |  |  | } | 
| 173 |  |  |  |  |  |  |  | 
| 174 | 193 |  |  |  |  | 332 | # If the destination is not a code reference, then we assume it's | 
| 175 | 193 |  |  |  |  | 388 | # a fully qualified function name, so we find its reference | 
| 176 |  |  |  |  |  |  | unless ( ref $to ) { | 
| 177 |  |  |  |  |  |  |  | 
| 178 | 193 | 100 | 100 |  |  | 915 | # Check if the destination function exists | 
|  |  |  | 100 |  |  |  |  | 
| 179 | 3 |  |  |  |  | 7 | unless ( exists &$to ) { | 
| 180 |  |  |  |  |  |  | die sprintf( 'Route not found %s for %s', $to, $req->path ); | 
| 181 |  |  |  |  |  |  | } | 
| 182 |  |  |  |  |  |  |  | 
| 183 |  |  |  |  |  |  | # Move to reference | 
| 184 | 190 | 100 |  |  |  | 410 | $to = \&{$to}; | 
| 185 |  |  |  |  |  |  | } | 
| 186 |  |  |  |  |  |  |  | 
| 187 | 22 | 100 |  |  |  | 93 | return $to->( $app, @{ $route->param } ); | 
| 188 | 3 |  |  |  |  | 8 | } | 
| 189 |  |  |  |  |  |  |  | 
| 190 |  |  |  |  |  |  | 1; | 
| 191 |  |  |  |  |  |  |  | 
| 192 | 19 |  |  |  |  | 29 |  | 
|  | 19 |  |  |  |  | 47 |  | 
| 193 |  |  |  |  |  |  | =pod | 
| 194 |  |  |  |  |  |  |  | 
| 195 | 187 |  |  |  |  | 230 | =head1 NAME | 
|  | 187 |  |  |  |  | 311 |  | 
| 196 |  |  |  |  |  |  |  | 
| 197 |  |  |  |  |  |  | Kelp::Routes - Routing for a Kelp app | 
| 198 |  |  |  |  |  |  |  | 
| 199 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 200 |  |  |  |  |  |  |  | 
| 201 |  |  |  |  |  |  | use Kelp::Routes; | 
| 202 |  |  |  |  |  |  | my $r = Kelp::Routes->new( base => 'MyApp' ); | 
| 203 |  |  |  |  |  |  | $r->add( '/home', 'home' ); | 
| 204 |  |  |  |  |  |  |  | 
| 205 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 206 |  |  |  |  |  |  |  | 
| 207 |  |  |  |  |  |  | The router provides the connection between the HTTP requests and the web | 
| 208 |  |  |  |  |  |  | application code. It tells the application I<"If you see a request coming to | 
| 209 |  |  |  |  |  |  | *this* URI, send it to *that* subroutine for processing">. For example, if a | 
| 210 |  |  |  |  |  |  | request comes to C</home>, then send it to C<sub home> in the current | 
| 211 |  |  |  |  |  |  | namespace. The process of capturing URIs and sending them to their corresponding | 
| 212 |  |  |  |  |  |  | code is called routing. | 
| 213 |  |  |  |  |  |  |  | 
| 214 |  |  |  |  |  |  | This router was specifically crafted as part of the C<Kelp> web framework. It | 
| 215 |  |  |  |  |  |  | is, however, possible to use it on its own, if needed. | 
| 216 |  |  |  |  |  |  |  | 
| 217 |  |  |  |  |  |  | It provides a simple, yet sophisticated routing utilizing Perl 5.10's | 
| 218 |  |  |  |  |  |  | regular expressions, which makes it fast, robust and reliable. | 
| 219 |  |  |  |  |  |  |  | 
| 220 |  |  |  |  |  |  | The routing process can roughly be broken down into three steps: | 
| 221 |  |  |  |  |  |  |  | 
| 222 |  |  |  |  |  |  | =over | 
| 223 |  |  |  |  |  |  |  | 
| 224 |  |  |  |  |  |  | =item B<Adding routes> | 
| 225 |  |  |  |  |  |  |  | 
| 226 |  |  |  |  |  |  | First you create a router object: | 
| 227 |  |  |  |  |  |  |  | 
| 228 |  |  |  |  |  |  | my $r = Kelp::Routes->new(); | 
| 229 |  |  |  |  |  |  |  | 
| 230 |  |  |  |  |  |  | Then you add your application's routes and their descriptions: | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | $r->add( '/path' => 'Module::function' ); | 
| 233 |  |  |  |  |  |  | ... | 
| 234 |  |  |  |  |  |  |  | 
| 235 |  |  |  |  |  |  | =item B<Matching> | 
| 236 |  |  |  |  |  |  |  | 
| 237 |  |  |  |  |  |  | Once you have your routes added, you can match with the L</match> subroutine. | 
| 238 |  |  |  |  |  |  |  | 
| 239 |  |  |  |  |  |  | $r->match( $path, $method ); | 
| 240 |  |  |  |  |  |  |  | 
| 241 |  |  |  |  |  |  | The Kelp framework already does matching for you, so you may never | 
| 242 |  |  |  |  |  |  | have to do your own matching. The above example is provided only for | 
| 243 |  |  |  |  |  |  | reference. | 
| 244 |  |  |  |  |  |  |  | 
| 245 |  |  |  |  |  |  | =item B<Building URLs from routes> | 
| 246 |  |  |  |  |  |  |  | 
| 247 |  |  |  |  |  |  | You can name each of your routes, and use that name later to build a URL: | 
| 248 |  |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  | $r->add( '/begin' => { to => 'function', name => 'home' } ); | 
| 250 |  |  |  |  |  |  | my $url = $r->url('home');    # /begin | 
| 251 |  |  |  |  |  |  |  | 
| 252 |  |  |  |  |  |  | This can be used in views and other places where you need the full URL of | 
| 253 |  |  |  |  |  |  | a route. | 
| 254 |  |  |  |  |  |  |  | 
| 255 |  |  |  |  |  |  | =back | 
| 256 |  |  |  |  |  |  |  | 
| 257 |  |  |  |  |  |  | =head1 PLACEHOLDERS | 
| 258 |  |  |  |  |  |  |  | 
| 259 |  |  |  |  |  |  | Often routes may get more complicated. They may contain variable parts. For | 
| 260 |  |  |  |  |  |  | example this one C</user/1000> is expected to do something with user ID 1000. | 
| 261 |  |  |  |  |  |  | So, in this case we need to capture a route that begins with C</user/> and then | 
| 262 |  |  |  |  |  |  | has something else after it. | 
| 263 |  |  |  |  |  |  |  | 
| 264 |  |  |  |  |  |  | Naturally, when it comes to capturing routes, the first instinct of the Perl | 
| 265 |  |  |  |  |  |  | programmer is to use regular expressions, like this: | 
| 266 |  |  |  |  |  |  |  | 
| 267 |  |  |  |  |  |  | qr{/user/(\d+)} -> "sub home" | 
| 268 |  |  |  |  |  |  |  | 
| 269 |  |  |  |  |  |  | This module will let you do that, however regular expressions can get very | 
| 270 |  |  |  |  |  |  | complicated, and it won't be long before you lose track of what does what. | 
| 271 |  |  |  |  |  |  |  | 
| 272 |  |  |  |  |  |  | This is why a good router (this one included) allows for I<named placeholders>. | 
| 273 |  |  |  |  |  |  | These are words prefixed with special symbols, which denote a variable piece in | 
| 274 |  |  |  |  |  |  | the URI. To use the above example: | 
| 275 |  |  |  |  |  |  |  | 
| 276 |  |  |  |  |  |  | "/user/:id" -> "sub home" | 
| 277 |  |  |  |  |  |  |  | 
| 278 |  |  |  |  |  |  | It looks a little cleaner. | 
| 279 |  |  |  |  |  |  |  | 
| 280 |  |  |  |  |  |  | Placeholders are variables you place in the route path. They are identified by | 
| 281 |  |  |  |  |  |  | a prefix character and their names must abide to the rules of a regular Perl | 
| 282 |  |  |  |  |  |  | variable. If necessary, curly braces can be used to separate placeholders from | 
| 283 |  |  |  |  |  |  | the rest of the path. | 
| 284 |  |  |  |  |  |  |  | 
| 285 |  |  |  |  |  |  | There are three types of place holders: | 
| 286 |  |  |  |  |  |  |  | 
| 287 |  |  |  |  |  |  | =head2 Explicit | 
| 288 |  |  |  |  |  |  |  | 
| 289 |  |  |  |  |  |  | These placeholders begin with a column (C<:>) and must have a value in order for the | 
| 290 |  |  |  |  |  |  | route to match. All characters are matched, except for the forward slash. | 
| 291 |  |  |  |  |  |  |  | 
| 292 |  |  |  |  |  |  | $r->add( '/user/:id' => 'Module::sub' ); | 
| 293 |  |  |  |  |  |  | # /user/a       -> match (id = 'a') | 
| 294 |  |  |  |  |  |  | # /user/123     -> match (id = 123) | 
| 295 |  |  |  |  |  |  | # /user/        -> no match | 
| 296 |  |  |  |  |  |  | # /user         -> no match | 
| 297 |  |  |  |  |  |  | # /user/10/foo  -> no match | 
| 298 |  |  |  |  |  |  |  | 
| 299 |  |  |  |  |  |  | $r->add( '/page/:page/line/:line' => 'Module::sub' ); | 
| 300 |  |  |  |  |  |  | # /page/1/line/2        -> match (page = 1, line = 2) | 
| 301 |  |  |  |  |  |  | # /page/bar/line/foo    -> match (page = 'bar', line = 'foo') | 
| 302 |  |  |  |  |  |  | # /page/line/4          -> no match | 
| 303 |  |  |  |  |  |  | # /page/5               -> no match | 
| 304 |  |  |  |  |  |  |  | 
| 305 |  |  |  |  |  |  | $r->add( '/{:a}ing/{:b}ing' => 'Module::sub' ); | 
| 306 |  |  |  |  |  |  | # /walking/singing      -> match (a = 'walk', b = 'sing') | 
| 307 |  |  |  |  |  |  | # /cooking/ing          -> no match | 
| 308 |  |  |  |  |  |  | # /ing/ing              -> no match | 
| 309 |  |  |  |  |  |  |  | 
| 310 |  |  |  |  |  |  | =head2 Optional | 
| 311 |  |  |  |  |  |  |  | 
| 312 |  |  |  |  |  |  | Optional placeholders begin with a question mark C<?> and denote an optional | 
| 313 |  |  |  |  |  |  | value. You may also specify a default value for the optional placeholder via | 
| 314 |  |  |  |  |  |  | the L</defaults> option. Again, like the explicit placeholders, the optional | 
| 315 |  |  |  |  |  |  | ones capture all characters, except the forward slash. | 
| 316 |  |  |  |  |  |  |  | 
| 317 |  |  |  |  |  |  | $r->add( '/data/?id' => 'Module::sub' ); | 
| 318 |  |  |  |  |  |  | # /bar/foo          -> match ( id = 'foo' ) | 
| 319 |  |  |  |  |  |  | # /bar/             -> match ( id = undef ) | 
| 320 |  |  |  |  |  |  | # /bar              -> match ( id = undef ) | 
| 321 |  |  |  |  |  |  |  | 
| 322 |  |  |  |  |  |  | $r->add( '/:a/?b/:c' => 'Module::sub' ); | 
| 323 |  |  |  |  |  |  | # /bar/foo/baz      -> match ( a = 'bar', b = 'foo', c = 'baz' ) | 
| 324 |  |  |  |  |  |  | # /bar/foo          -> match ( a = 'bar', b = undef, c = 'foo' ) | 
| 325 |  |  |  |  |  |  | # /bar              -> no match | 
| 326 |  |  |  |  |  |  | # /bar/foo/baz/moo  -> no match | 
| 327 |  |  |  |  |  |  |  | 
| 328 |  |  |  |  |  |  | Optional default values may be specified via the C<defaults> option. | 
| 329 |  |  |  |  |  |  |  | 
| 330 |  |  |  |  |  |  | $r->add( | 
| 331 |  |  |  |  |  |  | '/user/?name' => { | 
| 332 |  |  |  |  |  |  | to       => 'Module::sub', | 
| 333 |  |  |  |  |  |  | defaults => { name => 'hank' } | 
| 334 |  |  |  |  |  |  | } | 
| 335 |  |  |  |  |  |  | ); | 
| 336 |  |  |  |  |  |  |  | 
| 337 |  |  |  |  |  |  | # /user             -> match ( name = 'hank' ) | 
| 338 |  |  |  |  |  |  | # /user/            -> match ( name = 'hank' ) | 
| 339 |  |  |  |  |  |  | # /user/jane        -> match ( name = 'jane' ) | 
| 340 |  |  |  |  |  |  | # /user/jane/cho    -> no match | 
| 341 |  |  |  |  |  |  |  | 
| 342 |  |  |  |  |  |  | =head2 Wildcards | 
| 343 |  |  |  |  |  |  |  | 
| 344 |  |  |  |  |  |  | The wildcard placeholders expect a value and capture all characters, including | 
| 345 |  |  |  |  |  |  | the forward slash. | 
| 346 |  |  |  |  |  |  |  | 
| 347 |  |  |  |  |  |  | $r->add( '/:a/*b/:c'  => 'Module::sub' ); | 
| 348 |  |  |  |  |  |  | # /bar/foo/baz/bat  -> match ( a = 'bar', b = 'foo/baz', c = 'bat' ) | 
| 349 |  |  |  |  |  |  | # /bar/bat          -> no match | 
| 350 |  |  |  |  |  |  |  | 
| 351 |  |  |  |  |  |  | =head2 Using curly braces | 
| 352 |  |  |  |  |  |  |  | 
| 353 |  |  |  |  |  |  | Curly braces may be used to separate the placeholders from the rest of the | 
| 354 |  |  |  |  |  |  | path: | 
| 355 |  |  |  |  |  |  |  | 
| 356 |  |  |  |  |  |  | $r->add( '/{:a}ing/{:b}ing' => 'Module::sub' ); | 
| 357 |  |  |  |  |  |  | # /looking/seeing       -> match ( a = 'look', b = 'see' ) | 
| 358 |  |  |  |  |  |  | # /ing/ing              -> no match | 
| 359 |  |  |  |  |  |  |  | 
| 360 |  |  |  |  |  |  | $r->add( '/:a/{?b}ing' => 'Module::sub' ); | 
| 361 |  |  |  |  |  |  | # /bar/hopping          -> match ( a = 'bar', b = 'hopp' ) | 
| 362 |  |  |  |  |  |  | # /bar/ing              -> match ( a = 'bar' ) | 
| 363 |  |  |  |  |  |  | # /bar                  -> no match | 
| 364 |  |  |  |  |  |  |  | 
| 365 |  |  |  |  |  |  | $r->add( '/:a/{*b}ing/:c' => 'Module::sub' ); | 
| 366 |  |  |  |  |  |  | # /bar/hop/ping/foo     -> match ( a = 'bar', b = 'hop/p', c = 'foo' ) | 
| 367 |  |  |  |  |  |  | # /bar/ing/foo          -> no match | 
| 368 |  |  |  |  |  |  |  | 
| 369 |  |  |  |  |  |  | =head1 BRIDGES | 
| 370 |  |  |  |  |  |  |  | 
| 371 |  |  |  |  |  |  | The L</match> subroutine will stop and return the route that best matches the | 
| 372 |  |  |  |  |  |  | specified path. If that route is marked as a bridge, then L</match> will | 
| 373 |  |  |  |  |  |  | continue looking for another match, and will eventually return an array of one or | 
| 374 |  |  |  |  |  |  | more routes. Bridges can be used for authentication or other route preprocessing. | 
| 375 |  |  |  |  |  |  |  | 
| 376 |  |  |  |  |  |  | $r->add( '/users/*', { to => 'Users::auth', bridge => 1 } ); | 
| 377 |  |  |  |  |  |  | $r->add( '/users/:action' => 'Users::dispatch' ); | 
| 378 |  |  |  |  |  |  |  | 
| 379 |  |  |  |  |  |  | The above example will require F</users/profile> to go through two | 
| 380 |  |  |  |  |  |  | subroutines: C<Users::auth> and C<Users::dispatch>: | 
| 381 |  |  |  |  |  |  |  | 
| 382 |  |  |  |  |  |  | my $arr = $r->match('/users/view'); | 
| 383 |  |  |  |  |  |  | # $arr is an array of two routes now, the bridge and the last one matched | 
| 384 |  |  |  |  |  |  |  | 
| 385 |  |  |  |  |  |  | =head1 TREES | 
| 386 |  |  |  |  |  |  |  | 
| 387 |  |  |  |  |  |  | A quick way to add bridges is to use the L</tree> option. It allows you to | 
| 388 |  |  |  |  |  |  | define all routes under a bridge. Example: | 
| 389 |  |  |  |  |  |  |  | 
| 390 |  |  |  |  |  |  | $r->add( | 
| 391 |  |  |  |  |  |  | '/users/*' => { | 
| 392 |  |  |  |  |  |  | to   => 'users#auth', | 
| 393 |  |  |  |  |  |  | name => 'users', | 
| 394 |  |  |  |  |  |  | tree => [ | 
| 395 |  |  |  |  |  |  | '/profile' => { | 
| 396 |  |  |  |  |  |  | name => 'profile', | 
| 397 |  |  |  |  |  |  | to   => 'users#profile' | 
| 398 |  |  |  |  |  |  | }, | 
| 399 |  |  |  |  |  |  | '/settings' => { | 
| 400 |  |  |  |  |  |  | name => 'settings', | 
| 401 |  |  |  |  |  |  | to   => 'users#settings', | 
| 402 |  |  |  |  |  |  | tree => [ | 
| 403 |  |  |  |  |  |  | '/email' => { name => 'email', to => 'users#email' }, | 
| 404 |  |  |  |  |  |  | '/login' => { name => 'login', to => 'users#login' } | 
| 405 |  |  |  |  |  |  | ] | 
| 406 |  |  |  |  |  |  | } | 
| 407 |  |  |  |  |  |  | ] | 
| 408 |  |  |  |  |  |  | } | 
| 409 |  |  |  |  |  |  | ); | 
| 410 |  |  |  |  |  |  |  | 
| 411 |  |  |  |  |  |  | The above call to C<add> causes the following to occur under the hood: | 
| 412 |  |  |  |  |  |  |  | 
| 413 |  |  |  |  |  |  | =over | 
| 414 |  |  |  |  |  |  |  | 
| 415 |  |  |  |  |  |  | =item | 
| 416 |  |  |  |  |  |  |  | 
| 417 |  |  |  |  |  |  | The paths of all routes inside the tree are joined to the path of their | 
| 418 |  |  |  |  |  |  | parent, so the following five new routes are created: | 
| 419 |  |  |  |  |  |  |  | 
| 420 |  |  |  |  |  |  | /users                  -> MyApp::Users::auth | 
| 421 |  |  |  |  |  |  | /users/profile          -> MyApp::Users::profile | 
| 422 |  |  |  |  |  |  | /users/settings         -> MyApp::Users::settings | 
| 423 |  |  |  |  |  |  | /users/settings/email   -> MyApp::Users::email | 
| 424 |  |  |  |  |  |  | /users/settings/login   -> MyApp::Users::login | 
| 425 |  |  |  |  |  |  |  | 
| 426 |  |  |  |  |  |  | =item | 
| 427 |  |  |  |  |  |  |  | 
| 428 |  |  |  |  |  |  | The names of the routes are joined via C<_> with the name of their parent: | 
| 429 |  |  |  |  |  |  |  | 
| 430 |  |  |  |  |  |  | /users                  -> 'users' | 
| 431 |  |  |  |  |  |  | /users/profile          -> 'users_profile' | 
| 432 |  |  |  |  |  |  | /users/settings         -> 'users_settings' | 
| 433 |  |  |  |  |  |  | /users/settings/email   -> 'users_settings_email' | 
| 434 |  |  |  |  |  |  | /users/settings/login   -> 'users_settings_login' | 
| 435 |  |  |  |  |  |  |  | 
| 436 |  |  |  |  |  |  | =item | 
| 437 |  |  |  |  |  |  |  | 
| 438 |  |  |  |  |  |  | The C</users> and C</users/settings> routes are automatically marked as | 
| 439 |  |  |  |  |  |  | bridges, because they contain a tree. | 
| 440 |  |  |  |  |  |  |  | 
| 441 |  |  |  |  |  |  | =back | 
| 442 |  |  |  |  |  |  |  | 
| 443 |  |  |  |  |  |  | =head1 ATTRIBUTES | 
| 444 |  |  |  |  |  |  |  | 
| 445 |  |  |  |  |  |  | =head2 base | 
| 446 |  |  |  |  |  |  |  | 
| 447 |  |  |  |  |  |  | Sets the base class for the routes destinations. | 
| 448 |  |  |  |  |  |  |  | 
| 449 |  |  |  |  |  |  | my $r = Kelp::Routes->new( base => 'MyApp' ); | 
| 450 |  |  |  |  |  |  |  | 
| 451 |  |  |  |  |  |  | This will prepend C<MyApp::> to all route destinations. | 
| 452 |  |  |  |  |  |  |  | 
| 453 |  |  |  |  |  |  | $r->add( '/home' => 'home' );          # /home -> MyApp::home | 
| 454 |  |  |  |  |  |  | $r->add( '/user' => 'user#home' );     # /user -> MyApp::User::home | 
| 455 |  |  |  |  |  |  | $r->add( '/view' => 'User::view' );    # /view -> MyApp::User::view | 
| 456 |  |  |  |  |  |  |  | 
| 457 |  |  |  |  |  |  | A Kelp application will automatically set this value to the name of the main | 
| 458 |  |  |  |  |  |  | class. If you need to use a route located in another package, you must prefix | 
| 459 |  |  |  |  |  |  | it with a plus sign: | 
| 460 |  |  |  |  |  |  |  | 
| 461 |  |  |  |  |  |  | # Problem: | 
| 462 |  |  |  |  |  |  |  | 
| 463 |  |  |  |  |  |  | $r->add( '/outside' => 'Outside::Module::route' ); | 
| 464 |  |  |  |  |  |  | # /outside -> MyApp::Outside::Module::route | 
| 465 |  |  |  |  |  |  | # (most likely not what you want) | 
| 466 |  |  |  |  |  |  |  | 
| 467 |  |  |  |  |  |  | # Solution: | 
| 468 |  |  |  |  |  |  |  | 
| 469 |  |  |  |  |  |  | $r->add( '/outside' => '+Outside::Module::route' ); | 
| 470 |  |  |  |  |  |  | # /outside -> Outside::Module::route | 
| 471 |  |  |  |  |  |  |  | 
| 472 |  |  |  |  |  |  | =head2 cache | 
| 473 |  |  |  |  |  |  |  | 
| 474 |  |  |  |  |  |  | Routes will be cached in memory, so repeating requests will be dispatched much | 
| 475 |  |  |  |  |  |  | faster. The C<cache> attribute can optionally be initialized with an instance of | 
| 476 |  |  |  |  |  |  | a caching module with interface similar to L<CHI> and L<Cache>. | 
| 477 |  |  |  |  |  |  | The module interface should at the very least provide the following methods: | 
| 478 |  |  |  |  |  |  |  | 
| 479 |  |  |  |  |  |  | =head3 get($key) | 
| 480 |  |  |  |  |  |  |  | 
| 481 |  |  |  |  |  |  | retrieve a key from the cache | 
| 482 |  |  |  |  |  |  |  | 
| 483 |  |  |  |  |  |  | =head3 set($key, $value, $expiration) | 
| 484 |  |  |  |  |  |  |  | 
| 485 |  |  |  |  |  |  | set a key in the cache | 
| 486 |  |  |  |  |  |  |  | 
| 487 |  |  |  |  |  |  | =head3 clear() | 
| 488 |  |  |  |  |  |  |  | 
| 489 |  |  |  |  |  |  | clear all cache | 
| 490 |  |  |  |  |  |  |  | 
| 491 |  |  |  |  |  |  | The caching module should be initialized in the config file: | 
| 492 |  |  |  |  |  |  |  | 
| 493 |  |  |  |  |  |  | # config.pl | 
| 494 |  |  |  |  |  |  | { | 
| 495 |  |  |  |  |  |  | modules_init => { | 
| 496 |  |  |  |  |  |  | Routes => { | 
| 497 |  |  |  |  |  |  | cache => Cache::Memory->new( | 
| 498 |  |  |  |  |  |  | namespace       => 'MyApp', | 
| 499 |  |  |  |  |  |  | default_expires => '3600 sec' | 
| 500 |  |  |  |  |  |  | ); | 
| 501 |  |  |  |  |  |  | } | 
| 502 |  |  |  |  |  |  | } | 
| 503 |  |  |  |  |  |  | } | 
| 504 |  |  |  |  |  |  |  | 
| 505 |  |  |  |  |  |  | =head1 SUBROUTINES | 
| 506 |  |  |  |  |  |  |  | 
| 507 |  |  |  |  |  |  | =head2 add | 
| 508 |  |  |  |  |  |  |  | 
| 509 |  |  |  |  |  |  | Adds a new route definition to the routes array. | 
| 510 |  |  |  |  |  |  |  | 
| 511 |  |  |  |  |  |  | $r->add( $path, $destination ); | 
| 512 |  |  |  |  |  |  |  | 
| 513 |  |  |  |  |  |  | C<$path> can be a path string, e.g. C<'/user/view'> or an ARRAY containing a | 
| 514 |  |  |  |  |  |  | method and a path, e.g. C<[ PUT =E<gt> '/item' ]>. | 
| 515 |  |  |  |  |  |  |  | 
| 516 |  |  |  |  |  |  | The route destination is very flexible. It can be one of these three things: | 
| 517 |  |  |  |  |  |  |  | 
| 518 |  |  |  |  |  |  | =over | 
| 519 |  |  |  |  |  |  |  | 
| 520 |  |  |  |  |  |  | =item | 
| 521 |  |  |  |  |  |  |  | 
| 522 |  |  |  |  |  |  | A string name of a subroutine, for example C<"Users::item">. Using a C<#> sign | 
| 523 |  |  |  |  |  |  | to replace C<::> is also allowed, in which case the name will get converted. | 
| 524 |  |  |  |  |  |  | C<"users#item"> becomes C<"Users::item">. | 
| 525 |  |  |  |  |  |  |  | 
| 526 |  |  |  |  |  |  | $r->add( '/home' => 'user#home' ); | 
| 527 |  |  |  |  |  |  |  | 
| 528 |  |  |  |  |  |  | =item | 
| 529 |  |  |  |  |  |  |  | 
| 530 |  |  |  |  |  |  | A code reference. | 
| 531 |  |  |  |  |  |  |  | 
| 532 |  |  |  |  |  |  | $r->add( '/system' => sub { return \%ENV } ); | 
| 533 |  |  |  |  |  |  |  | 
| 534 |  |  |  |  |  |  | =item | 
| 535 |  |  |  |  |  |  |  | 
| 536 |  |  |  |  |  |  | A hashref with options. | 
| 537 |  |  |  |  |  |  |  | 
| 538 |  |  |  |  |  |  | # GET /item/100 -> MyApp::Items::view | 
| 539 |  |  |  |  |  |  | $r->add( | 
| 540 |  |  |  |  |  |  | '/item/:id', { | 
| 541 |  |  |  |  |  |  | to     => 'items#view', | 
| 542 |  |  |  |  |  |  | method => 'GET' | 
| 543 |  |  |  |  |  |  | } | 
| 544 |  |  |  |  |  |  | ); | 
| 545 |  |  |  |  |  |  |  | 
| 546 |  |  |  |  |  |  | See L</Destination Options> for details. | 
| 547 |  |  |  |  |  |  |  | 
| 548 |  |  |  |  |  |  | =back | 
| 549 |  |  |  |  |  |  |  | 
| 550 |  |  |  |  |  |  | =head3 Destination Options | 
| 551 |  |  |  |  |  |  |  | 
| 552 |  |  |  |  |  |  | There are a number of options you can add to modify the behavior of the route, | 
| 553 |  |  |  |  |  |  | if you specify a hashref for a destination: | 
| 554 |  |  |  |  |  |  |  | 
| 555 |  |  |  |  |  |  | =head4 to | 
| 556 |  |  |  |  |  |  |  | 
| 557 |  |  |  |  |  |  | Sets the destination for the route. It should be a subroutine name or CODE | 
| 558 |  |  |  |  |  |  | reference. | 
| 559 |  |  |  |  |  |  |  | 
| 560 |  |  |  |  |  |  | $r->add( '/home' => { to => 'users#home' } ); # /home -> MyApp::Users::home | 
| 561 |  |  |  |  |  |  | $r->add( '/sys' => { to => sub { ... } });    # /sys -> execute code | 
| 562 |  |  |  |  |  |  | $r->add( '/item' => { to => 'Items::handle' } ) ;   # /item -> MyApp::Items::handle | 
| 563 |  |  |  |  |  |  | $r->add( '/item' => { to => 'items#handle' } );    # Same as above | 
| 564 |  |  |  |  |  |  |  | 
| 565 |  |  |  |  |  |  | =head4 method | 
| 566 |  |  |  |  |  |  |  | 
| 567 |  |  |  |  |  |  | Specifies an HTTP method to be considered by L</match> when matching a route. | 
| 568 |  |  |  |  |  |  |  | 
| 569 |  |  |  |  |  |  | # POST /item -> MyApp::Items::add | 
| 570 |  |  |  |  |  |  | $r->add( | 
| 571 |  |  |  |  |  |  | '/item' => { | 
| 572 |  |  |  |  |  |  | method => 'POST', | 
| 573 |  |  |  |  |  |  | to     => 'items#add' | 
| 574 |  |  |  |  |  |  | } | 
| 575 |  |  |  |  |  |  | ); | 
| 576 |  |  |  |  |  |  |  | 
| 577 |  |  |  |  |  |  | A shortcut for the above is this: | 
| 578 |  |  |  |  |  |  |  | 
| 579 |  |  |  |  |  |  | $r->add( [ POST => '/item' ] => 'items#add' ); | 
| 580 |  |  |  |  |  |  |  | 
| 581 |  |  |  |  |  |  | =head4 name | 
| 582 |  |  |  |  |  |  |  | 
| 583 |  |  |  |  |  |  | Give the route a name, and you can always use it to build a URL later via the L</url> | 
| 584 |  |  |  |  |  |  | subroutine. | 
| 585 |  |  |  |  |  |  |  | 
| 586 |  |  |  |  |  |  | $r->add( | 
| 587 |  |  |  |  |  |  | '/item/:id/:name' => { | 
| 588 |  |  |  |  |  |  | to   => 'items#view', | 
| 589 |  |  |  |  |  |  | name => 'item' | 
| 590 |  |  |  |  |  |  | } | 
| 591 |  |  |  |  |  |  | ); | 
| 592 |  |  |  |  |  |  |  | 
| 593 |  |  |  |  |  |  | # Later | 
| 594 |  |  |  |  |  |  | $r->url( 'item', id => 8, name => 'foo' );    # /item/8/foo | 
| 595 |  |  |  |  |  |  |  | 
| 596 |  |  |  |  |  |  | =head4 check | 
| 597 |  |  |  |  |  |  |  | 
| 598 |  |  |  |  |  |  | A hashref of checks to perform on the captures. It should contain capture | 
| 599 |  |  |  |  |  |  | names and stringified regular expressions. Do not use C<^> and C<$> to denote | 
| 600 |  |  |  |  |  |  | beginning and ending of the matched expression, because it will get embedded | 
| 601 |  |  |  |  |  |  | in a bigger Regexp. | 
| 602 |  |  |  |  |  |  |  | 
| 603 |  |  |  |  |  |  | $r->add( | 
| 604 |  |  |  |  |  |  | '/item/:id/:name' => { | 
| 605 |  |  |  |  |  |  | to    => 'items#view', | 
| 606 |  |  |  |  |  |  | check => { | 
| 607 |  |  |  |  |  |  | id   => '\d+',          # id must be a digit | 
| 608 |  |  |  |  |  |  | name => 'open|close'    # name can be 'open' or 'close' | 
| 609 |  |  |  |  |  |  | } | 
| 610 |  |  |  |  |  |  | } | 
| 611 |  |  |  |  |  |  | ); | 
| 612 |  |  |  |  |  |  |  | 
| 613 |  |  |  |  |  |  | =head4 defaults | 
| 614 |  |  |  |  |  |  |  | 
| 615 |  |  |  |  |  |  | Set default values for optional placeholders. | 
| 616 |  |  |  |  |  |  |  | 
| 617 |  |  |  |  |  |  | $r->add( | 
| 618 |  |  |  |  |  |  | '/pages/?id' => { | 
| 619 |  |  |  |  |  |  | to       => 'pages#view', | 
| 620 |  |  |  |  |  |  | defaults => { id => 2 } | 
| 621 |  |  |  |  |  |  | } | 
| 622 |  |  |  |  |  |  | ); | 
| 623 |  |  |  |  |  |  |  | 
| 624 |  |  |  |  |  |  | # /pages    -> match ( id = 2 ) | 
| 625 |  |  |  |  |  |  | # /pages/   -> match ( id = 2 ) | 
| 626 |  |  |  |  |  |  | # /pages/4  -> match ( id = 4 ) | 
| 627 |  |  |  |  |  |  |  | 
| 628 |  |  |  |  |  |  | =head4 bridge | 
| 629 |  |  |  |  |  |  |  | 
| 630 |  |  |  |  |  |  | If set to 1 this route will be treated as a bridge. Please see L</BRIDGES> | 
| 631 |  |  |  |  |  |  | for more information. | 
| 632 |  |  |  |  |  |  |  | 
| 633 |  |  |  |  |  |  | =head4 tree | 
| 634 |  |  |  |  |  |  |  | 
| 635 |  |  |  |  |  |  | Creates a tree of sub-routes. See L</TREES> for more information and examples. | 
| 636 |  |  |  |  |  |  |  | 
| 637 |  |  |  |  |  |  | =head2 url | 
| 638 |  |  |  |  |  |  |  | 
| 639 |  |  |  |  |  |  | my $url = $r->url($path, @arguments); | 
| 640 |  |  |  |  |  |  |  | 
| 641 |  |  |  |  |  |  | Builds an url from path and arguments. If the request is named a name can be specified instead. | 
| 642 |  |  |  |  |  |  |  | 
| 643 |  |  |  |  |  |  | =head2 match | 
| 644 |  |  |  |  |  |  |  | 
| 645 |  |  |  |  |  |  | Returns an array of L<Kelp::Routes::Pattern> objects that match the path | 
| 646 |  |  |  |  |  |  | and HTTP method provided. Each object will contain a hash with the named | 
| 647 |  |  |  |  |  |  | placeholders in L<Kelp::Routes::Pattern/named>, and an array with their | 
| 648 |  |  |  |  |  |  | values in the order they were specified in the pattern in | 
| 649 |  |  |  |  |  |  | L<Kelp::Routes::Pattern/param>. | 
| 650 |  |  |  |  |  |  |  | 
| 651 |  |  |  |  |  |  | $r->add( '/:id/:name', "route" ); | 
| 652 |  |  |  |  |  |  | for my $pattern ( @{ $r->match('/15/alex') } ) { | 
| 653 |  |  |  |  |  |  | $pattern->named;    # { id => 15, name => 'alex' } | 
| 654 |  |  |  |  |  |  | $pattern->param;    # [ 15, 'alex' ] | 
| 655 |  |  |  |  |  |  | } | 
| 656 |  |  |  |  |  |  |  | 
| 657 |  |  |  |  |  |  | Routes that used regular expressions instead of patterns will only initialize | 
| 658 |  |  |  |  |  |  | the C<param> array with the regex captures, unless those patterns are using | 
| 659 |  |  |  |  |  |  | named captures in which case the C<named> hash will also be initialized. | 
| 660 |  |  |  |  |  |  |  | 
| 661 |  |  |  |  |  |  | =head2 dispatch | 
| 662 |  |  |  |  |  |  |  | 
| 663 |  |  |  |  |  |  | my $result = $r->dispatch($kelp, $route_pattern); | 
| 664 |  |  |  |  |  |  |  | 
| 665 |  |  |  |  |  |  | Dispatches an instance of L<Kelp::Routes::Pattern> by running the route destination specified in L<Kelp::Routes::Pattern/to> | 
| 666 |  |  |  |  |  |  |  | 
| 667 |  |  |  |  |  |  | =head1 EXTENDING | 
| 668 |  |  |  |  |  |  |  | 
| 669 |  |  |  |  |  |  | This is the default router class for each new Kelp application, but it doesn't | 
| 670 |  |  |  |  |  |  | have to be. You can create your own subclass that better suits your needs. It's | 
| 671 |  |  |  |  |  |  | generally enough to override the L</dispatch> method. | 
| 672 |  |  |  |  |  |  |  | 
| 673 |  |  |  |  |  |  | Kelp comes with L<Kelp::Routes::Controller>, a router extension which reblesses | 
| 674 |  |  |  |  |  |  | the application instance into a controller class. | 
| 675 |  |  |  |  |  |  |  | 
| 676 |  |  |  |  |  |  | =head1 ACKNOWLEDGEMENTS | 
| 677 |  |  |  |  |  |  |  | 
| 678 |  |  |  |  |  |  | This module was inspired by L<Routes::Tiny>. | 
| 679 |  |  |  |  |  |  |  | 
| 680 |  |  |  |  |  |  | =cut |