Skip Menu |

This queue is for tickets about the Catalyst-Runtime CPAN distribution.

Report information
The Basics
Id: 110009
Status: open
Priority: 0/
Queue: Catalyst-Runtime

People
Owner: Nobody in particular
Requestors: tsibley [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in:
  • 5.90085
  • 5.90089_001
  • 5.90089_002
  • 5.90089_003
  • 5.90089_004
  • 5.90090
  • 5.90091
  • 5.90092
  • 5.90093
  • 5.90094
  • 5.90095
  • 5.90096
  • 5.90097
  • 5.90100
  • 5.90101
  • 5.90102
  • 5.90103
Fixed in: (no value)



Subject: Args(0) regression with ActionRole::MatchRequestAccepts
When using ActionRole::MatchRequestAccepts, the fallback action takes precedence over any actions with Accept() if it has Args(0) instead of Args. The tests for ActionRole::MatchRequestAccepts don't catch this because they specify Args for the fallback, not Args(0). I noticed this behaviour in an internal work app. The problem may be best demonstrated by the attached example test case, which fails from 09914a4 onward. 5.90084 is the last known good release, and bisect confirmed this. Note that the test case will pass if you change not_accepted's Args(0) to Args. I think Catalyst is probably to blame here rather than ActionRole::MatchRequestAccepts, but let me know if I'm wrong and should report this regression over there instead.
Subject: args0-match-request-accepts.t
use warnings; use strict; use Test::More; plan skip_all => 'Requires Catalyst::ActionRole::MatchRequestAccepts' unless eval { require Catalyst::ActionRole::MatchRequestAccepts }; { package MyApp::Controller::Root; $INC{'MyApp/Controller/Root.pm'} = __FILE__; use Moose; use MooseX::MethodAttributes; BEGIN { extends 'Catalyst::Controller' }; sub root : Chained('/') PathPrefix CaptureArgs(0) {} sub text_plain : Chained('root') PathPart('') Does(MatchRequestAccepts) Accept('text/plain') Args(0) { my ($self, $ctx) = @_; $ctx->response->body('text_plain'); } sub json : Chained('root') PathPart('') Does(MatchRequestAccepts) Accept('application/json') Args(0) { my ($self, $ctx) = @_; $ctx->response->body('json'); } sub not_accepted : Chained('root') PathPart('') Args(0) { my ($self, $ctx) = @_; $ctx->response->body('error_not_accepted'); } MyApp::Controller::Root->config(namespace=>''); package MyApp; use Catalyst; MyApp->setup; } use Catalyst::Test 'MyApp'; use HTTP::Request::Common qw/GET/; is request(GET '/')->content, 'error_not_accepted'; is request(GET '/', 'Accept' => 'text/plain')->content, 'text_plain'; is request(GET '/', 'Accept' => 'application/json')->content, 'json'; done_testing;
The first bad commit is: commit 09914a47700308f8588e65eb4a29d378c192aee8 Author: John Napiorkowski <jjn1056@yahoo.com> Date: Wed Mar 25 10:09:53 2015 -0500 do not change the best_action when args(0) if current best_action is also args(0)
Subject: Re: [rt.cpan.org #110009] Args(0) regression with ActionRole::MatchRequestAccepts
Date: Wed, 2 Dec 2015 00:28:47 +0000 (UTC)
To: "bug-Catalyst-Runtime [...] rt.cpan.org" <bug-Catalyst-Runtime [...] rt.cpan.org>
From: John Napiorkowski <jjn1056 [...] yahoo.com>
This was a change in Catalyst around version 5.9008x where we normalized the priority of actions when several actions could match as in the example you've given.  Previously sometimes Catalyst would start checking actions defined first, other times last.  In particular the case Args(0) was acting differently from all other types of action definitions.  We changed this to always start matching the last action, so that is breaking your example.  See Catalyst::RouteMatching  for more on this change. |   | |   |   |   |   |   | | Catalyst::RouteMatchingHow Catalyst maps an incoming URL to actions in controllers. | | | | View on metacpan.org | Preview by Yahoo | | | |   | If you have code that relied on the old behavior and change change the action order, there is a global catalyst configuration option to re-enable the legacy behavior, see "use_chained_args_0_special_case" in the core Catalyst docs under configuration: https://metacpan.org/pod/Catalyst#CONFIGURATION "Args" or "Args()" is always going to be a special low priority match, since its defined to be a catchall and since it matches a lot we force the dispatcher to match it last, otherwise it would always gobble up the requests.  That's why using Args instead of Arg(0) also seems to fix your test case.  Think of Args as similar to Args(~).  I know people sometimes things Args and Args(0) are the same, but they are totally different.  Probably this is a bug in the interface engineering choices made early on that cause confusing today :) This action role probably needs a documentation patch to bring it up to modern times.   Thanks! jnap On Tuesday, December 1, 2015 3:59 PM, Thomas Sibley via RT <bug-Catalyst-Runtime@rt.cpan.org> wrote:       Queue: Catalyst-Runtime Ticket <URL: https://rt.cpan.org/Ticket/Display.html?id=110009 > The first bad commit is: commit 09914a47700308f8588e65eb4a29d378c192aee8 Author: John Napiorkowski <jjn1056@yahoo.com> Date:  Wed Mar 25 10:09:53 2015 -0500     do not change the best_action when args(0) if current best_action is also args(0)
Subject: Re: [rt.cpan.org #110009] Args(0) regression with ActionRole::MatchRequestAccepts
Date: Tue, 01 Dec 2015 20:42:43 -0800
To: bug-Catalyst-Runtime [...] rt.cpan.org
From: Thomas Sibley <tsibley [...] cpan.org>
On 12/01/2015 04:29 PM, jjn1056@yahoo.com via RT wrote: Show quoted text
> This was a change in Catalyst around version 5.9008x where we > normalized the priority of actions when several actions could match > as in the example you've given. Previously sometimes Catalyst would > start checking actions defined first, other times last. In > particular the case Args(0) was acting differently from all other > types of action definitions. We changed this to always start > matching the last action, so that is breaking your example.
Ah, I see. Given this, it seems like on >=5.90085, I can move my fallback to be defined first to fix the problem. This works because when the other actions with an Accept() match, they'll be after the fallback and the last one (of two, in my case) will be chosen. Show quoted text
> If you have code that relied on the old behavior and change change > the action order, there is a global catalyst configuration option to > re-enable the legacy behavior, see "use_chained_args_0_special_case" > in the core Catalyst docs under configuration: > https://metacpan.org/pod/Catalyst#CONFIGURATION
Thanks for the pointer. I can change it, so I'd rather future-proof the code than set a compat option. Show quoted text
> "Args" or "Args()" is always going to be a special low priority > match, since its defined to be a catchall and since it matches a lot > we force the dispatcher to match it last, otherwise it would always > gobble up the requests. That's why using Args instead of Arg(0) > also seems to fix your test case. Think of Args as similar to > Args(~). I know people sometimes things Args and Args(0) are the > same, but they are totally different.
Yep, I'm well aware that Args and Args(0) are very different. The thing is, I don't want my MatchRequestAccepts fallback to match Args. I want it to match Args(0), like all the other actions for that endpoint. Unlike in the test example, my real fallback isn't some error response, it's a response with a default content type. I'm not keen on permitting random trailing args to get a successful response even if they're unlikely to interfere with other actions. Show quoted text
> This action role probably needs a documentation patch to bring it up > to modern times.
What would that doc patch look like? Is it as simple as moving the fallback to be defined first? BTW, while I have your attention, any chance of this getting merged? https://rt.cpan.org/Ticket/Display.html?id=107786 :-)