Skip to content

Instantly share code, notes, and snippets.

/methods.diff Secret

Created March 14, 2015 03:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/b309ac31d92a9f07dac8 to your computer and use it in GitHub Desktop.
Save anonymous/b309ac31d92a9f07dac8 to your computer and use it in GitHub Desktop.
diff --git a/lib/Mojolicious/Guides/Routing.pod b/lib/Mojolicious/Guides/Routing.pod
index f8b1653..43f5e2c 100644
--- a/lib/Mojolicious/Guides/Routing.pod
+++ b/lib/Mojolicious/Guides/Routing.pod
@@ -236,6 +236,10 @@ but content will not be sent with the response even if it is present.
# HEAD /test -> {controller => 'bar', action => 'test'}
$r->get('/test')->to(controller => 'bar', action => 'test');
+You can also use the C<_method> query parameter to override the request method,
+this can be very useful when submitting forms with browsers that only support
+C<GET> and C<POST>.
+
=head2 IRIs
IRIs are handled transparently, that means paths are guaranteed to be unescaped
diff --git a/lib/Mojolicious/Plugin/TagHelpers.pm b/lib/Mojolicious/Plugin/TagHelpers.pm
index bf6ec1b..17a337f 100644
--- a/lib/Mojolicious/Plugin/TagHelpers.pm
+++ b/lib/Mojolicious/Plugin/TagHelpers.pm
@@ -53,17 +53,14 @@ sub _form_for {
push @url, shift if ref $_[0] eq 'HASH';
# POST detection
- my @post;
+ my (@post, $method);
if (my $r = $c->app->routes->lookup($url[0])) {
- my %methods = (GET => 1, POST => 1);
- do {
- my @via = @{$r->via || []};
- %methods = map { $_ => 1 } grep { $methods{$_} } @via if @via;
- } while $r = $r->parent;
- @post = (method => 'POST') if $methods{POST} && !$methods{GET};
+ @post = (method => 'POST') if ($method = $r->suggested_method) ne 'GET';
}
- return _tag('form', action => $c->url_for(@url), @post, @_);
+ my $url = $c->url_for(@url);
+ $url->query({_method => $method}) if @post && $method ne 'POST';
+ return _tag('form', action => $url, @post, @_);
}
sub _hidden_field {
@@ -366,8 +363,9 @@ Generate C<input> tag of type C<file>.
% end
Generate portable C<form> tag with L<Mojolicious::Controller/"url_for">. For
-routes that allow C<POST> but not C<GET>, a C<method> attribute will be
-automatically added.
+routes that do not allow C<GET>, a C<method> attribute will be automatically
+added. And for methods other than C<GET> and C<POST>, a C<_method> query
+parameter will be added as well.
<form action="/path/to/login">
<input name="first_name" type="text">
diff --git a/lib/Mojolicious/Routes.pm b/lib/Mojolicious/Routes.pm
index bdabbf2..d713913 100644
--- a/lib/Mojolicious/Routes.pm
+++ b/lib/Mojolicious/Routes.pm
@@ -71,7 +71,7 @@ sub match {
else { $path = $req->url->path->to_route }
# Method (HEAD will be treated as GET)
- my $method = uc $req->method;
+ my $method = uc($req->url->query->clone->param('_method') || $req->method);
$method = 'GET' if $method eq 'HEAD';
# Check cache
diff --git a/lib/Mojolicious/Routes/Route.pm b/lib/Mojolicious/Routes/Route.pm
index a98c461..c8bd0f4 100644
--- a/lib/Mojolicious/Routes/Route.pm
+++ b/lib/Mojolicious/Routes/Route.pm
@@ -128,6 +128,19 @@ sub route {
return $route;
}
+sub suggested_method {
+ my $self = shift;
+
+ my %methods;
+ for my $route (@{$self->_chain}) {
+ next unless my @via = @{$route->via || []};
+ %methods = map { $_ => 1 } grep { keys %methods ? $methods{$_} : 1 } @via;
+ }
+ return 'POST' if $methods{POST} && !$methods{GET};
+ return 'GET' if $methods{GET};
+ return (sort keys %methods)[0] || 'GET';
+}
+
sub to {
my $self = shift;
@@ -492,6 +505,12 @@ The L<Mojolicious::Routes> object this route is a descendant of.
Low-level generator for routes matching all HTTP request methods, returns a
L<Mojolicious::Routes::Route> object.
+=head2 suggested_method
+
+ my $method = $r->suggested_method;
+
+Suggested HTTP method for this route, C<GET> and C<POST> are preferred.
+
=head2 to
my $defaults = $r->to;
diff --git a/t/mojolicious/tag_helper_lite_app.t b/t/mojolicious/tag_helper_lite_app.t
index 68a6bfe..fae4ddf 100644
--- a/t/mojolicious/tag_helper_lite_app.t
+++ b/t/mojolicious/tag_helper_lite_app.t
@@ -313,7 +313,7 @@ EOF
# Empty selection
$t->put_ok('/selection')->status_is(200)
- ->content_is("<form action=\"/selection\">\n "
+ ->content_is("<form action=\"/selection?_method=PUT\" method=\"POST\">\n "
. '<select name="a">'
. '<option value="b">b</option>'
. '<optgroup label="c">'
@@ -342,7 +342,7 @@ $t->put_ok('/selection')->status_is(200)
# Selection with values
$t->put_ok('/selection?a=e&foo=bar&bar=baz&yada=b')->status_is(200)
- ->content_is("<form action=\"/selection\">\n "
+ ->content_is("<form action=\"/selection?_method=PUT\" method=\"POST\">\n "
. '<select name="a">'
. '<option value="b">b</option>'
. '<optgroup label="c">'
@@ -372,7 +372,7 @@ $t->put_ok('/selection?a=e&foo=bar&bar=baz&yada=b')->status_is(200)
# Selection with multiple values
$t->put_ok('/selection?foo=bar&a=e&foo=baz&bar=d&yada=a&yada=b')
->status_is(200)
- ->content_is("<form action=\"/selection\">\n "
+ ->content_is("<form action=\"/selection?_method=PUT\" method=\"POST\">\n "
. '<select name="a">'
. '<option value="b">b</option>'
. '<optgroup label="c">'
@@ -401,7 +401,7 @@ $t->put_ok('/selection?foo=bar&a=e&foo=baz&bar=d&yada=a&yada=b')
# Selection with multiple values preselected
$t->put_ok('/selection?preselect=1')->status_is(200)
- ->content_is("<form action=\"/selection\">\n "
+ ->content_is("<form action=\"/selection?_method=PUT\" method=\"POST\">\n "
. '<select name="a">'
. '<option selected value="b">b</option>'
. '<optgroup label="c">'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment