The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
Changes 034
MANIFEST 04
META.yml 23
Makefile.PL 02
inc/Module/AutoInstall.pm 12107
inc/Module/Install/AutoInstall.pm 112
inc/Module/Install/Base.pm 11
inc/Module/Install/Can.pm 11
inc/Module/Install/Fetch.pm 11
inc/Module/Install/Include.pm 11
inc/Module/Install/Makefile.pm 11
inc/Module/Install/Metadata.pm 33
inc/Module/Install/Scripts.pm 11
inc/Module/Install/Win32.pm 11
inc/Module/Install/WriteAll.pm 11
inc/Module/Install.pm 11
lib/Catalyst/Action.pm 021
lib/Catalyst/ActionChain.pm 315
lib/Catalyst/ClassData.pm 04
lib/Catalyst/Component/ContextClosure.pm 04
lib/Catalyst/Component.pm 22
lib/Catalyst/Controller.pm 11
lib/Catalyst/Dispatcher.pm 22
lib/Catalyst/Request/Upload.pm 35
lib/Catalyst/Request.pm 35
lib/Catalyst/Runtime.pm 11
lib/Catalyst/Script/CGI.pm 04
lib/Catalyst/Script/Create.pm 04
lib/Catalyst/Script/FastCGI.pm 04
lib/Catalyst/Script/Server.pm 04
lib/Catalyst/Script/Test.pm 04
lib/Catalyst/ScriptRunner.pm 1365
lib/Catalyst/Stats.pm 02
lib/Catalyst/Test.pm 22
lib/Catalyst/Upgrading.pod 44
lib/Catalyst.pm 7660
t/aggregate/unit_core_scriptrunner.t 119
t/aggregate/unit_core_uri_for_action.t 338
t/author/podcoverage.t 01
t/author/spelling.t 023
t/lib/ScriptTestApp/TraitFor/Script/Bar.pm 010
t/lib/ScriptTestApp/TraitFor/Script/Foo.pm 010
t/lib/ScriptTestApp/TraitFor/Script.pm 010
t/lib/TestApp/View/Dump/Action.pm 11
t/lib/TestApp/View/Dump.pm 46
45 files changed (This is a version diff) 156495
@@ -1,5 +1,39 @@
 # This file documents the revision history for Perl extension Catalyst.
 
+5.90005 - 2011-10-22 13:35:00
+
+  New features:
+
+   - $c->uri_for_action can now take an array of CaptureArgs and Args
+     If you have an action which has both, then you can now say:
+     $c->uri_for_action('/myaction', [@captures, @args]);
+     whereas before you had to say:
+     $c->uri_for_action('/myaction', [@captures], @args);
+     The previous form is still supported, however in many cases it is
+     easier for the application code to not have to differentiate between
+     the two.
+
+   - Catalyst::ScriptRunner has been enhanced so that it will now
+     load and apply traits, making it easier to customise.
+     - MyApp::TraitFor::Script (if it exists) will be applied to all
+       scripts in the application.
+     - MyApp::TraitFor::Script::XXXX will be applied to the relevant script
+       (for example MyApp::TraitFor::Script::Server will be applied to
+       MyApp::Script::Server if it exists, or Catalyst::Script::Server
+       otherwise).
+
+  Documentation:
+
+   - Document how to get the vhost of the request in $c->req->hostname
+     to avoid confusion
+   - Remove documentation showing Global / Regex / Private actionsi
+     as whilst these still exist (and work), they are not recommended.
+   - Remove references to the -Engine flag.
+   - Remove references to the deprecated Catalyst->plugin method
+   - Spelling fixed (and tested) throughout the documentation
+   - Note that wrapping the setup method will not work with method modifiers
+     and provide an alternative.
+
 5.90004 - 2011-10-11 17:12:00
 
   Bug fixes:
@@ -184,6 +184,7 @@ t/author/http-server.t
 t/author/notabs.t
 t/author/pod.t
 t/author/podcoverage.t
+t/author/spelling.t
 t/catalyst_130pix.gif
 t/conf/extra.conf.in
 t/custom_exception_class_simple.t
@@ -217,6 +218,9 @@ t/lib/PluginTestApp/Controller/Root.pm
 t/lib/ScriptTestApp/Script/Bar.pm
 t/lib/ScriptTestApp/Script/CompileTest.pm
 t/lib/ScriptTestApp/Script/Foo.pm
+t/lib/ScriptTestApp/TraitFor/Script.pm
+t/lib/ScriptTestApp/TraitFor/Script/Bar.pm
+t/lib/ScriptTestApp/TraitFor/Script/Foo.pm
 t/lib/TestApp.pm
 t/lib/TestApp/Action/TestBefore.pm
 t/lib/TestApp/Action/TestExtraArgsAction.pm
@@ -12,7 +12,7 @@ build_requires:
 configure_requires:
   ExtUtils::MakeMaker: 6.42
 distribution_type: module
-generated_by: 'Module::Install version 1.01'
+generated_by: 'Module::Install version 1.02'
 license: perl
 meta-spec:
   url: http://module-build.sourceforge.net/META-spec-v1.4.html
@@ -27,6 +27,7 @@ requires:
   CGI::Simple::Cookie: 1.109
   Carp: 0
   Class::C3::Adopt::NEXT: 0.07
+  Class::Load: 0.08
   Class::MOP: 0.95
   Data::Dump: 0
   Data::OptList: 0
@@ -73,4 +74,4 @@ resources:
   homepage: http://dev.catalyst.perl.org/
   license: http://dev.perl.org/licenses/
   repository: git://git.shadowcat.co.uk/catagits/Catalyst-Runtime.git
-version: 5.90004
+version: 5.90005
@@ -18,6 +18,7 @@ requires 'namespace::autoclean' => '0.09';
 requires 'namespace::clean' => '0.13';
 requires 'B::Hooks::EndOfScope' => '0.08';
 requires 'MooseX::Emulate::Class::Accessor::Fast' => '0.00903';
+requires 'Class::Load' => '0.08';
 requires 'Class::MOP' => '0.95';
 requires 'Data::OptList';
 requires 'Moose' => '1.03';
@@ -88,6 +89,7 @@ author_requires(map {; $_ => 0 } qw(
   Test::NoTabs
   Test::Pod
   Test::Pod::Coverage
+  Test::Spelling
   Pod::Coverage::TrustPod
 ));
 
@@ -17,11 +17,14 @@ my %FeatureMap = (
 );
 
 # various lexical flags
-my ( @Missing, @Existing,  %DisabledTests, $UnderCPAN,     $HasCPANPLUS );
+my ( @Missing, @Existing,  %DisabledTests, $UnderCPAN, $InstallDepsTarget, $HasCPANPLUS );
 my (
-    $Config, $CheckOnly, $SkipInstall, $AcceptDefault, $TestOnly, $AllDeps
+    $Config, $CheckOnly, $SkipInstall, $AcceptDefault, $TestOnly, $AllDeps,
+    $UpgradeDeps
 );
