diff --git a/lib/Catalyst/Controller.pm b/lib/Catalyst/Controller.pm index 34ad3505f..7d15dfcd4 100644 --- a/lib/Catalyst/Controller.pm +++ b/lib/Catalyst/Controller.pm @@ -400,14 +400,22 @@ sub _parse_attrs { my %raw_attributes; foreach my $attr (@attrs) { - # Parse out :Foo(bar) into Foo => bar etc (and arrayify) - - if ( my ( $key, $value ) = ( $attr =~ /^(.*?)(?:\(\s*(.+?)?\s*\))?$/ ) ) + if ( my ( $key, $value ) = $attr =~ m{ + \A + (\S*?) # match the key e.g. Foo in example + (?: + \( \s* + (.+?)? # match attr content e.g. "bar" in example + \s* \) + )? + \z + }xms ) { if ( defined $value ) { - ( $value =~ s/^'(.*)'$/$1/ ) || ( $value =~ s/^"(.*)"/$1/ ); + # Unquote single/double quoted attr values e.g. Foo("bar") + $value =~ s/^(['"])(.*)\1$/$2/s; } push( @{ $raw_attributes{$key} }, $value ); } diff --git a/t/aggregate/live_component_controller_action_action.t b/t/aggregate/live_component_controller_action_action.t index 41e2b165c..9b8b7e14b 100644 --- a/t/aggregate/live_component_controller_action_action.t +++ b/t/aggregate/live_component_controller_action_action.t @@ -190,7 +190,50 @@ sub run_tests { my $action = eval $response->content; is_deeply $action->attributes->{extra_attribute}, [13]; is_deeply $action->attributes->{another_extra_attribute}, ['foo']; + + # Test a multi-line attribute on the action comes through as expected + is_deeply $action->attributes->{MultiLineAttr}, ["one\n two\n three"]; + # and a normal one e.g. `Foo('bar')` + is_deeply $action->attributes->{Foo}, ['bar']; + # and one without a value, e.g. `Baz` - note that the presence of + # the arrayref shows it was there + is_deeply $action->attributes->{Baz}, [undef]; } + + { + ok( my $response = request('http://localhost/action_action_eightpointfive'), + 'Request' ); + ok( $response->is_success, 'Response Successful 2xx' ); + is( $response->content_type, 'text/plain', 'Response Content-Type' ); + is( $response->header('X-Catalyst-Action'), + 'action_action_eightpointfive', 'Test Action' ); + is( + $response->header('X-Test-Class'), + 'TestApp::Controller::Action::Action', + 'Test Class' + ); + like( + $response->content, + qr/^bless\( .* 'Catalyst::Action' \)$/s, + 'Content is a serialized Catalyst::Action' + ); + + require Catalyst::Action; # when running against a remote server, we + # need to load the class in the test process + # to be able to introspect the action instance + # later. + my $action = eval $response->content; + is_deeply $action->attributes->{extra_attribute}, [13]; + + # Test a multi-line attribute on the action comes through as expected + is_deeply $action->attributes->{MultiLineAttrQuoted}, ["\n 'one'\n 'two'\n 'three'\n"]; + # and a normal one e.g. `Foo('bar')` + is_deeply $action->attributes->{Foo}, ['bar']; + # and one without a value, e.g. `Baz` - note that the presence of + # the arrayref shows it was there + is_deeply $action->attributes->{Baz}, [undef]; + } + { ok( my $response = request('http://localhost/action_action_nine'), 'Request' ); diff --git a/t/lib/TestApp/Controller/Action/Action.pm b/t/lib/TestApp/Controller/Action/Action.pm index 515fb2a42..74fb71051 100644 --- a/t/lib/TestApp/Controller/Action/Action.pm +++ b/t/lib/TestApp/Controller/Action/Action.pm @@ -53,7 +53,20 @@ sub action_action_seven : Global : ActionClass('~TestExtraArgsAction') { $c->forward('TestApp::View::Dump::Request'); } -sub action_action_eight : Global { +sub action_action_eight : Global Foo('bar') MultiLineAttr( + one + two + three +) Baz { + my ( $self, $c ) = @_; + $c->forward('TestApp::View::Dump::Action'); +} + +sub action_action_eightpointfive : Global Foo('bar') MultiLineAttrQuoted(" + 'one' + 'two' + 'three' +") Baz { my ( $self, $c ) = @_; $c->forward('TestApp::View::Dump::Action'); } @@ -62,4 +75,5 @@ sub action_action_nine : Global : ActionClass('~TestActionArgsFromConstructor') my ( $self, $c ) = @_; $c->forward('TestApp::View::Dump::Request'); } + 1;