-
-
Save anonymous/fd41e12424baa7e9a7b7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/lib/Mojolicious/Routes.pm b/lib/Mojolicious/Routes.pm | |
index 9bff8d5..315427d 100644 | |
--- a/lib/Mojolicious/Routes.pm | |
+++ b/lib/Mojolicious/Routes.pm | |
@@ -10,12 +10,13 @@ use Scalar::Util 'weaken'; | |
has base_classes => sub { [qw(Mojolicious::Controller Mojo)] }; | |
has cache => sub { Mojo::Cache->new }; | |
-has [qw(conditions shortcuts)] => sub { {} }; | |
+has [qw(conditions shortcuts types)] => sub { {} }; | |
has hidden => sub { [qw(attr has new tap)] }; | |
has namespaces => sub { [] }; | |
sub add_condition { $_[0]->conditions->{$_[1]} = $_[2] and return $_[0] } | |
sub add_shortcut { $_[0]->shortcuts->{$_[1]} = $_[2] and return $_[0] } | |
+sub add_type { $_[0]->types->{$_[1]} = $_[2] and return $_[0] } | |
sub continue { | |
my ($self, $c) = @_; | |
@@ -279,6 +280,13 @@ Namespaces to load controllers from. | |
Contains all available shortcuts. | |
+=head2 types | |
+ | |
+ my $types = $r->types; | |
+ $r = $r->types({int => qr/\d+/}); | |
+ | |
+Contains all available placeholder types. | |
+ | |
=head1 METHODS | |
L<Mojolicious::Routes> inherits all methods from L<Mojolicious::Routes::Route> | |
@@ -307,6 +315,12 @@ Register a shortcut. | |
... | |
}); | |
+=head2 add_type | |
+ | |
+ $r = $r->add_type(foo => qr/.../); | |
+ | |
+Register a placeholder type. | |
+ | |
=head2 continue | |
$r->continue(Mojolicious::Controller->new); | |
diff --git a/lib/Mojolicious/Routes/Pattern.pm b/lib/Mojolicious/Routes/Pattern.pm | |
index dbf8806..08e2e18 100644 | |
--- a/lib/Mojolicious/Routes/Pattern.pm | |
+++ b/lib/Mojolicious/Routes/Pattern.pm | |
@@ -1,14 +1,15 @@ | |
package Mojolicious::Routes::Pattern; | |
use Mojo::Base -base; | |
+use Mojo::Util 'deprecated'; | |
+ | |
has [qw(constraints defaults)] => sub { {} }; | |
has placeholder_start => ':'; | |
has [qw(placeholders tree)] => sub { [] }; | |
has quote_end => ')'; | |
has quote_start => '('; | |
has [qw(regex unparsed)]; | |
-has relaxed_start => '#'; | |
-has wildcard_start => '*'; | |
+has types => sub { {relaxed => '([^/]+)', wildcard => '(.+)'} }; | |
sub match { | |
my ($self, $path, $detect) = @_; | |
@@ -86,11 +87,12 @@ sub _compile { | |
my $placeholders = $self->placeholders; | |
my $constraints = $self->constraints; | |
my $defaults = $self->defaults; | |
+ my $types = $self->types; | |
my $block = my $regex = ''; | |
my $optional = 1; | |
for my $token (reverse @{$self->tree}) { | |
- my ($op, $value) = @$token; | |
+ my ($op, $value, $type) = @$token; | |
my $fragment = ''; | |
# Text | |
@@ -108,13 +110,8 @@ sub _compile { | |
unshift @$placeholders, $value; | |
# Placeholder | |
- if ($op eq 'placeholder') { $fragment = '([^/.]+)' } | |
- | |
- # Relaxed | |
- elsif ($op eq 'relaxed') { $fragment = '([^/]+)' } | |
- | |
- # Wildcard | |
- else { $fragment = '(.+)' } | |
+ $fragment = $type && $types->{$type} ? $types->{$type} : '([^/.]+)' | |
+ if $op eq 'placeholder'; | |
# Custom regex | |
if (my $c = $constraints->{$value}) { $fragment = _compile_req($c) } | |
@@ -161,42 +158,42 @@ sub _tokenize { | |
my $quote_end = $self->quote_end; | |
my $quote_start = $self->quote_start; | |
- my $placeholder = $self->placeholder_start; | |
- my $relaxed = $self->relaxed_start; | |
- my $wildcard = $self->wildcard_start; | |
+ my $start = $self->placeholder_start; | |
- my (@tree, $inside, $quoted); | |
+ my (@tree, $spec, $type); | |
for my $char (split '', $pattern) { | |
- # Quote start | |
+ # Quoted | |
if ($char eq $quote_start) { | |
push @tree, ['placeholder', '']; | |
- ($inside, $quoted) = (1, 1); | |
+ $spec = 1; | |
} | |
+ elsif ($char eq $quote_end) { ($spec, $type) = (0, 0) } | |
- # Placeholder start | |
- elsif ($char eq $placeholder) { | |
- push @tree, ['placeholder', ''] unless $inside++; | |
- } | |
+ # Type | |
+ elsif ($spec && $tree[-1][1] && $char eq ':') { $type = 1 } | |
+ elsif ($type) { $tree[-1][2] .= $char } | |
- # Relaxed or wildcard start (upgrade when quoted) | |
- elsif ($char eq $relaxed || $char eq $wildcard) { | |
- push @tree, ['placeholder', ''] unless $quoted; | |
- $tree[-1][0] = $char eq $relaxed ? 'relaxed' : 'wildcard'; | |
- $inside = 1; | |
+ # Placeholder start | |
+ elsif ($char eq $start) { push @tree, ['placeholder', ''] unless $spec++ } | |
+ | |
+ # DEPRECATED in Clinking Beer Mugs! | |
+ elsif ($char eq '#' || $char eq '*') { | |
+ deprecated 'Relaxed and wildcard placeholders are DEPRECATED' | |
+ . ' in favor of placeholder types'; | |
+ push @tree, ['placeholder', ''] unless $spec; | |
+ $tree[-1][2] = $char eq '#' ? 'relaxed' : 'wildcard'; | |
+ $spec = 1; | |
} | |
- # Quote end | |
- elsif ($char eq $quote_end) { ($inside, $quoted) = (0, 0) } | |
- | |
# Slash | |
elsif ($char eq '/') { | |
push @tree, ['slash']; | |
- $inside = 0; | |
+ $spec = 0; | |
} | |
- # Placeholder, relaxed or wildcard | |
- elsif ($inside) { $tree[-1][-1] .= $char } | |
+ # Placeholder | |
+ elsif ($spec) { $tree[-1][1] .= $char } | |
# Text (optimize slash+text and *+text+slash+text) | |
elsif ($tree[-1][0] eq 'text') { $tree[-1][-1] .= $char } | |
@@ -235,6 +232,23 @@ Mojolicious::Routes::Pattern - Routes pattern engine | |
L<Mojolicious::Routes::Pattern> is the core of L<Mojolicious::Routes>. | |
+=head2 TYPES | |
+ | |
+These placeholder types are available by default. | |
+ | |
+=head2 relaxed | |
+ | |
+ "/(foo:relaxed)" | |
+ | |
+Match all characters except C</>, similar to the regular expression C<([^/]+)>. | |
+ | |
+=head2 wildcard | |
+ | |
+ "/(foo:wildcard)" | |
+ | |
+Match absolutely everything, including C</> and C<.>, similar to the regular | |
+expression C<(.+)>. | |
+ | |
=head1 ATTRIBUTES | |
L<Mojolicious::Routes::Pattern> implements the following attributes. | |
@@ -288,13 +302,6 @@ Character indicating the start of a quoted placeholder, defaults to C<(>. | |
Pattern in compiled regular expression form. | |
-=head2 relaxed_start | |
- | |
- my $start = $pattern->relaxed_start; | |
- $pattern = $pattern->relaxed_start('*'); | |
- | |
-Character indicating a relaxed placeholder, defaults to C<#>. | |
- | |
=head2 tree | |
my $tree = $pattern->tree; | |
@@ -303,6 +310,13 @@ Character indicating a relaxed placeholder, defaults to C<#>. | |
Pattern in parsed form. Note that this structure should only be used very | |
carefully since it is very dynamic. | |
+=head2 types | |
+ | |
+ my $types = $pattern->types; | |
+ $pattern = $pattern->types({foo => qr/\w+/}); | |
+ | |
+Placeholder types. | |
+ | |
=head2 unparsed | |
my $unparsed = $pattern->unparsed; | |
@@ -310,13 +324,6 @@ carefully since it is very dynamic. | |
Raw unparsed pattern. | |
-=head2 wildcard_start | |
- | |
- my $start = $pattern->wildcard_start; | |
- $pattern = $pattern->wildcard_start('*'); | |
- | |
-Character indicating the start of a wildcard placeholder, defaults to C<*>. | |
- | |
=head1 METHODS | |
L<Mojolicious::Routes::Pattern> inherits all methods from L<Mojo::Base> and | |
diff --git a/lib/Mojolicious/Routes/Route.pm b/lib/Mojolicious/Routes/Route.pm | |
index 35fcf12..49e5870 100644 | |
--- a/lib/Mojolicious/Routes/Route.pm | |
+++ b/lib/Mojolicious/Routes/Route.pm | |
@@ -73,8 +73,6 @@ sub name { | |
return $self; | |
} | |
-sub new { shift->SUPER::new->parse(@_) } | |
- | |
sub options { shift->_generate_route(OPTIONS => @_) } | |
sub over { | |
@@ -119,10 +117,15 @@ sub render { | |
sub root { shift->_chain->[0] } | |
sub route { | |
- my $self = shift; | |
- my $route = $self->add_child(__PACKAGE__->new(@_))->children->[-1]; | |
+ my $self = shift; | |
+ | |
+ my $route = __PACKAGE__->new; | |
+ my $pattern = $route->pattern; | |
+ $pattern->types({%{$pattern->types}, %{$self->root->types}}); | |
+ $self->add_child($route->parse(@_)); | |
my $format = $self->pattern->constraints->{format}; | |
- $route->pattern->constraints->{format} //= 0 if defined $format && !$format; | |
+ $pattern->constraints->{format} //= 0 if defined $format && !$format; | |
+ | |
return $route; | |
} | |
@@ -395,16 +398,6 @@ the current route. | |
# Route with destination and custom name | |
$r->get('/user')->to('user#show')->name('show_user'); | |
-=head2 new | |
- | |
- my $r = Mojolicious::Routes::Route->new; | |
- my $r = Mojolicious::Routes::Route->new('/:action'); | |
- my $r = Mojolicious::Routes::Route->new('/:action', action => qr/\w+/); | |
- my $r = Mojolicious::Routes::Route->new(format => 0); | |
- | |
-Construct a new L<Mojolicious::Routes::Route> object and L</"parse"> pattern if | |
-necessary. | |
- | |
=head2 options | |
my $route = $r->options('/:foo'); | |
diff --git a/t/mojolicious/pattern.t b/t/mojolicious/pattern.t | |
index 179758b..c3a3bbd 100644 | |
--- a/t/mojolicious/pattern.t | |
+++ b/t/mojolicious/pattern.t | |
@@ -106,7 +106,8 @@ is_deeply $pattern->match('/test/foo/bar.baz/yada'), | |
{controller => 'foo', action => 'bar.baz/yada'}, 'right structure'; | |
is $pattern->render({controller => 'foo', action => 'bar.baz/yada'}), | |
'/test/foo/bar.baz/yada', 'right result'; | |
-$pattern = Mojolicious::Routes::Pattern->new('/tset/:controller/*action'); | |
+$pattern | |
+ = Mojolicious::Routes::Pattern->new('/tset/:controller/(:action:wildcard)'); | |
is_deeply $pattern->match('/tset/foo/bar.baz/yada'), | |
{controller => 'foo', action => 'bar.baz/yada'}, 'right structure'; | |
is $pattern->render({controller => 'foo', action => 'bar.baz/yada'}), | |
diff --git a/t/mojolicious/routes.t b/t/mojolicious/routes.t | |
index 524abcc..c680222 100644 | |
--- a/t/mojolicious/routes.t | |
+++ b/t/mojolicious/routes.t | |
@@ -79,7 +79,7 @@ $r->route('/wildcards/1/(*wildcard)', wildcard => qr/(?:.*)/) | |
->to(controller => 'wild', action => 'card'); | |
# /wildcards/2/* | |
-$r->route('/wildcards/2/(*wildcard)') | |
+$r->route('/wildcards/2/(wildcard:wildcard)') | |
->to(controller => 'card', action => 'wild'); | |
# /wildcards/3/*/foo |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment