# This file documents the revision history for Perl extension Catalyst.
+5.80024 2010-05-15 11:55:44
+
+ Bug fixes:
+ - Revert the path resolution behaviour to how it used to work before
+ Catalyst 5.80014_02, so that application paths are (by default)
+ resolved from $ENV{PATH_INFO} and $ENV{SCRIPT_NAME}. This fixes backward
+ compatibility breakage seen by a number of people since that release
+ with mod_rewrite and SSI.
+
+ New features:
+ - Add a use_request_uri_for_path config setting to optionally
+ use the (more correct) $ENV{REQUEST_URI} path resolution behaviour.
+
+ Documentation:
+ - Clarify the documentation for the Catalyst::Stats interface.
+ - Copious documentation about the use_request_uri_for_path feature
+ and the implications of setting this to true/false in
+ Catalyst::Engine::CGI
+
+5.80023 2010-05-07 23:50:27
+
Bug fixes:
- - Additional fix for getting the base application path right when rewriting
- requests into an application sub path with mod_alias and mod_rewrite on
- Apache.
- Ensure to always cleanup temporary uploaded files in all cases, even
when exceptions occur during request processing, using HTTP::Body's
->cleanup feature. (RT#41442)
dereferencing it. (RT#49267)
- Fix regex special characters in REDIRECT_URL variable breaking
the request base. (2nd part of RT#24951)
+ - Fix not stripping backslashes in DispatchType::Regex::uri_for_action
New features:
- Setting __PACKAGE__->config(enable_catalyst_header => 1); in your MyApp.pm
HttpOnly flag
- Allow the myapp_test.pl script to be given a list of paths which it
will retrieve all of. (RT#53653)
+ - Allow parameterized roles to be applied as plugins.
+ - Allow requiring minimum versions of plugins when loading them.
Documentation:
- The Catalyst::Test::get method is documented as returning the raw
response bytes without any character decoding (RT#53678)
+ Cleanups:
+ - Removal of $Catalyst::PRETTY_VERSION. Future releases will always have the
+ full and unmangled version number, including trailing zeroes, in
+ $Catalyst::VERSION.
+
5.80022 2010-03-28 19:43:01
New features:
5.80021 2010-03-03 23:02:01
Bug fixed:
- - $c->uri_for will now escape unsafe characterss in captures
+ - $c->uri_for will now escape unsafe characters in captures
($c->request->captures) and correctly encode utf8 charracters.
5.80020 2010-02-04 06:51:18
requires 'B::Hooks::EndOfScope' => '0.08';
requires 'MooseX::Emulate::Class::Accessor::Fast' => '0.00903';
requires 'Class::MOP' => '0.95';
-requires 'Moose' => '0.93';
+requires 'Moose' => '1.03';
requires 'MooseX::MethodAttributes::Inheritable' => '0.19';
requires 'MooseX::Role::WithOverloading' => '0.05';
requires 'Carp';
requires 'Class::C3::Adopt::NEXT' => '0.07';
requires 'CGI::Simple::Cookie' => '1.109';
requires 'Data::Dump';
+requires 'Data::OptList';
requires 'HTML::Entities';
requires 'HTTP::Body' => '1.06'; # ->cleanup(1)
requires 'HTTP::Headers' => '1.64';
use Catalyst::Response;
use Catalyst::Utils;
use Catalyst::Controller;
+use Data::OptList;
use Devel::InnerPackage ();
use File::stat;
use Module::Pluggable::Object ();
# Remember to update this in Catalyst::Runtime as well!
-our $VERSION = '5.80022';
+our $VERSION = '5.80024';
sub import {
my ( $class, @arguments ) = @_;
=head2 -Stats
-Enables statistics collection and reporting. You can also force this setting
-from the system environment with CATALYST_STATS or <MYAPP>_STATS. The
-environment settings override the application, with <MYAPP>_STATS having the
-highest priority.
+Enables statistics collection and reporting.
-e.g.
+ use Catalyst qw/-Stats=1/;
- use Catalyst qw/-Stats=1/
+You can also force this setting from the system environment with CATALYST_STATS
+or <MYAPP>_STATS. The environment settings override the application, with
+<MYAPP>_STATS having the highest priority.
+
+Stats are also enabled if L<< debugging |/"-Debug" >> is enabled.
=head1 METHODS
carp "uri_for called with undef argument" if grep { ! defined $_ } @args;
foreach my $arg (@args) {
utf8::encode($arg) if utf8::is_utf8($arg);
- }
- s/([^$URI::uric])/$URI::Escape::escapes{$1}/go for @args;
- if (blessed $path) { # Action object only.
- s|/|%2F|g for @args;
+ $arg =~ s/([^$URI::uric])/$URI::Escape::escapes{$1}/go;
}
if ( blessed($path) ) { # action object
+ s|/|%2F|g for @args;
my $captures = [ map { s|/|%2F|g; $_; }
( scalar @args && ref $args[0] eq 'ARRAY'
? @{ shift(@args) }
$path = '/' if $path eq '';
}
- undef($path) if (defined $path && $path eq '');
-
unshift(@args, $path);
unless (defined $path && $path =~ s!^/!!) { # in-place strip
sub get_actions { my $c = shift; $c->dispatcher->get_actions( $c, @_ ) }
-=head2 $c->handle_request( $class, @arguments )
+=head2 $app->handle_request( @arguments )
Called to handle each HTTP request.
my ( $class, $plugins ) = @_;
$class->_plugins( {} ) unless $class->_plugins;
- $plugins ||= [];
+ $plugins = Data::OptList::mkopt($plugins || []);
- my @plugins = Catalyst::Utils::resolve_namespace($class . '::Plugin', 'Catalyst::Plugin', @$plugins);
+ my @plugins = map {
+ [ Catalyst::Utils::resolve_namespace(
+ $class . '::Plugin',
+ 'Catalyst::Plugin', $_->[0]
+ ),
+ $_->[1],
+ ]
+ } @{ $plugins };
for my $plugin ( reverse @plugins ) {
- Class::MOP::load_class($plugin);
- my $meta = find_meta($plugin);
+ Class::MOP::load_class($plugin->[0], $plugin->[1]);
+ my $meta = find_meta($plugin->[0]);
next if $meta && $meta->isa('Moose::Meta::Role');
- $class->_register_plugin($plugin);
+ $class->_register_plugin($plugin->[0]);
}
my @roles =
- map { $_->name }
- grep { $_ && blessed($_) && $_->isa('Moose::Meta::Role') }
- map { find_meta($_) }
+ map { $_->[0]->name, $_->[1] }
+ grep { blessed($_->[0]) && $_->[0]->isa('Moose::Meta::Role') }
+ map { [find_meta($_->[0]), $_->[1]] }
@plugins;
Moose::Util::apply_all_roles(
Returns an arrayref of the internal execution stack (actions that are
currently executing).
+=head2 $c->stats
+
+Returns the current timing statistics object. By default Catalyst uses
+L<Catalyst::Stats|Catalyst::Stats>, but can be set otherwise with
+L<< stats_class|/"$c->stats_class" >>.
+
+Even if L<< -Stats|/"-Stats" >> is not enabled, the stats object is still
+available. By enabling it with C< $c->stats->enabled(1) >, it can be used to
+profile explicitly, although MyApp.pm still won't profile nor output anything
+by itself.
+
=head2 $c->stats_class
-Returns or sets the stats (timing statistics) class.
+Returns or sets the stats (timing statistics) class. L<Catalyst::Stats|Catalyst::Stats> is used by default.
=head2 $c->use_stats
-Returns 1 when stats collection is enabled. Stats collection is enabled
-when the -Stats options is set, debug is on or when the <MYAPP>_STATS
-environment variable is set.
+Returns 1 when L<< stats collection|/"-Stats" >> is enabled.
Note that this is a static method, not an accessor and should be overridden
by declaring C<sub use_stats { 1 }> in your MyApp.pm, not by calling C<< $c->use_stats(1) >>.
=item *
+C<use_request_uri_for_path> - Controlls if the C<REQUEST_URI> or C<PATH_INFO> environment
+variable should be used for determining the request path. See L<Catalyst::Engine::CGI/PATH DECODING>
+for more information.
+
+=item *
+
C<using_frontend_proxy> - See L</PROXY SUPPORT>.
=back
Robert Sedlacek C<< <rs@474.at> >>
+SpiceMan: Marcel Montes
+
sky: Arthur Bergman
szbalint: Balint Szilakszi <szbalint@cpan.org>
willert: Sebastian Willert <willert@cpan.org>
+wreis: Wallace Reis <wallace@reis.org.br>
+
Yuval Kogman, C<nothingmuch@woobling.org>
=head1 LICENSE
Sets 'path_prefix', as described below.
+=head2 action
+
+Allows you to set the attributes that the dispatcher creates actions out of.
+This allows you to do 'rails style routes', or override some of the
+attribute defintions of actions composed from Roles.
+You can set arguments globally (for all actions of the controller) and
+specifically (for a single action).
+
+ __PACKAGE__->config(
+ action => {
+ '*' => { Chained => 'base', Args => 0 },
+ base => { Chained => '/', PathPart => '', CaptureArgs => 0 },
+ },
+ );
+
+In the case above every sub in the package would be made into a Chain
+endpoint with a URI the same as the sub name for each sub, chained
+to the sub named C<base>. Ergo dispatch to C</example> would call the
+C<base> method, then the C<example> method.
+
=head2 action_args
Allows you to set constructor arguments on your actions. You can set arguments
-globally (for all actions of the controller) and specifically (for a single
-action). This is particularly useful when using C<ActionRole>s
+globally and specifically (as above).
+This is particularly useful when using C<ActionRole>s
(L<Catalyst::Controller::ActionRole>) and custom C<ActionClass>es.
__PACKAGE__->config(
my $re = "$orig";
$re =~ s/^\^//;
$re =~ s/\$$//;
+ $re =~ s/\\([^\\])/$1/g;
my $final = '/';
my @captures = @$captures;
while (my ($front, $rest) = split(/\(/, $re, 2)) {
This is the Catalyst engine specialized for the CGI environment.
+=head1 PATH DECODING
+
+Most web server environments pass the requested path to the application using environment variables,
+from which Catalyst has to reconstruct the request base (i.e. the top level path to / in the application,
+exposed as C<< $c->request->base >>) and the request path below that base.
+
+There are two methods of doing this, both of which have advantages and disadvantages. Which method is used
+is determined by the C<< $c->config(use_request_uri_for_path) >> setting (which can either be true or false).
+
+=head2 use_request_uri_for_path => 0
+
+This is the default (and the) traditional method that Catalyst has used for determining the path information.
+The path is synthesised from a combination of the C<PATH_INFO> and C<SCRIPT_NAME> environment variables.
+The allows the application to behave correctly when C<mod_rewrite> is being used to redirect requests
+into the application, as these variables are adjusted by mod_rewrite to take account for the redirect.
+
+However this method has the major disadvantage that it is impossible to correctly decode some elements
+of the path, as RFC 3875 says: "C<< Unlike a URI path, the PATH_INFO is not URL-encoded, and cannot
+contain path-segment parameters. >>" This means PATH_INFO is B<always> decoded, and therefore Catalyst
+can't distinguish / vs %2F in paths (in addition to other encoded values).
+
+=head2 use_request_uri_for_path => 1
+
+This method uses the C<REQUEST_URI> and C<SCRIPT_NAME> environment variables. As C<REQUEST_URI> is never
+decoded, this means that applications using this mode can correctly handle URIs including the %2F character
+(i.e. with C<AllowEncodedSlashes> set to C<On> in Apache).
+
+Given that this method of path resolution is provably more correct, it is recommended that you use
+this unless you have a specific need to deploy your application in a non-standard environment, and you are
+aware of the implications of not being able to handle encoded URI paths correctly.
+
+However it also means that in a number of cases when the app isn't installed directly at a path, but instead
+is having paths rewritten into it (e.g. as a .cgi/fcgi in a public_html directory, with mod_rewrite in a
+.htaccess file, or when SSI is used to rewrite pages into the app, or when sub-paths of the app are exposed
+at other URIs than that which the app is 'normally' based at with C<mod_rewrite>), the resolution of
+C<< $c->request->base >> will be incorrect.
+
=head1 OVERLOADED METHODS
This class overloads some methods from C<Catalyst::Engine>.
else {
$base_path = $script_name || '/';
}
-# $base_path .= '/' unless $base_path =~ m{/$};
# If we are running as a backend proxy, get the true hostname
PROXY_CHECK:
}
}
- # RFC 3875: "Unlike a URI path, the PATH_INFO is not URL-encoded,
- # and cannot contain path-segment parameters." This means PATH_INFO
- # is always decoded, and the script can't distinguish / vs %2F.
- # See https://issues.apache.org/bugzilla/show_bug.cgi?id=35256
- # Here we try to resurrect the original encoded URI from REQUEST_URI.
my $path_info = $ENV{PATH_INFO};
- if (my $req_uri = $ENV{REQUEST_URI}) {
- $req_uri =~ s/^\Q$base_path\E//;
- $req_uri =~ s/\?.*$//;
- if ($req_uri && $req_uri ne '/') {
- # This means that REQUEST_URI needs information from PATH_INFO
- # prepending to it to be useful, otherwise the sub path which is
- # being redirected to becomes the app base address which is
- # incorrect.
- my ($match) = $req_uri =~ m{^(/?[^/]+)};
- my ($path_info_part) = $path_info =~ m|^(.*?\Q$match\E)|;
- substr($req_uri, 0, length($match), $path_info_part)
- if $path_info_part;
- $path_info = $req_uri;
+ if ($c->config->{use_request_uri_for_path}) {
+ # RFC 3875: "Unlike a URI path, the PATH_INFO is not URL-encoded,
+ # and cannot contain path-segment parameters." This means PATH_INFO
+ # is always decoded, and the script can't distinguish / vs %2F.
+ # See https://issues.apache.org/bugzilla/show_bug.cgi?id=35256
+ # Here we try to resurrect the original encoded URI from REQUEST_URI.
+ if (my $req_uri = $ENV{REQUEST_URI}) {
+ if (defined $script_name) {
+ $req_uri =~ s/^\Q$script_name\E//;
+ }
+ $req_uri =~ s/\?.*$//;
+ $path_info = $req_uri if $req_uri;
}
}
# Remember to update this in Catalyst as well!
-our $VERSION='5.80022';
+our $VERSION = '5.80024';
=head1 NAME
my $response;
- ok( $response = request("http://localhost/args/args/$path"), "Requested args for path $path");
+ ok( $response = request("http://localhost/args/args/$path"), "Requested /args/args/$path");
is( $response->content, $test, "$test as args" );
undef $response;
- ok( $response = request("http://localhost/args/params/$path"), "Requested params for path $path");
+ ok( $response = request("http://localhost/args/params/$path"), "Requested /args/params/$path");
- is( $response->content, $test, "$test as params" );
+ is( $response->content, $test, "response content $test as params" );
undef $response;
--- /dev/null
+use strict;
+use warnings;
+use FindBin qw/$Bin/;
+use lib "$FindBin::Bin/../lib";
+use Test::More;
+use URI;
+
+use_ok('TestApp');
+
+my $request = Catalyst::Request->new( {
+ base => URI->new('http://127.0.0.1/foo')
+ } );
+my $dispatcher = TestApp->dispatcher;
+my $context = TestApp->new( {
+ request => $request,
+ namespace => 'yada',
+ } );
+
+is( $context->hello_lazy, 'hello there', '$context->hello_lazy');
+eval { is( $context->hello_notlazy, 'hello there', '$context->hello_notlazy') };
+TODO: {
+ local $TODO = 'we appear to have a lazy bug';
+ if ($@) {
+ fail('$context->hello_notlazy');
+ warn $@;
+ }
+}
+
+done_testing;
+
# mod_rewrite to app root for non / based app
{
- my $r = get_req (
+ my $r = get_req (0,
REDIRECT_URL => '/comics/',
SCRIPT_NAME => '/comics/dispatch.cgi',
REQUEST_URI => '/comics/',
);
- is ''.$r->uri, 'http://www.foo.com/comics/';
- is ''.$r->base, 'http://www.foo.com/comics/';
+ is ''.$r->uri, 'http://www.foo.com/comics/', 'uri is correct';
+ is ''.$r->base, 'http://www.foo.com/comics/', 'base is correct';
}
# mod_rewrite to sub path under app root for non / based app
{
- my $r = get_req (
+ my $r = get_req (0,
PATH_INFO => '/foo/bar.gif',
REDIRECT_URL => '/comics/foo/bar.gif',
SCRIPT_NAME => '/comics/dispatch.cgi',
# Standard CGI hit for non / based app
{
- my $r = get_req (
+ my $r = get_req (0,
PATH_INFO => '/static/css/blueprint/screen.css',
SCRIPT_NAME => '/~bobtfish/Gitalist/script/gitalist.cgi',
REQUEST_URI => '/~bobtfish/Gitalist/script/gitalist.cgi/static/css/blueprint/screen.css',
}
# / %2F %252F escaping case.
{
- my $r = get_req (
+ my $r = get_req (1,
PATH_INFO => '/%2F/%2F',
SCRIPT_NAME => '/~bobtfish/Gitalist/script/gitalist.cgi',
REQUEST_URI => '/~bobtfish/Gitalist/script/gitalist.cgi/%252F/%252F',
);
- is ''.$r->uri, 'http://www.foo.com/~bobtfish/Gitalist/script/gitalist.cgi/%252F/%252F';
- is ''.$r->base, 'http://www.foo.com/~bobtfish/Gitalist/script/gitalist.cgi/';
+ is ''.$r->uri, 'http://www.foo.com/~bobtfish/Gitalist/script/gitalist.cgi/%252F/%252F', 'uri correct';
+ is ''.$r->base, 'http://www.foo.com/~bobtfish/Gitalist/script/gitalist.cgi/', 'base correct';
}
# Using rewrite rules to ask for a sub-path in your app.
# E.g. RewriteRule ^(.*)$ /path/to/fastcgi/domainprofi.fcgi/iframeredirect$1 [L,NS]
{
- my $r = get_req (
+ my $r = get_req (0,
PATH_INFO => '/iframeredirect/info',
SCRIPT_NAME => '',
REQUEST_URI => '/info',
# nginx example from espent with path /"foo"
{
- my $r = get_req (
+ my $r = get_req (0,
PATH_INFO => '"foo"',
SCRIPT_NAME => '/',
REQUEST_URI => '/%22foo%22',
# nginx example from espent with path /"foo" and the app based at /oslobilder
{
- my $r = get_req (
+ my $r = get_req (1,
PATH_INFO => 'oslobilder/"foo"',
SCRIPT_NAME => '/oslobilder/',
REQUEST_URI => '/oslobilder/%22foo%22',
);
- is ''.$r->path, '%22foo%22';
- is ''.$r->uri, 'http://www.foo.com/oslobilder/%22foo%22';
- is ''.$r->base, 'http://www.foo.com/oslobilder/';
+ is ''.$r->path, '%22foo%22', 'path correct';
+ is ''.$r->uri, 'http://www.foo.com/oslobilder/%22foo%22', 'uri correct';
+ is ''.$r->base, 'http://www.foo.com/oslobilder/', 'base correct';
}
{
- my $r = get_req (
+ local $TODO = 'Another mod_rewrite case';
+ my $r = get_req (0,
PATH_INFO => '/auth/login',
SCRIPT_NAME => '/tx',
REQUEST_URI => '/login',
);
- is ''.$r->path, 'auth/login';
- is ''.$r->uri, 'http://www.foo.com/tx/auth/login';
- is ''.$r->base, 'http://www.foo.com/tx/';
+ is ''.$r->path, 'auth/login', 'path correct';
+ is ''.$r->uri, 'http://www.foo.com/tx/auth/login', 'uri correct';
+ is ''.$r->base, 'http://www.foo.com/tx/', 'base correct';
}
# test req->base and c->uri_for work correctly after an internally redirected request
# (i.e. REDIRECT_URL set) when the PATH_INFO contains a regex
{
my $path = '/engine/request/uri/Rx(here)';
- my $r = get_req (
+ my $r = get_req (0,
SCRIPT_NAME => '/',
PATH_INFO => $path,
REQUEST_URI => $path,
# - Test scheme (secure request on port 80)
sub get_req {
+ my $use_request_uri_for_path = shift;
+
my %template = (
HTTP_HOST => 'www.foo.com',
PATH_INFO => '/',
local %ENV = (%template, @_);
my $i = TestApp->new;
+ $i->setup_finished(0);
+ $i->config(use_request_uri_for_path => $use_request_uri_for_path);
+ $i->setup_finished(1);
$i->engine(Catalyst::Engine::CGI->new);
$i->engine->prepare_path($i);
return $i->req;
use warnings;
use Test::More;
+use FindBin;
+use Path::Class;
+use File::Basename;
my %non_unix = (
MacOS => 1,
my $os = $non_unix{$^O} ? $^O : 'Unix';
-if( $os ne 'Unix' ) {
+if ( $os ne 'Unix' ) {
plan skip_all => 'tests require Unix';
}
-else {
- plan tests => 3;
-}
use_ok('Catalyst');
my $context = 'Catalyst';
+$context->setup_home;
+my $base = dir($FindBin::Bin)->relative->stringify;
+
+isa_ok( Catalyst::path_to( $context, $base ), 'Path::Class::Dir' );
+isa_ok( Catalyst::path_to( $context, $base, basename $0 ), 'Path::Class::File' );
+
my $config = Catalyst->config;
$config->{home} = '/home/sri/my-app/';
is( Catalyst::path_to( $context, 'foo', 'bar' ),
'/Users/sri/myapp/foo/bar', 'deep Unix path' );
+
+done_testing;
use strict;
use warnings;
-use Test::More tests => 24;
+use Test::More;
use lib 't/lib';
is_deeply [ TestApp->registered_plugins ], \@expected,
'registered_plugins() should only report the plugins for the current class';
+done_testing;
use Test::More;
-plan tests => 30;
+plan tests => 33;
use_ok('TestApp');
'/class_forward_test_method'
);
-warn $dispatcher->uri_for_action($private_action);
-
ok(!defined($dispatcher->uri_for_action($private_action)),
"Private action returns undef for URI");
"/action/regexp/foo/123",
"Regex action interpolates captures correctly");
+my $regex_action_bs = $dispatcher->get_action_by_path(
+ '/action/regexp/one_backslashes'
+ );
+
+ok(!defined($dispatcher->uri_for_action($regex_action_bs)),
+ "Regex action without captures returns undef");
+
+ok(!defined($dispatcher->uri_for_action($regex_action_bs, [ 1, 2, 3 ])),
+ "Regex action with too many captures returns undef");
+
+is($dispatcher->uri_for_action($regex_action_bs, [ 'foo', 123 ]),
+ "/action/regexp/foo/123.html",
+ "Regex action interpolates captures correctly");
+
+
#
# Index Action
#
package PluginTestApp;
use Test::More;
-use Catalyst qw(
- Test::Plugin
- +TestApp::Plugin::FullyQualified
- );
+use Catalyst (
+ 'Test::Plugin',
+ '+TestApp::Plugin::FullyQualified',
+ (eval { require MooseX::Role::Parameterized; 1 }
+ ? ('+TestApp::Plugin::ParameterizedRole' => { method_name => 'affe' })
+ : ()),
+);
sub _test_plugins {
my $c = shift;
ref($c)->plugin( faux => $faux_plugin );
isa_ok $c, 'Catalyst::Plugin::Test::Plugin';
+
+ # applied parameterized role
+ if (eval { require MooseX::Role::Parameterized; 1 }) {
+ can_ok $c, 'affe';
+ is $c->affe, 'birne', 'right method created by parameterized role';
+ }
+
isa_ok $c, 'TestApp::Plugin::FullyQualified';
ok !$c->isa($faux_plugin),
'... and it should not inherit from the instant plugin';
use Moose;
use namespace::autoclean;
+# -----------
+# t/aggregate/unit_core_ctx_attr.t pukes until lazy is true
+package Greeting;
+use Moose;
+sub hello_notlazy { 'hello there' }
+sub hello_lazy { 'hello there' }
+
+package TestApp;
+has 'my_greeting_obj_notlazy' => (
+ is => 'ro',
+ isa => 'Greeting',
+ default => sub { Greeting->new() },
+ handles => [ qw( hello_notlazy ) ],
+ lazy => 0,
+);
+has 'my_greeting_obj_lazy' => (
+ is => 'ro',
+ isa => 'Greeting',
+ default => sub { Greeting->new() },
+ handles => [ qw( hello_lazy ) ],
+ lazy => 1,
+);
+# -----------
+
our $VERSION = '0.01';
-TestApp->config( name => 'TestApp', root => '/some/dir' );
+TestApp->config( name => 'TestApp', root => '/some/dir', use_request_uri_for_path => 1 );
# Test bug found when re-adjusting the metaclass compat code in Moose
# in 292360. Test added to Moose in 4b760d6, but leave this attribute
);
}
+sub one_backslashes : Action Regex('^action/regexp/(\w+)/(\d+)\.html$') {
+ my ( $self, $c ) = @_;
+ $c->forward('TestApp::View::Dump::Request');
+}
+
1;
--- /dev/null
+package TestApp::Plugin::ParameterizedRole;
+
+use MooseX::Role::Parameterized;
+use namespace::autoclean;
+
+parameter method_name => (
+ isa => 'Str',
+ required => 1,
+);
+
+role {
+ my $p = shift;
+ my $method_name = $p->method_name;
+
+ method $method_name => sub { 'birne' };
+};
+
+1;