Skip Menu |

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

Report information
The Basics
Id: 65463
Status: resolved
Priority: 0/
Queue: Catalyst-Runtime

People
Owner: bobtfish [...] bobtfish.net
Requestors: catalyst [...] sethdaniel.org
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: (no value)
Fixed in: (no value)



Subject: Catalyst::Controller bug when parsing attributes (with patch)
Date: Fri, 4 Feb 2011 14:28:09 -0800
To: bug-Catalyst-Runtime [...] rt.cpan.org
From: Seth Daniel <catalyst [...] sethdaniel.org>
Hi, I believe I've discovered a bug in Catalyst::Controller. I triggered it while using Catalyst::Controller::ActionRole so my examples below will refer to that module. perl 5.10.1 Catalyst::Runtime 5.80031 Catalyst::Controller::ActionRole 0.15 I have a Catalyst controller with a configuration like the following: __PACKAGE->config( action => { some_action => { Does => [ '+Some:ActionRole' ] } } ); This will break *if* the Action module that processes the 'Does' attribute changes the value that is passed to it. For a real world example of this see Catalyst::Controller::ActionRole which changes the value passed to its _parse_Does_attr method. The _parse_attrs method in Catalyst::Controller does the following: [...] foreach my $key (keys %raw_attributes) { my $raw = $raw_attributes{$key}; foreach my $value (ref($raw) eq 'ARRAY' ? @$raw : $raw) { my $meth = "_parse_${key}_attr"; if ( my $code = $self->can($meth) ) { ( $key, $value ) = $self->$code( $c, $name, $value ); } push( @{ $final_attributes{$key} }, $value ); } } [...] The problem lies in the inner foreach. When $row is an array reference (and only when $row is an array reference) $value will be an *alias* to each element of that array. So if $value is changed within the attribute parsing method this means that the next time this attribute parsing method is called the $value passed to it will be the changed value...but this only occurs when using a configuration like what I pasted above because the actual configuration hash gets changed. I don't recall which perl introduced aliasing, but I use perl 5.10.1. Here is a simple patch which resolves the issue. This is merely meant to be illustrative: --- Controller.pm 2010-07-28 15:03:32.000000000 -0700 +++ Controller.pm.bak 2011-02-02 16:55:47.000000000 -0800 @@ -322,11 +322,12 @@ foreach my $value (ref($raw) eq 'ARRAY' ? @$raw : $raw) { + my $t_val = $value; my $meth = "_parse_${key}_attr"; if ( my $code = $self->can($meth) ) { - ( $key, $value ) = $self->$code( $c, $name, $value ); + ( $key, $t_val ) = $self->$code( $c, $name, $t_val ); } - push( @{ $final_attributes{$key} }, $value ); + push( @{ $final_attributes{$key} }, $t_val ); } } A workaround, at least when using Catalyst::Controller::ActionRole, is to not use the array ref syntax: __PACKAGE->config( action => { some_action => { Does => '+Some:ActionRole' } } ); Of course this workaround isn't useful if you want to apply multiple roles to an action. There are three conditions I have identified that trigger this bug: 1) There is an attribute parser that changes the passed in $value (which Catalyst::Controller::ActionRole does if you use the '+' or '~' syntax). 2) An ActionRole is assigned to some action via the configuration. 3) The controller that contains the modified action is subclassed *more than once*. The reason for this is that the first time the action is applied everything seems fine, however the $value was changed and this consquently changes the configuration. The second time it is applied the '+' or '~' will be missing and the application of the role will fail. The test program I provide below illustrates this. http://doubleplusgood.org/ccbug.tar The above link is as simple a program as I could make to exhibit the problem. Mostly I just whittled down the Catalyst::Controller::ActionRole tests to as few files as I could and changed a few things. Unpack the tar file and cd to the 't' dir. Run 'prove execution-via-config.t'. It should fail with the bug I reported. You can get the test to pass if you either patch Catalyst::Controller with the patch I proved above or simply change t/lib/TestApp/Controller/ExecutionViaConfig.pm to apply the '+Moo' role like so: __PACKAGE__->config( action => { three => { Does => '+Moo' } } ); Thank you. -- seth /\ sethdaniel.org
I've chosen to fix this in a different way to you, however thanks for the illustrative patch and explanation of the bug. Commit e95b2b4 in the repository, will be in the next release.