Stash values for controller and action in routes, maybe a bug in either Mojo, or the documentation

37 views
Skip to first unread message

Pierre VIGIER

unread,
May 5, 2017, 6:36:58 AM5/5/17
to Mojolicious

Hi Mojolicious users,

i came across something strange when trying to play with routing and dispatcher.

In the documentation, in that section: http://mojolicious.org/perldoc/Mojolicious/Guides/Routing#Optional-placeholders , we see:

Special stash values like controller and action can also be placeholders, which is very convenient especially during development, but should only be used very carefully, because every controller method becomes a potential route. All uppercase methods as well as those starting with an underscore are automatically hidden from the router and you can use “hide” in Mojolicious::Routes to add additional ones.

I create a small app to show case what could be an issue either in the documentation or in the code itself.

  • lib/App.pm
package App;
use Mojo::Base 'Mojolicious';

sub startup {
    my $app = shift;

    my $r = $app->routes;

    $r->get('/in_url/:controller/:action')->to( controler => 'first', action => 'fallback' );

    $r->under( '/in_stash' => sub {
        my $c = shift;
        $c->stash( controller => 'second', action => 'index' );
        return 1;
    } )->get('test')->to( controller => 'first', action => 'fallback' );
}
1;
  • lib/App/Controller/First.pm
package App::Controller::First;
use Mojo::Base 'Mojolicious::Controller';

sub index { my $c = shift; $c->render( text => 'Index : '.$c->stash('controller').' / '.$c->stash('action') ); }
sub other { my $c = shift; $c->render( text => 'Other : '.$c->stash('controller').' / '.$c->stash('action') ); }
sub fallback { my $c = shift; $c->render( text => 'Fallback : '.$c->stash('controller').' / '.$c->stash('action') ); }

1;
  • lib/App/Controller/Second.pm
package App::Controller::Second;
use Mojo::Base 'Mojolicious::Controller';

sub index { my $c = shift; $c->render( text => 'Index 2 : '.$c->stash('controller').' / '.$c->stash('action') ); }
sub other { my $c = shift; $c->render( text => 'Other 2 : '.$c->stash('controller').' / '.$c->stash('action') ); }
sub fallback { my $c = shift; $c->render( text => 'Fallback 2 : '.$c->stash('controller').' / '.$c->stash('action') ); }

1;

The routes under /in_url are taking controller and action from the placeholder in the url, and according to the documentation from the stash, it’s working as expected:

» curl http://localhost:3000/in_url/first
Fallback : first / fallback
» curl http://localhost:3000/in_url/second/other
Other 2 : second / other

However, with the second route, where set the stash within the under part of the route:

curl http://localhost:3000/in_stash/test
Fallback : first / fallback

The stash actually got rewritten, which means that the stash values that i set in the under are rewritten with the values from the to

When i look at the code, in Mojolicious/Routes.pm, it seems that the controller and class are taken from the route itself and not from the stash, and after that, those value are pushed to the stash. So the value from the placeholders are taking precedence on the default ones set in the to part of the route, however, the one from the stash get discarded

Am i getting something wrong? That was not my understanding from the documentation.

Pierre

Stefan Adams

unread,
May 6, 2017, 1:12:21 PM5/6/17
to mojolicious

On Fri, May 5, 2017 at 5:36 AM, Pierre VIGIER <pierre...@gmail.com> wrote:
Am i getting something wrong? That was not my understanding from the documentation.

$r->get('/in_url/:controller/:action')->to( controler => 'first', action => 'fallback' );

I'm pretty sure that according to the optional placeholders documentation you wouldn't set controller and action in to(). Basically, you're able to skip the to() because you've already set the controller and action in your route (:controller stashes that part of the path into the reserved stash value controller, and likewise for action). So you should be doing this, I think:

$r->get('/in_url/:controller/:action');

And that will route appropriately for a GET request to, e.g., /in_url/first/fallback

So if'm right and if you're misunderstanding the documentation, then perhaps the rest of your question is moot until you try with this new interpretation. Let us know!

And yes, I think to() overrides the stash.

In the end, I don't think there's anything strange except that you're using the :controller / :action trick yet declaring them explicitly with to().

Pierre VIGIER

unread,
May 6, 2017, 8:37:04 PM5/6/17
to Mojolicious

HI Stefan,

i guess my post was not clear on a specific part, from the documentation, for instance, that link http://mojolicious.org/perldoc/Mojolicious/Guides/Routing#Special-stash-values , i see that stash values action and controller are used instantiating the controller class, however, when setting those values in the stash, it does not change the controller that is used, except if it is set within route placeholder.

By checking code of Mojolicious/Routes.pm and Mojolicious/Routes/Match.pm , it seems that controller and action are not taken from the stash, they are only taken the match object, so from the route match itself and captures.

That is the part where i am not sure to understand correctly, documentation says that action and controller are special value of the stash, and that

When the dispatcher sees controller and action values in the stash it will always try to turn them into a class and method to dispatch to.

However, setting controller and action in the stash before the dispatcher kicks in does not change the routing. From the code, values are not taken from the stash, there are taken from the route match, first from the captures of the route, and then from the to method, but they are not taken from the stash.

Am i missing something?

Pierre

Reply all
Reply to author
Forward
0 new messages