-my ( $PostambleActions, $PostambleUsed );
+my ( $PostambleActions, $PostambleActionsNoTest, $PostambleActionsUpgradeDeps,
+    $PostambleActionsUpgradeDepsNoTest, $PostambleActionsListDeps,
+    $PostambleActionsListAllDeps, $PostambleUsed, $NoTest);
 
 # See if it's a testing or non-interactive session
 _accept_default( $ENV{AUTOMATED_TESTING} or ! -t STDIN ); 
@@ -31,6 +34,10 @@ sub _accept_default {
     $AcceptDefault = shift;
 }
 
+sub _installdeps_target {
+    $InstallDepsTarget = shift;
+}
+
 sub missing_modules {
     return @Missing;
 }
@@ -63,6 +70,11 @@ sub _init {
             __PACKAGE__->install( $Config, @Missing = split( /,/, $1 ) );
             exit 0;
         }
+	elsif ( $arg =~ /^--upgradedeps=(.*)$/ ) {
+	    $UpgradeDeps = 1;
+	    __PACKAGE__->install( $Config, @Missing = split( /,/, $1 ) );
+	    exit 0;
+	}
         elsif ( $arg =~ /^--default(?:deps)?$/ ) {
             $AcceptDefault = 1;
         }
@@ -125,7 +137,7 @@ sub import {
     # check entirely since we don't want to have to load (and configure)
     # an old CPAN just for a cosmetic message
 
-    $UnderCPAN = _check_lock(1) unless $SkipInstall;
+    $UnderCPAN = _check_lock(1) unless $SkipInstall || $InstallDepsTarget;
 
     while ( my ( $feature, $modules ) = splice( @args, 0, 2 ) ) {
         my ( @required, @tests, @skiptests );
@@ -207,6 +219,7 @@ sub import {
                 $CheckOnly
                 or ($mandatory and $UnderCPAN)
                 or $AllDeps
+                or $InstallDepsTarget
                 or _prompt(
                     qq{==> Auto-install the }
                       . ( @required / 2 )
@@ -237,10 +250,17 @@ sub import {
         }
     }
 
-    if ( @Missing and not( $CheckOnly or $UnderCPAN ) ) {
+    if ( @Missing and not( $CheckOnly or $UnderCPAN) ) {
         require Config;
-        print
-"*** Dependencies will be installed the next time you type '$Config::Config{make}'.\n";
+        my $make = $Config::Config{make};
+        if ($InstallDepsTarget) {
+            print
+"*** To install dependencies type '$make installdeps' or '$make installdeps_notest'.\n";
+        }
+        else {
+            print
+"*** Dependencies will be installed the next time you type '$make'.\n";
+        }
 
         # make an educated guess of whether we'll need root permission.
         print "    (You may need to do that as the 'root' user.)\n"
@@ -271,6 +291,10 @@ END_MESSAGE
 sub _check_lock {
     return unless @Missing or @_;
 
+    if ($ENV{PERL5_CPANM_IS_RUNNING}) {
+        return _running_under('cpanminus');
+    }
+
     my $cpan_env = $ENV{PERL5_CPAN_IS_RUNNING};
 
     if ($ENV{PERL5_CPANPLUS_IS_RUNNING}) {
@@ -332,6 +356,11 @@ sub install {
         }
     }
 
+    if ($UpgradeDeps) {
+	push @modules, @installed;
+	@installed = ();
+    }
+
     return @installed unless @modules;  # nothing to do
     return @installed if _check_lock(); # defer to the CPAN shell
 
@@ -463,6 +492,11 @@ sub _cpanplus_config {
 			} else {
 				die "*** Cannot convert option $key = '$value' to CPANPLUS version.\n";
 			}
+			push @config, 'prereqs', $value;
+		} elsif ( $key eq 'force' ) {
+		    push @config, $key, $value;
+		} elsif ( $key eq 'notest' ) {
+		    push @config, 'skiptest', $value;
 		} else {
 			die "*** Cannot convert option $key to CPANPLUS version.\n";
 		}
@@ -497,10 +531,14 @@ sub _install_cpan {
     # set additional options
     while ( my ( $opt, $arg ) = splice( @config, 0, 2 ) ) {
         ( $args{$opt} = $arg, next )
-          if $opt =~ /^force$/;    # pseudo-option
+          if $opt =~ /^(?:force|notest)$/;    # pseudo-option
         $CPAN::Config->{$opt} = $arg;
     }
 
+    if ($args{notest} && (not CPAN::Shell->can('notest'))) {
+	die "Your version of CPAN is too old to support the 'notest' pragma";
+    }
+
     local $CPAN::Config->{prerequisites_policy} = 'follow';
 
     while ( my ( $pkg, $ver ) = splice( @modules, 0, 2 ) ) {
@@ -519,8 +557,16 @@ sub _install_cpan {
                 delete $INC{$inc};
             }
 
-            my $rv = $args{force} ? CPAN::Shell->force( install => $pkg )
-                                  : CPAN::Shell->install($pkg);
+            my $rv = do {
+		if ($args{force}) {
+		    CPAN::Shell->force( install => $pkg )
+		} elsif ($args{notest}) {
+		    CPAN::Shell->notest( install => $pkg )
+		} else {
+		    CPAN::Shell->install($pkg)
+		}
+	    };
+
             $rv ||= eval {
                 $CPAN::META->instance( 'CPAN::Distribution', $obj->cpan_file, )
                   ->{install}
@@ -763,6 +809,35 @@ sub _make_args {
         : "\$(NOECHO) \$(NOOP)"
     );
 
+    my $deps_list = join( ',', @Missing, @Existing );
+
+    $PostambleActionsUpgradeDeps =
+        "\$(PERL) $0 --config=$config --upgradedeps=$deps_list";
+
+    my $config_notest =
+      join( ',', (UNIVERSAL::isa( $Config, 'HASH' ) ? %{$Config} : @{$Config}),
+	  'notest', 1 )
+      if $Config;
+
+    $PostambleActionsNoTest = (
+        ($missing and not $UnderCPAN)
+        ? "\$(PERL) $0 --config=$config_notest --installdeps=$missing"
+        : "\$(NOECHO) \$(NOOP)"
+    );
+
+    $PostambleActionsUpgradeDepsNoTest =
+        "\$(PERL) $0 --config=$config_notest --upgradedeps=$deps_list";
+
+    $PostambleActionsListDeps =
+        '@$(PERL) -le "print for @ARGV" '
+            . join(' ', map $Missing[$_], grep $_ % 2 == 0, 0..$#Missing);
+
+    my @all = (@Missing, @Existing);
+
+    $PostambleActionsListAllDeps =
+        '@$(PERL) -le "print for @ARGV" '
+            . join(' ', map $all[$_], grep $_ % 2 == 0, 0..$#all);
+
     return %args;
 }
 
@@ -797,11 +872,15 @@ sub Write {
 
 sub postamble {
     $PostambleUsed = 1;
+    my $fragment;
 
-    return <<"END_MAKE";
+    $fragment .= <<"AUTO_INSTALL" if !$InstallDepsTarget;
 
 config :: installdeps
 \t\$(NOECHO) \$(NOOP)
+AUTO_INSTALL
+
+    $fragment .= <<"END_MAKE";
 
 checkdeps ::
 \t\$(PERL) $0 --checkdeps
@@ -809,12 +888,28 @@ checkdeps ::
 installdeps ::
 \t$PostambleActions
 
+installdeps_notest ::
+\t$PostambleActionsNoTest
+
+upgradedeps ::
+\t$PostambleActionsUpgradeDeps
+
+upgradedeps_notest ::
+\t$PostambleActionsUpgradeDepsNoTest
+
+listdeps ::
+\t$PostambleActionsListDeps
+
+listalldeps ::
+\t$PostambleActionsListAllDeps
+
 END_MAKE
 
+    return $fragment;
 }
 
 1;
 
 __END__
 
-#line 1071
+#line 1178
@@ -6,7 +6,7 @@ use Module::Install::Base ();
 
 use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '1.01';
+	$VERSION = '1.02';
 	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
 }
@@ -73,6 +73,17 @@ sub auto_install {
     );
 }
 
+sub installdeps_target {
+    my ($self, @args) = @_;
+
+    $self->include('Module::AutoInstall');
+    require Module::AutoInstall;
+
+    Module::AutoInstall::_installdeps_target(1);
+
+    $self->auto_install(@args);
+}
+
 sub auto_install_now {
     my $self = shift;
     $self->auto_install(@_);
@@ -4,7 +4,7 @@ package Module::Install::Base;
 use strict 'vars';
 use vars qw{$VERSION};
 BEGIN {
-	$VERSION = '1.01';
+	$VERSION = '1.02';
 }
 
 # Suspend handler for "redefined" warnings
@@ -9,7 +9,7 @@ use Module::Install::Base ();
 
 use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '1.01';
+	$VERSION = '1.02';
 	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
 }
@@ -6,7 +6,7 @@ use Module::Install::Base ();
 
 use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '1.01';
+	$VERSION = '1.02';
 	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
 }
@@ -6,7 +6,7 @@ use Module::Install::Base ();
 
 use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '1.01';
+	$VERSION = '1.02';
 	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
 }
@@ -8,7 +8,7 @@ use Fcntl qw/:flock :seek/;
 
 use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '1.01';
+	$VERSION = '1.02';
 	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
 }
@@ -6,7 +6,7 @@ use Module::Install::Base ();
 
 use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '1.01';
+	$VERSION = '1.02';
 	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
 }
@@ -170,7 +170,7 @@ sub perl_version {
 	# Normalize the version
 	$version = $self->_perl_version($version);
 
-	# We don't support the reall old versions
+	# We don't support the really old versions
 	unless ( $version >= 5.005 ) {
 		die "Module::Install only supports 5.005 or newer (use ExtUtils::MakeMaker)\n";
 	}
@@ -582,7 +582,7 @@ sub bugtracker_from {
 sub requires_from {
 	my $self     = shift;
 	my $content  = Module::Install::_readperl($_[0]);
-	my @requires = $content =~ m/^use\s+([^\W\d]\w*(?:::\w+)*)\s+([\d\.]+)/mg;
+	my @requires = $content =~ m/^use\s+([^\W\d]\w*(?:::\w+)*)\s+(v?[\d\.]+)/mg;
 	while ( @requires ) {
 		my $module  = shift @requires;
 		my $version = shift @requires;
@@ -6,7 +6,7 @@ use Module::Install::Base ();
 
 use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '1.01';
+	$VERSION = '1.02';
 	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
 }
@@ -6,7 +6,7 @@ use Module::Install::Base ();
 
 use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '1.01';
+	$VERSION = '1.02';
 	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
 }
@@ -6,7 +6,7 @@ use Module::Install::Base ();
 
 use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '1.01';
+	$VERSION = '1.02';
 	@ISA     = qw{Module::Install::Base};
 	$ISCORE  = 1;
 }
@@ -31,7 +31,7 @@ BEGIN {
 	# This is not enforced yet, but will be some time in the next few
 	# releases once we can make sure it won't clash with custom
 	# Module::Install extensions.
-	$VERSION = '1.01';
+	$VERSION = '1.02';
 
 	# Storage for the pseudo-singleton
 	$MAIN    = undef;
@@ -88,6 +88,19 @@ sub compare {
     return $a1_args <=> $a2_args;
 }
 
+sub number_of_args {
+    my ( $self ) = @_;
+    return 0 unless exists $self->attributes->{Args};
+    return $self->attributes->{Args}[0];
+}
+
+sub number_of_captures {
+    my ( $self ) = @_;
+
+    return 0 unless exists $self->attributes->{CaptureArgs};
+    return $self->attributes->{CaptureArgs}[0] || 0;
+}
+
 __PACKAGE__->meta->make_immutable;
 
 1;
@@ -147,6 +160,14 @@ C<private_path> of an action is always suitable for passing to C<forward>.
 
 Returns the sub name of this action.
 
+=head2 number_of_args
+
+Returns the number of args this action expects. This is 0 if the action doesn't take any arguments and undef if it will take any number of arguments.
+
+=head2 number_of_captures
+
+Returns the number of captures this action expects for L<Chained|Catalyst::DispatchType::Chained> actions.
+
 =head2 meta
 
 Provided by Moose.
@@ -4,7 +4,6 @@ use Moose;
 extends qw(Catalyst::Action);
 
 has chain => (is => 'rw');
-
 no Moose;
 
 =head1 NAME
@@ -30,8 +29,8 @@ sub dispatch {
     my $last = pop(@chain);
     foreach my $action ( @chain ) {
         my @args;
-        if (my $cap = $action->attributes->{CaptureArgs}) {
-          @args = splice(@captures, 0, $cap->[0]);
+        if (my $cap = $action->number_of_captures) {
+          @args = splice(@captures, 0, $cap);
         }
         local $c->request->{arguments} = \@args;
         $action->dispatch( $c );
@@ -45,6 +44,15 @@ sub from_chain {
     return $self->new({ %$final, chain => $actions });
 }
 
+sub number_of_captures {
+    my ( $self ) = @_;
+    my $chain = $self->chain;
+    my $captures = 0;
+
+    $captures += $_->number_of_captures for @$chain;
+    return $captures;
+}
+
 __PACKAGE__->meta->make_immutable;
 1;
 
@@ -67,6 +75,10 @@ actions in order.
 Takes a list of Catalyst::Action objects and constructs and returns a
 Catalyst::ActionChain object representing a chain of these actions
 
+=head2 number_of_captures
+
+Returns the total number of captures for the entire chain of actions.
+
 =head2 meta
 
 Provided by Moose
@@ -81,8 +81,12 @@ L<Class::Accessor::Grouped>;
 
 =head1 AUTHOR
 
+=begin stopwords
+
 Guillermo Roditi
 
+=end stopwords
+
 =head1 COPYRIGHT
 
 This library is free software. You can redistribute it and/or modify it under
@@ -63,10 +63,14 @@ L<Catalyst::Controller>
 
 L<CatalystX::LeakChecker>
 
+=begin stopwords
+
 =head1 AUTHOR
 
 Florian Ragwitz E<lt>rafl@debian.orgE<gt>
 
+=end stopwords
+
 =head1 COPYRIGHT
 
 This library is free software. You can redistribute it and/or modify it under
@@ -53,7 +53,7 @@ Catalyst::Component - Catalyst Component Base Class
 This is the universal base class for Catalyst components
 (Model/View/Controller).
 
-It provides you with a generic new() for instantiation through Catalyst's
+It provides you with a generic new() for component construction through Catalyst's
 component loader with config() support and a process() method placeholder.
 
 =cut
@@ -180,7 +180,7 @@ The arguments are expected to be a hashref and are merged with the
 C<< __PACKAGE__->config >> hashref before calling C<< ->new >>
 to instantiate the component.
 
-You can override it in your components to do custom instantiation, using
+You can override it in your components to do custom construction, using
 something like this:
 
   sub COMPONENT {
@@ -482,7 +482,7 @@ Sets 'path_prefix', as described below.
 
 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.
+attribute definitions of actions composed from Roles.
 You can set arguments globally (for all actions of the controller) and
 specifically (for a single action).
 
@@ -588,8 +588,8 @@ sub _find_or_create_namespace_node {
 
 =head2 $self->setup_actions( $class, $context )
 
-Loads all of the preload dispatch types, registers their actions and then
-loads all of the postload dispatch types, and iterates over the tree of
+Loads all of the pre-load dispatch types, registers their actions and then
+loads all of the post-load dispatch types, and iterates over the tree of
 actions, displaying the debug information if appropriate.
 
 =cut
@@ -45,6 +45,8 @@ sub _build_basename {
 
 no Moose;
 
+=for stopwords uploadtmp
+
 =head1 NAME
 
 Catalyst::Request::Upload - handles file upload requests
@@ -129,9 +131,9 @@ Returns the size of the uploaded file in bytes.
 
 Returns a scalar containing the contents of the temporary file.
 
-Note that this method will cause the filehandle pointed to by
-C<< $upload->fh >> to be seeked to the start of the file,
-and the file handle to be put into binary mode.
+Note that this will cause the filehandle pointed to by C<< $upload->fh >> to
+be reset to the start of the file using seek and the file handle to be put
+into binary mode.
 
 =cut
 
@@ -138,6 +138,8 @@ sub query_params    { shift->query_parameters(@_) }
 sub path_info       { shift->path(@_) }
 sub snippets        { shift->captures(@_) }
 
+=for stopwords param params
+
 =head1 NAME
 
 Catalyst::Request - provides information about the current client request
@@ -228,7 +230,7 @@ Shortcut for L</arguments>.
 =head2 $req->base
 
 Contains the URI base. This will always have a trailing slash. Note that the
-URI scheme (eg., http vs. https) must be determined through heuristics;
+URI scheme (e.g., http vs. https) must be determined through heuristics;
 depending on your server configuration, it may be incorrect. See $req->secure
 for more info.
 
@@ -316,7 +318,7 @@ Returns an L<HTTP::Headers> object containing the headers for the current reques
 
 =head2 $req->hostname
 
-Returns the hostname of the client.
+Returns the hostname of the client. Use C<< $req->uri->host >> to get the hostname of the server.
 
 =head2 $req->input
 
@@ -487,7 +489,7 @@ Shortcut for $req->headers->referer. Returns the referring page.
 =head2 $req->secure
 
 Returns true or false, indicating whether the connection is secure
-(https). Note that the URI scheme (eg., http vs. https) must be determined
+(https). Note that the URI scheme (e.g., http vs. https) must be determined
 through heuristics, and therefore the reliability of $req->secure will depend
 on your server configuration. If you are serving secure pages on the standard
 SSL port (443) and/or setting the HTTPS environment variable, $req->secure
@@ -7,7 +7,7 @@ BEGIN { require 5.008004; }
 
 # Remember to update this in Catalyst as well!
 
-our $VERSION = '5.90004';
+our $VERSION = '5.90005';
 
 =head1 NAME
 
@@ -23,6 +23,10 @@ Catalyst::Script::CGI - The CGI Catalyst Script
 
 This is a script to run the Catalyst engine specialized for the CGI environment.
 
+=head1 SEE ALSO
+
+L<Catalyst::ScriptRunner>
+
 =head1 AUTHORS
 
 Catalyst Contributors, see Catalyst.pm
@@ -90,6 +90,10 @@ Existing component files are not overwritten.  If any of the component files
 to be created already exist the file will be written with a '.new' suffix.
 This behavior can be suppressed with the C<--force> option.
 
+=head1 SEE ALSO
+
+L<Catalyst::ScriptRunner>
+
 =head1 AUTHORS
 
 Catalyst Contributors, see Catalyst.pm
@@ -153,6 +153,10 @@ Catalyst::Script::FastCGI - The FastCGI Catalyst Script
 
 Run a Catalyst application as fastcgi.
 
+=head1 SEE ALSO
+
+L<Catalyst::ScriptRunner>
+
 =head1 AUTHORS
 
 Catalyst Contributors, see Catalyst.pm
@@ -322,6 +322,10 @@ Catalyst::Script::Server - Catalyst test server
 
 Run a Catalyst test server for this application.
 
+=head1 SEE ALSO
+
+L<Catalyst::ScriptRunner>
+
 =head1 AUTHORS
 
 Catalyst Contributors, see Catalyst.pm
@@ -33,6 +33,10 @@ Catalyst::Script::Test - Test Catalyst application on the command line
 
 Script to perform a test hit against your application and display the output.
 
+=head1 SEE ALSO
+
+L<Catalyst::ScriptRunner>
+
 =head1 AUTHORS
 
 Catalyst Contributors, see Catalyst.pm
@@ -3,21 +3,48 @@ use Moose;
 use FindBin;
 use lib;
 use File::Spec;
-use namespace::autoclean;
+use Class::Load qw/ load_first_existing_class load_optional_class /;
+use namespace::autoclean -also => 'subclass_with_traits';
+use Try::Tiny;
+
+sub find_script_class {
+    my ($self, $app, $script) = @_;
+    return load_first_existing_class("${app}::Script::${script}", "Catalyst::Script::$script");
+}
+
+sub find_script_traits {
+    my ($self, @try) = @_;
+
+    return grep { load_optional_class($_) } @try;
+}
+
+sub subclass_with_traits {
+    my ($base, @traits) = @_;
+
+    my $meta = Class::MOP::class_of($base)->create_anon_class(
+        superclasses => [ $base   ],
+        roles        => [ @traits ],
+        cache        => 1,
+    );
+    $meta->add_method(meta => sub { $meta });
+
+    return $meta->name;
+}
 
 sub run {
-    my ($self, $class, $scriptclass, %args) = @_;
-    my $classtoload = "${class}::Script::$scriptclass";
+    my ($self, $appclass, $scriptclass) = @_;
 
     lib->import(File::Spec->catdir($FindBin::Bin, '..', 'lib'));
 
-    unless ( eval { Class::MOP::load_class($classtoload) } ) {
-        warn("Could not load $classtoload - falling back to Catalyst::Script::$scriptclass : $@\n")
-            if $@ !~ /Can't locate/;
-        $classtoload = "Catalyst::Script::$scriptclass";
-        Class::MOP::load_class($classtoload);
-    }
-    $classtoload->new_with_options( application_name => $class, %args )->run;
+    my $class = $self->find_script_class($appclass, $scriptclass);
+
+    my @possible_traits = ("${appclass}::TraitFor::Script::${scriptclass}", "${appclass}::TraitFor::Script");
+    my @traits = $self->find_script_traits(@possible_traits);
+
+    $class = subclass_with_traits($class, @traits)
+        if @traits;
+
+    $class->new_with_options( application_name => $appclass )->run;
 }
 
 __PACKAGE__->meta->make_immutable;
@@ -34,16 +61,41 @@ Catalyst::ScriptRunner - The Catalyst Framework script runner
 
 =head1 DESCRIPTION
 
-This class is responsible for running scripts, either in the application specific namespace
-(e.g. C<MyApp::Script::Server>), or the Catalyst namespace (e.g. C<Catalyst::Script::Server>)
+This class is responsible for loading and running scripts, either in the
+application specific namespace
+(e.g. C<MyApp::Script::Server>), or the Catalyst namespace (e.g. C<Catalyst::Script::Server>).
+
+If your application contains a custom script, then it will be used in preference to the generic
+script, and is expected to sub-class the standard script.
+
+=head1 TRAIT LOADING
+
+Catalyst will automatically load and apply roles to the scripts in your
+application.
+
+C<MyApp::TraitFor::Script> will be loaded if present, and will be applied to B<ALL>
+scripts.
+
+C<MyApp::TraitFor::Script::XXXX> will be loaded (if present) and for script
+individually.
 
 =head1 METHODS
 
 =head2 run ($application_class, $scriptclass)
 
-Called with two parameters, the application classs (e.g. MyApp)
+Called with two parameters, the application class (e.g. MyApp)
 and the script class, (i.e. one of Server/FastCGI/CGI/Create/Test)
 
+=head2 find_script_class ($appname, $script_name)
+
+Finds and loads the class for the script, trying the application specific
+script first, and falling back to the generic script. Returns the script
+which was loaded.
+
+=head2 find_script_traits ($appname, @try)
+
+Finds and loads a set of traits. Returns the list of traits which were loaded.
+
 =head1 AUTHORS
 
 Catalyst Contributors, see Catalyst.pm
@@ -166,6 +166,8 @@ __PACKAGE__->meta->make_immutable();
 
 __END__
 
+=for stopwords addChild getNodeValue mysub rollup setNodeValue
+
 =head1 NAME
 
 Catalyst::Stats - Catalyst Timing Statistics Class
@@ -209,8 +209,8 @@ functions take either a URI or an L<HTTP::Request> object.
 
 =head1 INLINE TESTS WILL NO LONGER WORK
 
-While it used to be possible to inline a whole testapp into a C<.t> file for a
-distribution, this will no longer work.
+While it used to be possible to inline a whole test app into a C<.t> file for
+a distribution, this will no longer work.
 
 The convention is to place your L<Catalyst> test apps into C<t/lib> in your
 distribution. E.g.: C<t/lib/TestApp.pm>, C<t/lib/TestApp/Controller/Root.pm>,
@@ -65,7 +65,7 @@ script is upgraded to use L<Catalyst::Script::HTTP>.
 If you were using L<Catalyst::Engine::CGI> there is no upgrade needed if your
 myapp_cgi.pl script is already upgraded to use L<Catalyst::Script::CGI>.
 
-=head2 Upgrading the Preforking Engine
+=head2 Upgrading Catalyst::Engine::HTTP::Prefork
 
 If you were using L<Catalyst::Engine::HTTP::Prefork> then L<Starman>
 is automatically loaded. You should (at least) change your C<Makefile.PL>
@@ -223,7 +223,7 @@ been made which could cause incompatibilities. If your application or plugin
 is using deprecated code, or relying on side effects, then you could have
 issues upgrading to this release.
 
-Most issues found with pre-existing components have been easy to
+Most issues found with existing components have been easy to
 solve. This document provides a complete description of behavior changes
 which may cause compatibility issues, and of new Catalyst warnings which
 might be unclear.
@@ -281,7 +281,7 @@ replaces L<NEXT> with L<Class::C3::Adopt::NEXT>, forcing all components
 to resolve methods using C3, rather than the unpredictable dispatch
 order of L<NEXT>.
 
-This issue is characterised by your application failing to start due to an
+This issue manifests itself by your application failing to start due to an
 error message about having a non-linear @ISA.
 
 The Catalyst plugin most often causing this is
@@ -488,7 +488,7 @@ The following test demonstrates the problem:
     use Test::More;
     isnt(BaseClass->can('foo'), Child->can('foo'));
 
-=head2 Extending Catalyst::Request or other classes in an ad-hoc manner using mk_accessors
+=head2 Extending Catalyst::Request or other classes in an ad hoc manner using mk_accessors
 
 Previously, it was possible to add additional accessors to Catalyst::Request
 (or other classes) by calling the mk_accessors class method.
@@ -84,7 +84,7 @@ __PACKAGE__->stats_class('Catalyst::Stats');
 
 # Remember to update this in Catalyst::Runtime as well!
 
-our $VERSION = '5.90004';
+our $VERSION = '5.90005';
 
 sub import {
     my ( $class, @arguments ) = @_;
@@ -148,7 +148,7 @@ documentation and tutorials.
     use Catalyst qw/-Debug/; # include plugins here as well
 
     ### In lib/MyApp/Controller/Root.pm (autocreated)
-    sub foo : Global { # called for /foo, /foo/1, /foo/1/2, etc.
+    sub foo : Chained('/') Args() { # called for /foo, /foo/1, /foo/1/2, etc.
         my ( $self, $c, @args ) = @_; # args are qw/1 2/ for /foo/1/2
         $c->stash->{template} = 'foo.tt'; # set the template
         # lookup something from db -- stash vars are passed to TT
@@ -166,50 +166,16 @@ documentation and tutorials.
     [% END %]
 
     # called for /bar/of/soap, /bar/of/soap/10, etc.
-    sub bar : Path('/bar/of/soap') { ... }
-
-    # called for all actions, from the top-most controller downwards
-    sub auto : Private {
-        my ( $self, $c ) = @_;
-        if ( !$c->user_exists ) { # Catalyst::Plugin::Authentication
-            $c->res->redirect( '/login' ); # require login
-            return 0; # abort request and go immediately to end()
-        }
-        return 1; # success; carry on to next action
-    }
+    sub bar : Chained('/') PathPart('/bar/of/soap') Args() { ... }
 
     # called after all actions are finished
-    sub end : Private {
+    sub end : Action {
         my ( $self, $c ) = @_;
         if ( scalar @{ $c->error } ) { ... } # handle errors
         return if $c->res->body; # already have a response
         $c->forward( 'MyApp::View::TT' ); # render template
     }
 
-    ### in MyApp/Controller/Foo.pm
-    # called for /foo/bar
-    sub bar : Local { ... }
-
-    # called for /blargle
-    sub blargle : Global { ... }
-
-    # an index action matches /foo, but not /foo/1, etc.
-    sub index : Private { ... }
-
-    ### in MyApp/Controller/Foo/Bar.pm
-    # called for /foo/bar/baz
-    sub baz : Local { ... }
-
-    # first Root auto is called, then Foo auto, then this
-    sub auto : Private { ... }
-
-    # powerful regular expression paths are also possible
-    sub details : Regex('^product/(\w+)/details$') {
-        my ( $self, $c ) = @_;
-        # extract the (\w+) from the URI
-        my $product = $c->req->captures->[0];
-    }
-
 See L<Catalyst::Manual::Intro> for additional information.
 
 =head1 DESCRIPTION
@@ -236,7 +202,7 @@ fully qualify the name by using a unary plus:
         +Fully::Qualified::Plugin::Name
     /;
 
-Special flags like C<-Debug> and C<-Engine> can also be specified as
+Special flags like C<-Debug> can also be specified as
 arguments when Catalyst is loaded:
 
     use Catalyst qw/-Debug My::Module/;
@@ -256,13 +222,6 @@ priority.
 This sets the log level to 'debug' and enables full debug output on the
 error screen. If you only want the latter, see L<< $c->debug >>.
 
-=head2 -Engine
-
-Forces Catalyst to use a specific engine. Omit the
-C<Catalyst::Engine::> prefix of the engine name, i.e.:
-
-    use Catalyst qw/-Engine=CGI/;
-
 =head2 -Home
 
 Forces Catalyst to use a specific home directory, e.g.:
@@ -276,11 +235,11 @@ the name will be replaced with underscores, e.g. MyApp::Web should use
 MYAPP_WEB_HOME. If both variables are set, the MYAPP_HOME one will be used.
 
 If none of these are set, Catalyst will attempt to automatically detect the
-home directory. If you are working in a development envirnoment, Catalyst
+home directory. If you are working in a development environment, Catalyst
 will try and find the directory containing either Makefile.PL, Build.PL or
 dist.ini. If the application has been installed into the system (i.e.
 you have done C<make install>), then Catalyst will use the path to your
-application module, without the .pm extension (ie, /foo/MyApp if your
+application module, without the .pm extension (e.g., /foo/MyApp if your
 application was installed at /foo/MyApp.pm)
 
 =head2 -Log
@@ -348,9 +307,10 @@ call to forward.
 
 Note that L<< forward|/"$c->forward( $action [, \@arguments ] )" >> implies
 an C<< eval { } >> around the call (actually
-L<< execute|/"$c->execute( $class, $coderef )" >> does), thus de-fatalizing
-all 'dies' within the called action. If you want C<die> to propagate you
-need to do something like:
+L<< execute|/"$c->execute( $class, $coderef )" >> does), thus rendering all
+exceptions thrown by the called action non-fatal and pushing them onto
+$c->error instead. If you want C<die> to propagate you need to do something
+like:
 
     $c->forward('foo');
     die join "\n", @{ $c->error } if @{ $c->error };
@@ -412,7 +372,7 @@ L<reverse|Catalyst::Action/reverse> return information for the visited action
 when they are invoked within the visited action.  This is different from the
 behavior of L<< forward|/"$c->forward( $action [, \@arguments ] )" >>, which
 continues to use the $c->action object from the caller action even when
-invoked from the callee.
+invoked from the called action.
 
 C<< $c->stash >> is kept unchanged.
 
@@ -1035,26 +995,11 @@ sub path_to {
     else { return Path::Class::File->new( $c->config->{home}, @path ) }
 }
 
-=head2 $c->plugin( $name, $class, @args )
-
-Helper method for plugins. It creates a class data accessor/mutator and
-loads and instantiates the given class.
-
-    MyApp->plugin( 'prototype', 'HTML::Prototype' );
-
-    $c->prototype->define_javascript_functions;
-
-B<Note:> This method of adding plugins is deprecated. The ability
-to add plugins like this B<will be removed> in a Catalyst 5.81.
-Please do not use this functionality in new code.
-
-=cut
-
 sub plugin {
     my ( $class, $name, $plugin, @args ) = @_;
 
     # See block comment in t/unit_core_plugin.t
-    $class->log->warn(qq/Adding plugin using the ->plugin method is deprecated, and will be removed in Catalyst 5.81/);
+    $class->log->warn(qq/Adding plugin using the ->plugin method is deprecated, and will be removed in a future release/);
 
     $class->_register_plugin( $plugin, 1 );
 
@@ -1083,6 +1028,9 @@ Catalyst> line.
     MyApp->setup;
     MyApp->setup( qw/-Debug/ );
 
+B<Note:> You B<should not> wrap this method with method modifiers
+or bad things will happen - wrap the C<setup_finalize> method instead.
+
 =cut
 
 sub setup {
@@ -1347,7 +1295,15 @@ sub uri_for {
         }
 
         my $action = $path;
-        $path = $c->dispatcher->uri_for_action($action, $captures);
+        # ->uri_for( $action, \@captures_and_args, \%query_values? )
+        if( !@args && $action->number_of_args ) {
+            my $expanded_action = $c->dispatcher->expand_action( $action );
+
+            my $num_captures = $expanded_action->number_of_captures;
+            unshift @args, splice @$captures, $num_captures;
+        }
+
+       $path = $c->dispatcher->uri_for_action($action, $captures);
         if (not defined $path) {
             $c->log->debug(qq/Can't find uri_for action '$action' @$captures/)
                 if $c->debug;
@@ -1398,9 +1354,9 @@ sub uri_for {
     $res;
 }
 
-=head2 $c->uri_for_action( $path, \@captures?, @args?, \%query_values? )
+=head2 $c->uri_for_action( $path, \@captures_and_args?, @args?, \%query_values? )
 
-=head2 $c->uri_for_action( $action, \@captures?, @args?, \%query_values? )
+=head2 $c->uri_for_action( $action, \@captures_and_args?, @args?, \%query_values? )
 
 =over
 
@@ -1429,6 +1385,30 @@ You can use:
 
 and it will create the URI /users/the-list.
 
+=item \@captures_and_args?
+
+Optional array reference of Captures (i.e. C<<CaptureArgs or $c->req->captures>)
+and arguments to the request. Usually used with L<Catalyst::DispatchType::Chained>
+to interpolate all the parameters in the URI.
+
+=item @args?
+
+Optional list of extra arguments - can be supplied in the C<< \@captures_and_args? >>
+array ref, or here - whichever is easier for your code..
+
+If your action may have a zero, a fixed or a variable number of args (e.g. C<< Args(1) >>
+for a fixed number or C<< Args() >> for a variable number)..
+
+=item \%query_values?
+
+Optional array reference of query parameters to append. E.g.
+
+  { foo => 'bar' }
+
+will generate
+
+  /rest/of/your/uri?foo=bar
+
 =back
 
 =cut
@@ -2258,7 +2238,7 @@ sub log_response_status_line {
 
 =head2 $c->log_response_headers($headers);
 
-Hook method which can be wrapped by plugins to log the responseheaders.
+Hook method which can be wrapped by plugins to log the response headers.
 No-op in the default implementation.
 
 =cut
@@ -2868,7 +2848,7 @@ sub setup_stats {
 =head2 $c->registered_plugins
 
 Returns a sorted list of the plugins which have either been stated in the
-import list or which have been added via C<< MyApp->plugin(@args); >>.
+import list.
 
 If passed a given plugin name, it will report a boolean value indicating
 whether or not that plugin is loaded.  A fully qualified name is required if
@@ -3039,7 +3019,7 @@ welcome screens
 
 C<parse_on_demand> - The request body (for example file uploads) will not be parsed
 until it is accessed. This allows you to (for example) check authentication (and reject
-the upload) before actually recieving all the data. See L</ON-DEMAND PARSER>
+the upload) before actually receiving all the data. See L</ON-DEMAND PARSER>
 
 =item *
 
@@ -3060,7 +3040,7 @@ to be shown in hit debug tables in the test server.
 
 =item *
 
-C<use_request_uri_for_path> - Controlls if the C<REQUEST_URI> or C<PATH_INFO> environment
+C<use_request_uri_for_path> - Controls if the C<REQUEST_URI> or C<PATH_INFO> environment
 variable should be used for determining the request path. 
 
 Most web server environments pass the requested path to the application using environment variables,
@@ -3075,7 +3055,7 @@ is determined by the C<< $c->config(use_request_uri_for_path) >> setting (which
 =item 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 path is generated 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.
 
@@ -3215,6 +3195,8 @@ Wiki:
 
 =head2 L<Catalyst::Test> - The test suite.
 
+=begin stopwords
+
 =head1 PROJECT FOUNDER
 
 sri: Sebastian Riedel <sri@cpan.org>
@@ -3357,6 +3339,8 @@ rainboxx: Matthias Dietrich, C<perl@rainboxx.de>
 
 dd070: Dhaval Dhanani <dhaval070@gmail.com>
 
+=end stopwords
+
 =head1 COPYRIGHT
 
 Copyright (c) 2005, the above named PROJECT FOUNDER and CONTRIBUTORS.
@@ -2,23 +2,21 @@ use strict;
 use warnings;
 use Test::More;
 use FindBin qw/$Bin/;
+use Test::Exception;
 use lib "$Bin/../lib";
 
 use_ok('Catalyst::ScriptRunner');
 
-is Catalyst::ScriptRunner->run('ScriptTestApp', 'Foo'), 'ScriptTestApp::Script::Foo',
-    'Script existing only in app';
-is Catalyst::ScriptRunner->run('ScriptTestApp', 'Bar'), 'ScriptTestApp::Script::Bar',
+is Catalyst::ScriptRunner->run('ScriptTestApp', 'Foo'), 'mooScriptTestApp::Script::Foo42',
+    'Script existing only in app got trait applied';
+is Catalyst::ScriptRunner->run('ScriptTestApp', 'Bar'), 'mooScriptTestApp::Script::Bar23',
     'Script existing in both app and Catalyst - prefers app';
-is Catalyst::ScriptRunner->run('ScriptTestApp', 'Baz'), 'Catalyst::Script::Baz',
+is Catalyst::ScriptRunner->run('ScriptTestApp', 'Baz'), 'mooCatalyst::Script::Baz',
     'Script existing only in Catalyst';
 # +1 test for the params passed to new_with_options in t/lib/Catalyst/Script/Baz.pm
-{
-    my $warnings = '';
-    local $SIG{__WARN__} = sub { $warnings .= shift };
-    is 'Catalyst::Script::CompileTest', Catalyst::ScriptRunner->run('ScriptTestApp', 'CompileTest');
-    like $warnings, qr/Does not compile/;
-    like $warnings, qr/Could not load ScriptTestApp::Script::CompileTest - falling back to Catalyst::Script::CompileTest/;
-}
+
+throws_ok(sub {
+    Catalyst::ScriptRunner->run('ScriptTestApp', 'CompileTest');
+}, qr/Couldn't load class/);
 
 done_testing;
@@ -8,8 +8,6 @@ use lib "$FindBin::Bin/../lib";
 
 use Test::More;
 
-plan tests => 33;
-
 use_ok('TestApp');
 
 my $dispatcher = TestApp->dispatcher;
@@ -143,10 +141,18 @@ is($context->uri_for($chained_action, [ 1 ], 2, { q => 1 }),
         'http://127.0.0.1/foo/chained/foo2/1/2/end2/3/4?x=5',
         'uri_for_action correct for chained with multiple captures and args' );
 
+    is( $context->uri_for_action( '/action/chained/endpoint2', [1,2,3,4], { x => 5 } ),
+        'http://127.0.0.1/foo/chained/foo2/1/2/end2/3/4?x=5',
+        'uri_for_action correct for chained with multiple captures and args combined' );
+
     is( $context->uri_for_action( '/action/chained/three_end', [1,2,3], (4,5,6) ),
         'http://127.0.0.1/foo/chained/one/1/two/2/3/three/4/5/6',
         'uri_for_action correct for chained with multiple capturing actions' );
 
+    is( $context->uri_for_action( '/action/chained/three_end', [1,2,3,4,5,6] ),
+        'http://127.0.0.1/foo/chained/one/1/two/2/3/three/4/5/6',
+        'uri_for_action correct for chained with multiple capturing actions and args combined' );
+
     my $action_needs_two = '/action/chained/endpoint2';
     
     ok( ! defined( $context->uri_for_action($action_needs_two, [1],     (2,3)) ),
@@ -155,7 +161,11 @@ is($context->uri_for($chained_action, [ 1 ], 2, { q => 1 }),
     is( $context->uri_for_action($action_needs_two,            [1,2],   (2,3)),
         'http://127.0.0.1/foo/chained/foo2/1/2/end2/2/3',
         'uri_for_action returns correct uri for correct captures' );
-        
+
+    is( $context->uri_for_action($action_needs_two,            [1,2,2,3]),
+        'http://127.0.0.1/foo/chained/foo2/1/2/end2/2/3',
+        'uri_for_action returns correct uri for correct captures and args combined' );
+
     ok( ! defined( $context->uri_for_action($action_needs_two, [1,2,3], (2,3)) ),
         'uri_for_action returns undef for too many captures' );
     
@@ -163,26 +173,49 @@ is($context->uri_for($chained_action, [ 1 ], 2, { q => 1 }),
         'http://127.0.0.1/foo/chained/foo2/1/2/end2/3',
         'uri_for_action returns uri with lesser args than specified on action' );
 
+    is( $context->uri_for_action($action_needs_two, [1,2,3]),
+        'http://127.0.0.1/foo/chained/foo2/1/2/end2/3',
+        'uri_for_action returns uri with lesser args than specified on action with captures combined' );
+
     is( $context->uri_for_action($action_needs_two, [1,2],   (3,4,5)),
         'http://127.0.0.1/foo/chained/foo2/1/2/end2/3/4/5',
         'uri_for_action returns uri with more args than specified on action' );
 
+    is( $context->uri_for_action($action_needs_two, [1,2,3,4,5]),
+        'http://127.0.0.1/foo/chained/foo2/1/2/end2/3/4/5',
+        'uri_for_action returns uri with more args than specified on action with captures combined' );
+
     is( $context->uri_for_action($action_needs_two, [1,''], (3,4)),
         'http://127.0.0.1/foo/chained/foo2/1//end2/3/4',
         'uri_for_action returns uri with empty capture on undef capture' );
 
+    is( $context->uri_for_action($action_needs_two, [1,'',3,4]),
+        'http://127.0.0.1/foo/chained/foo2/1//end2/3/4',
+        'uri_for_action returns uri with empty capture on undef capture and args combined' );
+
     is( $context->uri_for_action($action_needs_two, [1,2], ('',3)),
         'http://127.0.0.1/foo/chained/foo2/1/2/end2//3',
         'uri_for_action returns uri with empty arg on undef argument' );
 
+    is( $context->uri_for_action($action_needs_two, [1,2,'',3]),
+        'http://127.0.0.1/foo/chained/foo2/1/2/end2//3',
+        'uri_for_action returns uri with empty arg on undef argument and args combined' );
+
     is( $context->uri_for_action($action_needs_two, [1,2], (3,'')),
         'http://127.0.0.1/foo/chained/foo2/1/2/end2/3/',
         'uri_for_action returns uri with empty arg on undef last argument' );
 
+    is( $context->uri_for_action($action_needs_two, [1,2,3,'']),
+        'http://127.0.0.1/foo/chained/foo2/1/2/end2/3/',
+        'uri_for_action returns uri with empty arg on undef last argument with captures combined' );
+
     my $complex_chained = '/action/chained/empty_chain_f';
     is( $context->uri_for_action( $complex_chained, [23], (13), {q => 3} ),
         'http://127.0.0.1/foo/chained/empty/23/13?q=3',
         'uri_for_action returns correct uri for chain with many empty path parts' );
+    is( $context->uri_for_action( $complex_chained, [23,13], {q => 3} ),
+        'http://127.0.0.1/foo/chained/empty/23/13?q=3',
+        'uri_for_action returns correct uri for chain with many empty path parts with captures and args combined' );
 
     eval { $context->uri_for_action( '/does/not/exist' ) };
     like $@, qr{^Can't find action for path '/does/not/exist'},
@@ -190,3 +223,5 @@ is($context->uri_for($chained_action, [ 1 ], 2, { q => 1 }),
 
 }
 
+done_testing;
+
@@ -9,6 +9,7 @@ my @modules = all_modules;
 our @private = ( 'BUILD' );
 foreach my $module (@modules) {
     local @private = (@private, 'run') if $module =~ /^Catalyst::Script::/;
+    local @private = (@private, 'plugin') if $module =~ /^Catalyst$/;
     pod_coverage_ok($module, {
         also_private   => \@private,
         coverage_class => 'Pod::Coverage::TrustPod',
@@ -0,0 +1,23 @@
+use strict;
+use warnings;
+use Test::More;
+use Test::Spelling;
+
+add_stopwords(qw(
+    API CGI MVC PSGI Plack README SSI Starman XXXX URI htaccess middleware
+    mixins namespace psgi startup Deprecations catamoose cataplack linearize
+    subclasses subdirectories refactoring adaptors
+    undef env regex unary rethrow rethrows stringifies CPAN STDERR SIGCHLD baz
+    roadmap wishlist refactor refactored Runtime pluggable pluggability hoc apis
+    fastcgi nginx Lighttpd IIS middlewares backend IRC
+    ctx _application MyApp restarter httponly Utils stash's unescapes
+    dispatchtype dispatchtypes redispatch redispatching
+    CaptureArgs ChainedParent PathPart PathPrefix
+    BUILDARGS metaclass namespaces pre
+    filename tempname request's subdirectory ini uninstalled uppercased
+    wiki bitmask uri url urls dir hostname proxied http https IP SSL
+));
+set_spell_cmd('aspell list -l en');
+all_pod_files_spelling_ok();
+
+done_testing();
@@ -0,0 +1,10 @@
+package ScriptTestApp::TraitFor::Script::Bar;
+use Moose::Role;
+use namespace::autoclean;
+
+around run => sub {
+    my ($orig, $self, @args) = @_;
+    return $self->$orig(@args) . '23';
+};
+
+1;
@@ -0,0 +1,10 @@
+package ScriptTestApp::TraitFor::Script::Foo;
+use Moose::Role;
+use namespace::autoclean;
+
+around run => sub {
+    my ($orig, $self, @args) = @_;
+    return $self->$orig(@args) . '42';
+};
+
+1;
@@ -0,0 +1,10 @@
+package ScriptTestApp::TraitFor::Script;
+use Moose::Role;
+use namespace::autoclean;
+
+around run => sub {
+    my ($orig, $self, @args) = @_;
+    return 'moo' . $self->$orig(@args);
+};
+
+1;
@@ -5,7 +5,7 @@ use base qw[TestApp::View::Dump];
 
 sub process {
     my ( $self, $c ) = @_;
-    return $self->SUPER::process( $c, $c->action );
+    return $self->SUPER::process( $c, $c->action, 0 );
 }
 
 1;
@@ -7,13 +7,15 @@ use Data::Dumper ();
 use Scalar::Util qw(blessed weaken);
 
 sub dump {
-    my ( $self, $reference ) = @_;
+    my ( $self, $reference, $purity ) = @_;
 
     return unless $reference;
 
+    $purity = defined $purity ? $purity : 1;
+
     my $dumper = Data::Dumper->new( [$reference] );
     $dumper->Indent(1);
-    $dumper->Purity(1);
+    $dumper->Purity($purity);
     $dumper->Useqq(0);
     $dumper->Deepcopy(1);
     $dumper->Quotekeys(0);
@@ -23,7 +25,7 @@ sub dump {
 }
 
 sub process {
-    my ( $self, $c, $reference ) = @_;
+    my ( $self, $c, $reference, $purity ) = @_;
 
     # Force processing of on-demand data
     $c->prepare_body;
@@ -37,7 +39,7 @@ sub process {
     my $context = delete $reference->{_context};
 
     if ( my $output =
-        $self->dump( $reference ) )
+        $self->dump( $reference, $purity ) )
     {
 
         $c->res->headers->content_type('text/plain');