Tests, fix args as well as captureargs and be less violent - only encode /
[catagits/Catalyst-Runtime.git] / lib / Catalyst.pm
index 8805134..8d5113e 100644 (file)
@@ -1,10 +1,14 @@
 package Catalyst;
 
 use Moose;
+use Moose::Meta::Class ();
 extends 'Catalyst::Component';
+use Moose::Util qw/find_meta/;
 use bytes;
-use Scope::Upper ();
+use B::Hooks::EndOfScope ();
 use Catalyst::Exception;
+use Catalyst::Exception::Detach;
+use Catalyst::Exception::Go;
 use Catalyst::Log;
 use Catalyst::Request;
 use Catalyst::Request::Upload;
@@ -23,11 +27,12 @@ use URI::https;
 use Tree::Simple qw/use_weak_refs/;
 use Tree::Simple::Visitor::FindByUID;
 use Class::C3::Adopt::NEXT;
+use List::MoreUtils qw/uniq/;
 use attributes;
 use utf8;
 use Carp qw/croak carp shortmess/;
 
-BEGIN { require 5.008001; }
+BEGIN { require 5.008004; }
 
 has stack => (is => 'ro', default => sub { [] });
 has stash => (is => 'rw', default => sub { {} });
@@ -43,11 +48,9 @@ sub depth { scalar @{ shift->stack || [] }; }
 sub comp { shift->component(@_) }
 
 sub req {
-    # carp "the use of req() is deprecated in favour of request()";
     my $self = shift; return $self->request(@_);
 }
 sub res {
-    # carp "the use of res() is deprecated in favour of response()";
     my $self = shift; return $self->response(@_);
 }
 
@@ -58,14 +61,14 @@ sub finalize_output { shift->finalize_body(@_) };
 our $COUNT     = 1;
 our $START     = time;
 our $RECURSION = 1000;
-our $DETACH    = "catalyst_detach\n";
-our $GO        = "catalyst_go\n";
+our $DETACH    = Catalyst::Exception::Detach->new;
+our $GO        = Catalyst::Exception::Go->new;
 
 #I imagine that very few of these really need to be class variables. if any.
 #maybe we should just make them attributes with a default?
 __PACKAGE__->mk_classdata($_)
   for qw/components arguments dispatcher engine log dispatcher_class
-  engine_class context_class request_class response_class stats_class 
+  engine_class context_class request_class response_class stats_class
   setup_finished/;
 
 __PACKAGE__->dispatcher_class('Catalyst::Dispatcher');
@@ -76,7 +79,14 @@ __PACKAGE__->stats_class('Catalyst::Stats');
 
 # Remember to update this in Catalyst::Runtime as well!
 
-our $VERSION = '5.8000_06';
+our $VERSION = '5.80013';
+
+{
+    my $dev_version = $VERSION =~ /_\d{2}$/;
+    *_IS_DEVELOPMENT_VERSION = sub () { $dev_version };
+}
+
+$VERSION = eval $VERSION;
 
 sub import {
     my ( $class, @arguments ) = @_;
@@ -89,17 +99,18 @@ sub import {
     return if $caller eq 'main';
 
     # Kill Adopt::NEXT warnings if we're a non-RC version
-    if ($VERSION !~ /_\d{2}$/) {
+    unless (_IS_DEVELOPMENT_VERSION()) {
         Class::C3::Adopt::NEXT->unimport(qr/^Catalyst::/);
     }
 
     my $meta = Moose::Meta::Class->initialize($caller);
-    #Moose->import({ into => $caller }); #do we want to do this?
-
     unless ( $caller->isa('Catalyst') ) {
         my @superclasses = ($meta->superclasses, $class, 'Catalyst::Controller');
         $meta->superclasses(@superclasses);
     }
+    # Avoid possible C3 issues if 'Moose::Object' is already on RHS of MyApp
+    $meta->superclasses(grep { $_ ne 'Moose::Object' } $meta->superclasses);
+
     unless( $meta->has_method('meta') ){
         $meta->add_method(meta => sub { Moose::Meta::Class->initialize("${caller}") } );
     }
@@ -108,6 +119,8 @@ sub import {
     $caller->setup_home;
 }
 
+sub _application { $_[0] }
+
 =head1 NAME
 
 Catalyst - The Elegant MVC Web Application Framework
@@ -135,30 +148,30 @@ documentation and tutorials.
 
     ### in lib/MyApp.pm
     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.
         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
-        $c->stash->{data} = 
+        $c->stash->{data} =
           $c->model('Database::Foo')->search( { country => $args[0] } );
         if ( $c->req->params->{bar} ) { # access GET or POST parameters
             $c->forward( 'bar' ); # process another action
-            # do something else after forward returns            
+            # do something else after forward returns
         }
     }
-    
+
     # The foo.tt TT template can use the stash data from the database
     [% WHILE (item = data.next) %]
         [% item.foo %]
     [% 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 { 
+    sub auto : Private {
         my ( $self, $c ) = @_;
         if ( !$c->user_exists ) { # Catalyst::Plugin::Authentication
             $c->res->redirect( '/login' ); # require login
@@ -166,9 +179,9 @@ documentation and tutorials.
         }
         return 1; # success; carry on to next action
     }
-    
+
     # called after all actions are finished
-    sub end : Private { 
+    sub end : Private {
         my ( $self, $c ) = @_;
         if ( scalar @{ $c->error } ) { ... } # handle errors
         return if $c->res->body; # already have a response
@@ -178,20 +191,20 @@ documentation and tutorials.
     ### 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 ) = @_;
@@ -264,7 +277,7 @@ MYAPP_WEB_HOME. If both variables are set, the MYAPP_HOME one will be used.
 =head2 -Log
 
     use Catalyst '-Log=warn,fatal,error';
+
 Specifies a comma-delimited list of log levels.
 
 =head2 -Stats
@@ -274,7 +287,7 @@ from the system environment with CATALYST_STATS or <MYAPP>_STATS. The
 environment settings override the application, with <MYAPP>_STATS having the
 highest priority.
 
-e.g. 
+e.g.
 
    use Catalyst qw/-Stats=1/
 
@@ -323,9 +336,11 @@ call to forward.
     $c->forward(qw/MyApp::Model::DBIC::Foo do_stuff/);
     $c->forward('MyApp::View::TT');
 
-Note that forward implies an C<<eval { }>> around the call (actually
-C<execute> does), thus de-fatalizing all 'dies' within the called
-action. If you want C<die> to propagate you need to do something like:
+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:
 
     $c->forward('foo');
     die $c->error if $c->error;
@@ -334,6 +349,21 @@ Or make sure to always return true values from your actions and write
 your code like this:
 
     $c->forward('foo') || return;
+    
+Another note is that C<< $c->forward >> always returns a scalar because it
+actually returns $c->state which operates in a scalar context.
+Thus, something like:
+
+    return @array;
+    
+in an action that is forwarded to is going to return a scalar, 
+i.e. how many items are in that array, which is probably not what you want.
+If you need to return an array then return a reference to it, 
+or stash it like so:
+
+    $c->stash->{array} = \@array;
+
+and access it from the stash.
 
 =cut
 
@@ -345,8 +375,8 @@ sub forward { my $c = shift; no warnings 'recursion'; $c->dispatcher->forward( $
 
 =head2 $c->detach()
 
-The same as C<forward>, but doesn't return to the previous action when 
-processing is finished. 
+The same as L<< forward|/"$c->forward( $action [, \@arguments ] )" >>, but
+doesn't return to the previous action when processing is finished.
 
 When called with no arguments it escapes the processing chain entirely.
 
@@ -354,42 +384,49 @@ When called with no arguments it escapes the processing chain entirely.
 
 sub detach { my $c = shift; $c->dispatcher->detach( $c, @_ ) }
 
-=head2 $c->visit( $action [, \@arguments ] )
+=head2 $c->visit( $action [, \@captures, \@arguments ] )
 
-=head2 $c->visit( $class, $method, [, \@arguments ] )
+=head2 $c->visit( $class, $method, [, \@captures, \@arguments ] )
 
-Almost the same as C<forward>, but does a full dispatch, instead of just
-calling the new C<$action> / C<$class-E<gt>$method>. This means that C<begin>,
-C<auto> and the method you go to are called, just like a new request.
+Almost the same as L<< forward|/"$c->forward( $action [, \@arguments ] )" >>,
+but does a full dispatch, instead of just calling the new C<$action> /
+C<< $class->$method >>. This means that C<begin>, C<auto> and the method
+you go to are called, just like a new request.
 
 In addition both C<< $c->action >> and C<< $c->namespace >> are localized.
-This means, for example, that $c->action methods such as C<name>, C<class> and
-C<reverse> return information for the visited action when they are invoked
-within the visited action.  This is different from the behavior of C<forward>
-which continues to use the $c->action object from the caller action even when
+This means, for example, that C<< $c->action >> methods such as
+L<name|Catalyst::Action/name>, L<class|Catalyst::Action/class> and
+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.
 
-C<$c-E<gt>stash> is kept unchanged.
+C<< $c->stash >> is kept unchanged.
 
-In effect, C<visit> allows you to "wrap" another action, just as it
-would have been called by dispatching from a URL, while the analogous
-C<go> allows you to transfer control to another action as if it had
-been reached directly from a URL.
+In effect, L<< visit|/"$c->visit( $action [, \@captures, \@arguments ] )" >>
+allows you to "wrap" another action, just as it would have been called by
+dispatching from a URL, while the analogous
+L<< go|/"$c->go( $action [, \@captures, \@arguments ] )" >> allows you to
+transfer control to another action as if it had been reached directly from a URL.
 
 =cut
 
 sub visit { my $c = shift; $c->dispatcher->visit( $c, @_ ) }
 
-=head2 $c->go( $action [, \@arguments ] )
-
-=head2 $c->go( $class, $method, [, \@arguments ] )
+=head2 $c->go( $action [, \@captures, \@arguments ] )
 
-Almost the same as C<detach>, but does a full dispatch like C<visit>,
-instead of just calling the new C<$action> /
-C<$class-E<gt>$method>. This means that C<begin>, C<auto> and the
-method you visit are called, just like a new request.
+=head2 $c->go( $class, $method, [, \@captures, \@arguments ] )
 
-C<$c-E<gt>stash> is kept unchanged.
+The relationship between C<go> and 
+L<< visit|/"$c->visit( $action [, \@captures, \@arguments ] )" >> is the same as
+the relationship between 
+L<< forward|/"$c->forward( $class, $method, [, \@arguments ] )" >> and
+L<< detach|/"$c->detach( $action [, \@arguments ] )" >>. Like C<< $c->visit >>,
+C<< $c->go >> will perform a full dispatch on the specified action or method,
+with localized C<< $c->action >> and C<< $c->namespace >>. Like C<detach>,
+C<go> escapes the processing of the current request chain on completion, and
+does not return to its caller.
 
 =cut
 
@@ -414,7 +451,7 @@ Catalyst).
     $c->stash->{foo} = $bar;
     $c->stash( { moose => 'majestic', qux => 0 } );
     $c->stash( bar => 1, gorch => 2 ); # equivalent to passing a hashref
-    
+
     # stash is automatically passed to the view for use in a template
     $c->forward( 'MyApp::View::TT' );
 
@@ -468,7 +505,9 @@ sub error {
 
 =head2 $c->state
 
-Contains the return value of the last executed action.
+Contains the return value of the last executed action.   
+Note that << $c->state >> operates in a scalar context which means that all
+values it returns are scalar.
 
 =head2 $c->clear_errors
 
@@ -486,11 +525,17 @@ sub clear_errors {
     $c->error(0);
 }
 
-# search components given a name and some prefixes
 sub _comp_search_prefixes {
+    my $c = shift;
+    return map $c->components->{ $_ }, $c->_comp_names_search_prefixes(@_);
+}
+
+# search components given a name and some prefixes
+sub _comp_names_search_prefixes {
     my ( $c, $name, @prefixes ) = @_;
     my $appclass = ref $c || $c;
     my $filter   = "^${appclass}::(" . join( '|', @prefixes ) . ')::';
+    $filter = qr/$filter/; # Compile regex now rather than once per loop
 
     # map the original component name to the sub part that we will search against
     my %eligible = map { my $n = $_; $n =~ s{^$appclass\::[^:]+::}{}; $_ => $n; }
@@ -502,27 +547,34 @@ sub _comp_search_prefixes {
     my $query  = ref $name ? $name : qr/^$name$/i;
     my @result = grep { $eligible{$_} =~ m{$query} } keys %eligible;
 
-    return map { $c->components->{ $_ } } @result if @result;
+    return @result if @result;
 
     # if we were given a regexp to search against, we're done.
     return if ref $name;
 
+    # skip regexp fallback if configured
+    return
+        if $appclass->config->{disable_component_resolution_regex_fallback};
+
     # regexp fallback
     $query  = qr/$name/i;
-    @result = map { $c->components->{ $_ } } grep { $eligible{ $_ } =~ m{$query} } keys %eligible;
+    @result = grep { $eligible{ $_ } =~ m{$query} } keys %eligible;
 
     # no results? try against full names
     if( !@result ) {
-        @result = map { $c->components->{ $_ } } grep { m{$query} } keys %eligible;
+        @result = grep { m{$query} } keys %eligible;
     }
 
     # don't warn if we didn't find any results, it just might not exist
     if( @result ) {
-        my $msg = "Used regexp fallback for \$c->model('${name}'), which found '" .
+        # Disgusting hack to work out correct method name
+        my $warn_for = lc $prefixes[0];
+        my $msg = "Used regexp fallback for \$c->${warn_for}('${name}'), which found '" .
            (join '", "', @result) . "'. Relying on regexp fallback behavior for " .
            "component resolution is unreliable and unsafe.";
         my $short = $result[0];
-        $short =~ s/.*?Model:://;
+        # remove the component namespace prefix
+        $short =~ s/.*?(Model|Controller|View):://;
         my $shortmess = Carp::shortmess('');
         if ($shortmess =~ m#Catalyst/Plugin#) {
            $msg .= " You probably need to set '$short' instead of '${name}' in this " .
@@ -531,9 +583,9 @@ sub _comp_search_prefixes {
            $msg .= " You probably need to set '$short' instead of '${name}' in this " .
               "component's config";
         } else {
-           $msg .= " You probably meant \$c->model('$short') instead of \$c->model{'${name}'}, " .
+           $msg .= " You probably meant \$c->${warn_for}('$short') instead of \$c->${warn_for}('${name}'), " .
               "but if you really wanted to search, pass in a regexp as the argument " .
-              "like so: \$c->model(qr/${name}/)";
+              "like so: \$c->${warn_for}(qr/${name}/)";
         }
         $c->log->warn( "${msg}$shortmess" );
     }
@@ -541,14 +593,16 @@ sub _comp_search_prefixes {
     return @result;
 }
 
-# Find possible names for a prefix 
+# Find possible names for a prefix
 sub _comp_names {
     my ( $c, @prefixes ) = @_;
     my $appclass = ref $c || $c;
 
     my $filter = "^${appclass}::(" . join( '|', @prefixes ) . ')::';
 
-    my @names = map { s{$filter}{}; $_; } $c->_comp_search_prefixes( undef, @prefixes );
+    my @names = map { s{$filter}{}; $_; }
+        $c->_comp_names_search_prefixes( undef, @prefixes );
+
     return @names;
 }
 
@@ -559,7 +613,7 @@ sub _filter_component {
     if ( eval { $comp->can('ACCEPT_CONTEXT'); } ) {
         return $comp->ACCEPT_CONTEXT( $c, @args );
     }
-    
+
     return $comp;
 }
 
@@ -602,7 +656,7 @@ Gets a L<Catalyst::Model> instance by name.
 
 Any extra arguments are directly passed to ACCEPT_CONTEXT.
 
-If the name is omitted, it will look for 
+If the name is omitted, it will look for
  - a model object in $c->stash->{current_model_instance}, then
  - a model name in $c->stash->{current_model}, then
  - a config setting 'default_model', or
@@ -617,7 +671,7 @@ If you want to search for models, pass in a regexp as the argument.
 
 sub model {
     my ( $c, $name, @args ) = @_;
-
+    my $appclass = ref($c) || $c;
     if( $name ) {
         my @result = $c->_comp_search_prefixes( $name, qw/Model M/ );
         return map { $c->_filter_component( $_, @args ) } @result if ref $name;
@@ -625,19 +679,19 @@ sub model {
     }
 
     if (ref $c) {
-        return $c->stash->{current_model_instance} 
+        return $c->stash->{current_model_instance}
           if $c->stash->{current_model_instance};
         return $c->model( $c->stash->{current_model} )
           if $c->stash->{current_model};
     }
-    return $c->model( $c->config->{default_model} )
-      if $c->config->{default_model};
+    return $c->model( $appclass->config->{default_model} )
+      if $appclass->config->{default_model};
 
     my( $comp, $rest ) = $c->_comp_search_prefixes( undef, qw/Model M/);
 
     if( $rest ) {
         $c->log->warn( Carp::shortmess('Calling $c->model() will return a random model unless you specify one of:') );
-        $c->log->warn( '* $c->config->{default_model} # the name of the default model to use' );
+        $c->log->warn( '* $c->config(default_model => "the name of the default model to use")' );
         $c->log->warn( '* $c->stash->{current_model} # the name of the model to use for this request' );
         $c->log->warn( '* $c->stash->{current_model_instance} # the instance of the model to use for this request' );
         $c->log->warn( 'NB: in version 5.81, the "random" behavior will not work at all.' );
@@ -655,7 +709,7 @@ Gets a L<Catalyst::View> instance by name.
 
 Any extra arguments are directly passed to ACCEPT_CONTEXT.
 
-If the name is omitted, it will look for 
+If the name is omitted, it will look for
  - a view object in $c->stash->{current_view_instance}, then
  - a view name in $c->stash->{current_view}, then
  - a config setting 'default_view', or
@@ -671,6 +725,7 @@ If you want to search for views, pass in a regexp as the argument.
 sub view {
     my ( $c, $name, @args ) = @_;
 
+    my $appclass = ref($c) || $c;
     if( $name ) {
         my @result = $c->_comp_search_prefixes( $name, qw/View V/ );
         return map { $c->_filter_component( $_, @args ) } @result if ref $name;
@@ -678,19 +733,19 @@ sub view {
     }
 
     if (ref $c) {
-        return $c->stash->{current_view_instance} 
+        return $c->stash->{current_view_instance}
           if $c->stash->{current_view_instance};
         return $c->view( $c->stash->{current_view} )
           if $c->stash->{current_view};
     }
-    return $c->view( $c->config->{default_view} )
-      if $c->config->{default_view};
+    return $c->view( $appclass->config->{default_view} )
+      if $appclass->config->{default_view};
 
     my( $comp, $rest ) = $c->_comp_search_prefixes( undef, qw/View V/);
 
     if( $rest ) {
         $c->log->warn( 'Calling $c->view() will return a random view unless you specify one of:' );
-        $c->log->warn( '* $c->config->{default_view} # the name of the default view to use' );
+        $c->log->warn( '* $c->config(default_view => "the name of the default view to use")' );
         $c->log->warn( '* $c->stash->{current_view} # the name of the view to use for this request' );
         $c->log->warn( '* $c->stash->{current_view_instance} # the instance of the view to use for this request' );
         $c->log->warn( 'NB: in version 5.81, the "random" behavior will not work at all.' );
@@ -745,6 +800,12 @@ should be used instead.
 If C<$name> is a regexp, a list of components matched against the full
 component name will be returned.
 
+If Catalyst can't find a component by name, it will fallback to regex
+matching by default. To disable this behaviour set
+disable_component_resolution_regex_fallback to a true value.
+    
+    __PACKAGE__->config( disable_component_resolution_regex_fallback => 1 );
+
 =cut
 
 sub component {
@@ -796,13 +857,35 @@ Returns or takes a hashref containing the application's configuration.
 
     __PACKAGE__->config( { db => 'dsn:SQLite:foo.db' } );
 
-You can also use a C<YAML>, C<XML> or C<Config::General> config file
-like myapp.yml in your applications home directory. See
+You can also use a C<YAML>, C<XML> or L<Config::General> config file
+like C<myapp.conf> in your applications home directory. See
 L<Catalyst::Plugin::ConfigLoader>.
 
-    ---
-    db: dsn:SQLite:foo.db
+=head3 Cascading configuration
+
+The config method is present on all Catalyst components, and configuration
+will be merged when an application is started. Configuration loaded with
+L<Catalyst::Plugin::ConfigLoader> takes precedence over other configuration,
+followed by configuration in your top level C<MyApp> class. These two
+configurations are merged, and then configuration data whose hash key matches a
+component name is merged with configuration for that component.
+
+The configuration for a component is then passed to the C<new> method when a
+component is constructed.
+
+For example:
+
+    MyApp->config({ 'Model::Foo' => { bar => 'baz', overrides => 'me' } });
+    MyApp::Model::Foo->config({ quux => 'frob', 'overrides => 'this' });
 
+will mean that C<MyApp::Model::Foo> receives the following data when
+constructed:
+
+    MyApp::Model::Foo->new({
+        bar => 'baz',
+        quux => 'frob',
+        overrides => 'me',
+    });
 
 =cut
 
@@ -836,10 +919,23 @@ L<Catalyst::Log>.
 
 =head2 $c->debug
 
-Overload to enable debug messages (same as -Debug option).
+Returns 1 if debug mode is enabled, 0 otherwise.
+
+You can enable debug mode in several ways:
 
-Note that this is a static method, not an accessor and should be overloaded
-by declaring "sub debug { 1 }" in your MyApp.pm, not by calling $c->debug(1).
+=over
+
+=item By calling myapp_server.pl with the -d flag
+
+=item With the environment variables MYAPP_DEBUG, or CATALYST_DEBUG
+
+=item The -Debug option in your MyApp.pm
+
+=item By declaring C<sub debug { 1 }> in your MyApp.pm.
+
+=back
+
+Calling C<< $c->debug(1) >> has no effect.
 
 =cut
 
@@ -859,7 +955,9 @@ Returns the engine instance. See L<Catalyst::Engine>.
 =head2 $c->path_to(@path)
 
 Merges C<@path> with C<< $c->config->{home} >> and returns a
-L<Path::Class::Dir> object.
+L<Path::Class::Dir> object. Note you can usually use this object as
+a filename, but sometimes you will have to explicitly stringify it
+yourself by calling the C<< ->stringify >> method.
 
 For example:
 
@@ -882,9 +980,9 @@ 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.9.
+to add plugins like this B<will be removed> in a Catalyst 5.81.
 Please do not use this functionality in new code.
 
 =cut
@@ -892,9 +990,9 @@ Please do not use this functionality in new code.
 sub plugin {
     my ( $class, $name, $plugin, @args ) = @_;
 
-    # See block comment in t/unit_core_plugin.t    
-    $class->log->debug(qq/Adding plugin using the ->plugin method is deprecated, and will be removed in Catalyst 5.9/);
-    
+    # 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->_register_plugin( $plugin, 1 );
 
     eval { $plugin->import };
@@ -987,7 +1085,7 @@ You are running an old script!
 
 EOF
     }
-    
+
     if ( $class->debug ) {
         my @plugins = map { "$_  " . ( $_->VERSION || '' ) } $class->registered_plugins;
 
@@ -1013,10 +1111,11 @@ EOF
     }
 
     # Call plugins setup, this is stupid and evil.
+    # Also screws C3 badly on 5.10, hack to avoid.
     {
         no warnings qw/redefine/;
         local *setup = sub { };
-        $class->setup;
+        $class->setup unless $Catalyst::__AM_RESTARTING;
     }
 
     # Initialize our data structure
@@ -1048,16 +1147,28 @@ EOF
     }
     $class->log->_flush() if $class->log->can('_flush');
 
-    # Make sure that the application class becomes immutable at this point, 
-    # which ensures that it gets an inlined constructor. This means that it 
-    # works even if the user has added a plugin which contains a new method.
-    # Note however that we have to do the work on scope end, so that method
-    # modifiers work correctly in MyApp (as you have to call setup _before_ 
-    # applying modifiers).
-    Scope::Upper::reap(sub {
+    # Make sure that the application class becomes immutable at this point,
+    B::Hooks::EndOfScope::on_scope_end {
+        return if $@;
         my $meta = Class::MOP::get_metaclass_by_name($class);
-        $meta->make_immutable unless $meta->is_immutable;
-    }, Scope::Upper::SCOPE(1));
+        if (
+            $meta->is_immutable
+            && ! { $meta->immutable_options }->{replace_constructor}
+            && (
+                   $class->isa('Class::Accessor::Fast')
+                || $class->isa('Class::Accessor')
+            )
+        ) {
+            warn "You made your application class ($class) immutable, "
+                . "but did not inline the\nconstructor. "
+                . "This will break catalyst, as your app \@ISA "
+                . "Class::Accessor(::Fast)?\nPlease pass "
+                . "(replace_constructor => 1)\nwhen making your class immutable.\n";
+        }
+        $meta->make_immutable(
+            replace_constructor => 1,
+        ) unless $meta->is_immutable;
+    };
 
     $class->setup_finalize;
 }
@@ -1066,7 +1177,7 @@ EOF
 =head2 $app->setup_finalize
 
 A hook to attach modifiers to.
-Using C< after setup => sub{}; > doesn't work, because of quirky things done for plugin setup.
+Using C<< after setup => sub{}; >> doesn't work, because of quirky things done for plugin setup.
 Also better than C< setup_finished(); >, as that is a getter method.
 
     sub setup_finalize {
@@ -1087,42 +1198,66 @@ sub setup_finalize {
     $class->setup_finished(1);
 }
 
-=head2 $c->uri_for( $action, \@captures?, @args?, \%query_values? )
-
 =head2 $c->uri_for( $path, @args?, \%query_values? )
 
-=over
-
-=item $action
-
-A Catalyst::Action object representing the Catalyst action you want to
-create a URI for. To get one for an action in the current controller,
-use C<< $c->action('someactionname') >>. To get one from different
-controller, fetch the controller using C<< $c->controller() >>, then
-call C<action_for> on it.
+=head2 $c->uri_for( $action, \@captures?, @args?, \%query_values? )
 
-You can maintain the arguments captured by an action (e.g.: Regex, Chained)
-using C<< $c->req->captures >>. 
+Constructs an absolute L<URI> object based on the application root, the
+provided path, and the additional arguments and query parameters provided.
+When used as a string, provides a textual URI.
+
+If the first argument is a string, it is taken as a public URI path relative
+to C<< $c->namespace >> (if it doesn't begin with a forward slash) or
+relative to the application root (if it does). It is then merged with
+C<< $c->request->base >>; any C<@args> are appended as additional path
+components; and any C<%query_values> are appended as C<?foo=bar> parameters.
+
+If the first argument is a L<Catalyst::Action> it represents an action which
+will have its path resolved using C<< $c->dispatcher->uri_for_action >>. The
+optional C<\@captures> argument (an arrayref) allows passing the captured
+variables that are needed to fill in the paths of Chained and Regex actions;
+once the path is resolved, C<uri_for> continues as though a path was
+provided, appending any arguments or parameters and creating an absolute
+URI.
+
+The captures for the current request can be found in
+C<< $c->request->captures >>, and actions can be resolved using
+C<< Catalyst::Controller->action_for($name) >>. If you have a private action
+path, use C<< $c->uri_for_action >> instead.
+
+  # Equivalent to $c->req->uri
+  $c->uri_for($c->action, $c->req->captures,
+      @{ $c->req->args }, $c->req->params);
 
-  # For the current action
-  $c->uri_for($c->action, $c->req->captures);
-  
   # For the Foo action in the Bar controller
-  $c->uri_for($c->controller->('Bar')->action_for('Foo'), $c->req->captures);
+  $c->uri_for($c->controller('Bar')->action_for('Foo'));
 
-=back
+  # Path to a static resource
+  $c->uri_for('/static/images/logo.png');
 
 =cut
 
 sub uri_for {
     my ( $c, $path, @args ) = @_;
 
+    if (blessed($path) && $path->isa('Catalyst::Controller')) {
+        $path = $path->path_prefix;
+        $path =~ s{/+\z}{};
+        $path .= '/';
+    }
+
     if ( blessed($path) ) { # action object
-        my $captures = ( scalar @args && ref $args[0] eq 'ARRAY'
-                         ? shift(@args)
-                         : [] );
-        $path = $c->dispatcher->uri_for_action($path, $captures);
-        return undef unless defined($path);
+        my $captures = [ map { s|/|%2F|; $_; }
+                        ( scalar @args && ref $args[0] eq 'ARRAY'
+                         ? @{ shift(@args) }
+                         : ()) ];
+        my $action = $path;
+        $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;
+            return undef;
+        }
         $path = '/' if $path eq '';
     }
 
@@ -1133,6 +1268,7 @@ sub uri_for {
 
     carp "uri_for called with undef argument" if grep { ! defined $_ } @args;
     s/([^$URI::uric])/$URI::Escape::escapes{$1}/go for @args;
+    s|/|%2F| for @args;
 
     unshift(@args, $path);
 
@@ -1143,7 +1279,7 @@ sub uri_for {
         }
         unshift(@args, $namespace || '');
     }
-    
+
     # join args with '/', or a blank string
     my $args = join('/', grep { defined($_) } @args);
     $args =~ s/\?/%3F/g; # STUPID STUPID SPECIAL CASE
@@ -1163,12 +1299,12 @@ sub uri_for {
           my $key = $_;
           $val = '' unless defined $val;
           (map {
-              $_ = "$_";
-              utf8::encode( $_ ) if utf8::is_utf8($_);
+              my $param = "$_";
+              utf8::encode( $param ) if utf8::is_utf8($param);
               # using the URI::Escape pattern here so utf8 chars survive
-              s/([^A-Za-z0-9\-_.!~*'() ])/$URI::Escape::escapes{$1}/go;
-              s/ /+/g;
-              "${key}=$_"; } ( ref $val eq 'ARRAY' ? @$val : $val ));
+              $param =~ s/([^A-Za-z0-9\-_.!~*'() ])/$URI::Escape::escapes{$1}/go;
+              $param =~ s/ /+/g;
+              "${key}=$param"; } ( ref $val eq 'ARRAY' ? @$val : $val ));
       } @keys);
     }
 
@@ -1176,6 +1312,38 @@ sub uri_for {
     $res;
 }
 
+=head2 $c->uri_for_action( $path, \@captures?, @args?, \%query_values? )
+
+=head2 $c->uri_for_action( $action, \@captures?, @args?, \%query_values? )
+
+=over
+
+=item $path
+
+A private path to the Catalyst action you want to create a URI for.
+
+This is a shortcut for calling C<< $c->dispatcher->get_action_by_path($path)
+>> and passing the resulting C<$action> and the remaining arguments to C<<
+$c->uri_for >>.
+
+You can also pass in a Catalyst::Action object, in which case it is passed to
+C<< $c->uri_for >>.
+
+=back
+
+=cut
+
+sub uri_for_action {
+    my ( $c, $path, @args ) = @_;
+    my $action = blessed($path)
+      ? $path
+      : $c->dispatcher->get_action_by_path($path);
+    unless (defined $action) {
+      croak "Can't find action for path '$path'";
+    }
+    return $c->uri_for( $action, @args );
+}
+
 =head2 $c->welcome_message
 
 Returns the Catalyst welcome HTML page.
@@ -1295,7 +1463,7 @@ sub welcome_message {
                     they can save you a lot of work.</p>
                     <pre><code>script/${prefix}_create.pl -help</code></pre>
                     <p>Also, be sure to check out the vast and growing
-                    collection of <a href="http://cpansearch.perl.org/search?query=Catalyst%3A%3APlugin%3A%3A&amp;mode=all">plugins for Catalyst on CPAN</a>;
+                    collection of <a href="http://search.cpan.org/search?query=Catalyst">plugins for Catalyst on CPAN</a>;
                     you are likely to find what you need there.
                     </p>
 
@@ -1314,7 +1482,7 @@ sub welcome_message {
                      </li>
                  </ul>
                  <h2>In conclusion</h2>
-                 <p>The Catalyst team hopes you will enjoy using Catalyst as much 
+                 <p>The Catalyst team hopes you will enjoy using Catalyst as much
                     as we enjoyed making it. Please contact us if you have ideas
                     for improvement or other feedback.</p>
              </div>
@@ -1366,8 +1534,8 @@ that will be dumped on the error page in debug mode.
 
 sub dump_these {
     my $c = shift;
-    [ Request => $c->req ], 
-    [ Response => $c->res ], 
+    [ Request => $c->req ],
+    [ Response => $c->res ],
     [ Stash => $c->stash ],
     [ Config => $c->config ];
 }
@@ -1401,19 +1569,20 @@ sub execute {
     my $stats_info = $c->_stats_start_execute( $code ) if $c->use_stats;
 
     push( @{ $c->stack }, $code );
-    
+
+    no warnings 'recursion';
     eval { $c->state( $code->execute( $class, $c, @{ $c->req->args } ) || 0 ) };
 
     $c->_stats_finish_execute( $stats_info ) if $c->use_stats and $stats_info;
-    
+
     my $last = pop( @{ $c->stack } );
 
     if ( my $error = $@ ) {
-        if ( !ref($error) and $error eq $DETACH ) {
-            die $DETACH if($c->depth > 1);
+        if ( blessed($error) and $error->isa('Catalyst::Exception::Detach') ) {
+            $error->rethrow if $c->depth > 1;
         }
-        elsif ( !ref($error) and $error eq $GO ) {
-            die $GO if($c->depth > 0);
+        elsif ( blessed($error) and $error->isa('Catalyst::Exception::Go') ) {
+            $error->rethrow if $c->depth > 0;
         }
         else {
             unless ( ref $error ) {
@@ -1432,9 +1601,9 @@ sub execute {
 
 sub _stats_start_execute {
     my ( $c, $code ) = @_;
-
+    my $appclass = ref($c) || $c;
     return if ( ( $code->name =~ /^_.*/ )
-        && ( !$c->config->{show_internal_actions} ) );
+        && ( !$appclass->config->{show_internal_actions} ) );
 
     my $action_name = $code->reverse();
     $c->counter->{$action_name}++;
@@ -1462,11 +1631,12 @@ sub _stats_start_execute {
 
     # is this a root-level call or a forwarded call?
     if ( $callsub =~ /forward$/ ) {
+        my $parent = $c->stack->[-1];
 
         # forward, locate the caller
-        if ( my $parent = $c->stack->[-1] ) {
+        if ( exists $c->counter->{"$parent"} ) {
             $c->stats->profile(
-                begin  => $action, 
+                begin  => $action,
                 parent => "$parent" . $c->counter->{"$parent"},
                 uid    => $uid,
             );
@@ -1481,7 +1651,7 @@ sub _stats_start_execute {
         }
     }
     else {
-        
+
         # root-level call
         $c->stats->profile(
             begin => $action,
@@ -1497,25 +1667,6 @@ sub _stats_finish_execute {
     $c->stats->profile( end => $info );
 }
 
-=head2 $c->_localize_fields( sub { }, \%keys );
-
-=cut
-
-#Why does this exist? This is no longer safe and WILL NOT WORK.
-# it doesnt seem to be used anywhere. can we remove it?
-sub _localize_fields {
-    my ( $c, $localized, $code ) = ( @_ );
-
-    my $request = delete $localized->{request} || {};
-    my $response = delete $localized->{response} || {};
-    
-    local @{ $c }{ keys %$localized } = values %$localized;
-    local @{ $c->request }{ keys %$request } = values %$request;
-    local @{ $c->response }{ keys %$response } = values %$response;
-
-    $code->();
-}
-
 =head2 $c->finalize
 
 Finalizes the request.
@@ -1552,12 +1703,12 @@ sub finalize {
 
         $c->finalize_body;
     }
-    
-    if ($c->use_stats) {        
+
+    if ($c->use_stats) {
         my $elapsed = sprintf '%f', $c->stats->elapsed;
         my $av = $elapsed == 0 ? '??' : sprintf '%.3f', 1 / $elapsed;
         $c->log->info(
-            "Request took ${elapsed}s ($av/s)\n" . $c->stats->report . "\n" );        
+            "Request took ${elapsed}s ($av/s)\n" . $c->stats->report . "\n" );
     }
 
     return $c->response->status;
@@ -1706,7 +1857,7 @@ sub handle_request {
 
         my $c = $class->prepare(@arguments);
         $c->dispatch;
-        $status = $c->finalize;   
+        $status = $c->finalize;
     };
 
     if ( my $error = $@ ) {
@@ -1715,7 +1866,7 @@ sub handle_request {
     }
 
     $COUNT++;
-    
+
     if(my $coderef = $class->log->can('_flush')){
         $class->log->$coderef();
     }
@@ -1736,7 +1887,7 @@ sub prepare {
     # After the app/ctxt split, this should become an attribute based on something passed
     # into the application.
     $class->context_class( ref $class || $class ) unless $class->context_class;
-   
+
     my $c = $class->context_class->new({});
 
     # For on-demand data
@@ -1746,7 +1897,7 @@ sub prepare {
     #surely this is not the most efficient way to do things...
     $c->stats($class->stats_class->new)->enable($c->use_stats);
     if ( $c->debug ) {
-        $c->res->headers->header( 'X-Catalyst' => $Catalyst::VERSION );            
+        $c->res->headers->header( 'X-Catalyst' => $Catalyst::VERSION );
     }
 
     #XXX reuse coderef from can
@@ -1765,9 +1916,9 @@ sub prepare {
         # Prepare the body for reading, either by prepare_body
         # or the user, if they are using $c->read
         $c->prepare_read;
-        
+
         # Parse the body unless the user wants it on-demand
-        unless ( $c->config->{parse_on_demand} ) {
+        unless ( ref($c)->config->{parse_on_demand} ) {
             $c->prepare_body;
         }
     }
@@ -1981,7 +2132,7 @@ Reads a chunk of data from the request body. This method is designed to
 be used in a while loop, reading C<$maxlength> bytes on every call.
 C<$maxlength> defaults to the size of the request if not specified.
 
-You have to set C<< MyApp->config->{parse_on_demand} >> to use this
+You have to set C<< MyApp->config(parse_on_demand => 1) >> to use this
 directly.
 
 Warning: If you use read(), Catalyst will not process the body,
@@ -2018,40 +2169,32 @@ sub setup_actions { my $c = shift; $c->dispatcher->setup_actions( $c, @_ ) }
 
 =head2 $c->setup_components
 
-Sets up components. Specify a C<setup_components> config option to pass
-additional options directly to L<Module::Pluggable>. To add additional
-search paths, specify a key named C<search_extra> as an array
-reference. Items in the array beginning with C<::> will have the
-application class name prepended to them.
+This method is called internally to set up the application's components.
+
+It finds modules by calling the L<locate_components> method, expands them to
+package names with the L<expand_component_module> method, and then installs
+each component into the application.
+
+The C<setup_components> config option is passed to both of the above methods.
 
-All components found will also have any 
-L<Devel::InnerPackage|inner packages> loaded and set up as components.
-Note, that modules which are B<not> an I<inner package> of the main
-file namespace loaded will not be instantiated as components.
+Installation of each component is performed by the L<setup_component> method,
+below.
 
 =cut
 
 sub setup_components {
     my $class = shift;
 
-    my @paths   = qw( ::Controller ::C ::Model ::M ::View ::V );
     my $config  = $class->config->{ setup_components };
-    my $extra   = delete $config->{ search_extra } || [];
-    
-    push @paths, @$extra;
-        
-    my $locator = Module::Pluggable::Object->new(
-        search_path => [ map { s/^(?=::)/$class/; $_; } @paths ],
-        %$config
-    );
 
-    my @comps = sort { length $a <=> length $b } $locator->plugins;
+    my @comps = sort { length $a <=> length $b }
+                $class->locate_components($config);
     my %comps = map { $_ => 1 } @comps;
 
-    my $deprecated_component_names = grep { /::[CMV]::/ } @comps;
+    my $deprecatedcatalyst_component_names = grep { /::[CMV]::/ } @comps;
     $class->log->warn(qq{Your application is using the deprecated ::[MVC]:: type naming scheme.\n}.
         qq{Please switch your class names to ::Model::, ::View:: and ::Controller: as appropriate.\n}
-    );
+    ) if $deprecatedcatalyst_component_names;
 
     for my $component ( @comps ) {
 
@@ -2060,28 +2203,82 @@ sub setup_components {
         # we know M::P::O found a file on disk so this is safe
 
         Catalyst::Utils::ensure_class_loaded( $component, { ignore_loaded => 1 } );
-        #Class::MOP::load_class($component);
-
-        my $module  = $class->setup_component( $component );
-        my %modules = (
-            $component => $module,
-            map {
-                $_ => $class->setup_component( $_ )
-            } grep { 
-              not exists $comps{$_}
-            } Devel::InnerPackage::list_packages( $component )
-        );
-        
-        for my $key ( keys %modules ) {
-            $class->components->{ $key } = $modules{ $key };
+
+        # Needs to be done as soon as the component is loaded, as loading a sub-component
+        # (next time round the loop) can cause us to get the wrong metaclass..
+        $class->_controller_init_base_classes($component);
+    }
+
+    for my $component (@comps) {
+        $class->components->{ $component } = $class->setup_component($component);
+        for my $component ($class->expand_component_module( $component, $config )) {
+            next if $comps{$component};
+            $class->_controller_init_base_classes($component); # Also cover inner packages
+            $class->components->{ $component } = $class->setup_component($component);
         }
     }
 }
 
+=head2 $c->locate_components( $setup_component_config )
+
+This method is meant to provide a list of component modules that should be
+setup for the application.  By default, it will use L<Module::Pluggable>.
+
+Specify a C<setup_components> config option to pass additional options directly
+to L<Module::Pluggable>. To add additional search paths, specify a key named
+C<search_extra> as an array reference. Items in the array beginning with C<::>
+will have the application class name prepended to them.
+
+=cut
+
+sub locate_components {
+    my $class  = shift;
+    my $config = shift;
+
+    my @paths   = qw( ::Controller ::C ::Model ::M ::View ::V );
+    my $extra   = delete $config->{ search_extra } || [];
+
+    push @paths, @$extra;
+
+    my $locator = Module::Pluggable::Object->new(
+        search_path => [ map { s/^(?=::)/$class/; $_; } @paths ],
+        %$config
+    );
+
+    my @comps = $locator->plugins;
+
+    return @comps;
+}
+
+=head2 $c->expand_component_module( $component, $setup_component_config )
+
+Components found by C<locate_components> will be passed to this method, which
+is expected to return a list of component (package) names to be set up.
+
+=cut
+
+sub expand_component_module {
+    my ($class, $module) = @_;
+    return Devel::InnerPackage::list_packages( $module );
+}
+
 =head2 $c->setup_component
 
 =cut
 
+# FIXME - Ugly, ugly hack to ensure the we force initialize non-moose base classes
+#         nearest to Catalyst::Controller first, no matter what order stuff happens
+#         to be loaded. There are TODO tests in Moose for this, see
+#         f2391d17574eff81d911b97be15ea51080500003
+sub _controller_init_base_classes {
+    my ($app_class, $component) = @_;
+    return unless $component->isa('Catalyst::Controller');
+    foreach my $class ( reverse @{ mro::get_linear_isa($component) } ) {
+        Moose::Meta::Class->initialize( $class )
+            unless find_meta($class);
+    }
+}
+
 sub setup_component {
     my( $class, $component ) = @_;
 
@@ -2091,6 +2288,10 @@ sub setup_component {
 
     my $suffix = Catalyst::Utils::class2classsuffix( $component );
     my $config = $class->config->{ $suffix } || {};
+    # Stash catalyst_component_name in the config here, so that custom COMPONENT
+    # methods also pass it. local to avoid pointlessly shitting in config
+    # for the debug screen, as $component is already the key name.
+    local $config->{catalyst_component_name} = $component;
 
     my $instance = eval { $component->COMPONENT( $class, $config ); };
 
@@ -2101,11 +2302,16 @@ sub setup_component {
         );
     }
 
-    Catalyst::Exception->throw(
-        message =>
-        qq/Couldn't instantiate component "$component", "COMPONENT() didn't return an object-like value"/
-    ) unless blessed($instance);
-
+    unless (blessed $instance) {
+        my $metaclass = Moose::Util::find_meta($component);
+        my $method_meta = $metaclass->find_method_by_name('COMPONENT');
+        my $component_method_from = $method_meta->associated_metaclass->name;
+        my $value = defined($instance) ? $instance : 'undef';
+        Catalyst::Exception->throw(
+            message =>
+            qq/Couldn't instantiate component "$component", COMPONENT() method (from $component_method_from) didn't return an object-like value (value was $value)./
+        );
+    }
     return $instance;
 }
 
@@ -2155,7 +2361,7 @@ sub setup_engine {
 
     if ( $ENV{MOD_PERL} ) {
         my $meta = Class::MOP::get_metaclass_by_name($class);
-        
+
         # create the apache method
         $meta->add_method('apache' => sub { shift->engine->apache });
 
@@ -2279,7 +2485,7 @@ sub setup_home {
 Sets up log by instantiating a L<Catalyst::Log|Catalyst::Log> object and
 passing it to C<log()>. Pass in a comma-delimited list of levels to set the
 log to.
+
 This method also installs a C<debug> method that returns a true value into the
 catalyst subclass if the "debug" level is passed in the comma-delimited list,
 or if the C<$CATALYST_DEBUG> environment variable is set to a true value.
@@ -2296,14 +2502,19 @@ sub setup_log {
     $levels ||= '';
     $levels =~ s/^\s+//;
     $levels =~ s/\s+$//;
-    my %levels = map { $_ => 1 } split /\s*,\s*/, $levels || '';
-    
+    my %levels = map { $_ => 1 } split /\s*,\s*/, $levels;
+
+    my $env_debug = Catalyst::Utils::env_value( $class, 'DEBUG' );
+    if ( defined $env_debug ) {
+        $levels{debug} = 1 if $env_debug; # Ugly!
+        delete($levels{debug}) unless $env_debug;
+    }
+
     unless ( $class->log ) {
         $class->log( Catalyst::Log->new(keys %levels) );
     }
 
-    my $env_debug = Catalyst::Utils::env_value( $class, 'DEBUG' );
-    if ( defined($env_debug) or $levels{debug} ) {
+    if ( $levels{debug} ) {
         Class::MOP::get_metaclass_by_name($class)->add_method('debug' => sub { 1 });
         $class->log->debug('Debug messages enabled');
     }
@@ -2334,7 +2545,7 @@ sub setup_stats {
 }
 
 
-=head2 $c->registered_plugins 
+=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); >>.
@@ -2363,9 +2574,6 @@ the plugin name does not begin with C<Catalyst::Plugin::>.
         my ( $proto, $plugin, $instant ) = @_;
         my $class = ref $proto || $proto;
 
-        # no ignore_loaded here, the plugin may already have been
-        # defined in memory and we don't want to error on "no file" if so
-
         Class::MOP::load_class( $plugin );
 
         $proto->_plugins->{$plugin} = 1;
@@ -2386,14 +2594,26 @@ the plugin name does not begin with C<Catalyst::Plugin::>.
 
         $class->_plugins( {} ) unless $class->_plugins;
         $plugins ||= [];
-        for my $plugin ( reverse @$plugins ) {
 
-            unless ( $plugin =~ s/\A\+// ) {
-                $plugin = "Catalyst::Plugin::$plugin";
-            }
+        my @plugins = Catalyst::Utils::resolve_namespace($class . '::Plugin', 'Catalyst::Plugin', @$plugins);
+
+        for my $plugin ( reverse @plugins ) {
+            Class::MOP::load_class($plugin);
+            my $meta = find_meta($plugin);
+            next if $meta && $meta->isa('Moose::Meta::Role');
 
             $class->_register_plugin($plugin);
         }
+
+        my @roles =
+            map { $_->name }
+            grep { $_ && blessed($_) && $_->isa('Moose::Meta::Role') }
+            map { find_meta($_) }
+            @plugins;
+
+        Moose::Util::apply_all_roles(
+            $class => @roles
+        ) if @roles;
     }
 }
 
@@ -2412,8 +2632,8 @@ 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.
 
-Note that this is a static method, not an accessor and should be overloaded
-by declaring "sub use_stats { 1 }" in your MyApp.pm, not by calling $c->use_stats(1).
+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) >>.
 
 =cut
 
@@ -2446,13 +2666,84 @@ messages in template systems.
 
 sub version { return $Catalyst::VERSION }
 
+=head1 CONFIGURATION
+
+There are a number of 'base' config variables which can be set:
+
+=over
+
+=item *
+
+C<case_sensitive> - Makes private paths case sensitive. See L</CASE SENSITIVITY>.
+
+=item *
+
+C<default_model> - The default model picked if you say C<< $c->model >>. See L</$c->model($name)>.
+
+=item *
+
+C<default_view> - The default view to be rendered or returned when C<< $c->view >>. See L</$c->view($name)>.
+is called.
+
+=item *
+
+C<disable_component_resolution_regex_fallback> - Turns
+off the deprecated component resolution functionality so
+that if any of the component methods (e.g. C<< $c->controller('Foo') >>)
+are called then regex search will not be attempted on string values and
+instead C<undef> will be returned.
+
+=item *
+
+C<home> - The application home directory. In an uninstalled application,
+this is the top level application directory. In an installed application,
+this will be the directory containing C<< MyApp.pm >>.
+
+=item *
+
+C<ignore_frontend_proxy> - See L</PROXY SUPPORT>
+
+=item *
+
+C<name> - The name of the application in debug messages and the debug and
+welcome screens
+
+=item *
+
+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>
+
+=item *
+
+C<root> - The root directory for templates. Usually this is just a
+subdirectory of the home directory, but you can set it to change the
+templates to a different directory.
+
+=item *
+
+C<search_extra> - Array reference passed to Module::Pluggable to for additional
+namespaces from which components will be loaded (and constructed and stored in
+C<< $c->components >>).
+
+=item *
+
+C<show_internal_actions> - If true, causes internal actions such as C<< _DISPATCH >>
+to be shown in hit debug tables in the test server.
+
+=item *
+
+C<using_frontend_proxy> - See L</PROXY SUPPORT>.
+
+=back
+
 =head1 INTERNAL ACTIONS
 
 Catalyst uses internal actions like C<_DISPATCH>, C<_BEGIN>, C<_AUTO>,
 C<_ACTION>, and C<_END>. These are by default not shown in the private
 action table, but you can make them visible with a config parameter.
 
-    MyApp->config->{show_internal_actions} = 1;
+    MyApp->config(show_internal_actions => 1);
 
 =head1 CASE SENSITIVITY
 
@@ -2460,7 +2751,7 @@ By default Catalyst is not case sensitive, so C<MyApp::C::FOO::Bar> is
 mapped to C</foo/bar>. You can activate case sensitivity with a config
 parameter.
 
-    MyApp->config->{case_sensitive} = 1;
+    MyApp->config(case_sensitive => 1);
 
 This causes C<MyApp::C::Foo::Bar> to map to C</Foo/Bar>.
 
@@ -2470,8 +2761,8 @@ The request body is usually parsed at the beginning of a request,
 but if you want to handle input yourself, you can enable on-demand
 parsing with a config parameter.
 
-    MyApp->config->{parse_on_demand} = 1;
-    
+    MyApp->config(parse_on_demand => 1);
+
 =head1 PROXY SUPPORT
 
 Many production servers operate using the common double-server approach,
@@ -2485,12 +2776,24 @@ Catalyst will automatically detect this situation when you are running
 the frontend and backend servers on the same machine. The following
 changes are made to the request.
 
-    $c->req->address is set to the user's real IP address, as read from 
+    $c->req->address is set to the user's real IP address, as read from
     the HTTP X-Forwarded-For header.
-    
+
     The host value for $c->req->base and $c->req->uri is set to the real
     host, as read from the HTTP X-Forwarded-Host header.
 
+Additionally, you may be running your backend application on an insecure
+connection (port 80) while your frontend proxy is running under SSL.  If there
+is a discrepancy in the ports, use the HTTP header C<X-Forwarded-Port> to
+tell Catalyst what port the frontend listens on.  This will allow all URIs to
+be created properly.
+
+In the case of passing in:
+
+    X-Forwarded-Port: 443
+
+All calls to C<uri_for> will result in an https link, as is expected.
+
 Obviously, your web server must support these headers for this to work.
 
 In a more complex server farm environment where you may have your
@@ -2498,11 +2801,11 @@ frontend proxy server(s) on different machines, you will need to set a
 configuration option to tell Catalyst to read the proxied data from the
 headers.
 
-    MyApp->config->{using_frontend_proxy} = 1;
-    
+    MyApp->config(using_frontend_proxy => 1);
+
 If you do not wish to use the proxy support at all, you may set:
 
-    MyApp->config->{ignore_frontend_proxy} = 1;
+    MyApp->config(ignore_frontend_proxy => 1);
 
 =head1 THREAD SAFETY
 
@@ -2539,7 +2842,7 @@ Wiki:
 
 =head2 L<Catalyst::Manual> - The Catalyst Manual
 
-=head2 L<Catalyst::Component>, L<Catalyst::Base> - Base classes for components
+=head2 L<Catalyst::Component>, L<Catalyst::Controller> - Base classes for components
 
 =head2 L<Catalyst::Engine> - Core engine
 
@@ -2561,9 +2864,11 @@ abw: Andy Wardley
 
 acme: Leon Brocard <leon@astray.com>
 
+abraxxa: Alexander Hartmaier <abraxxa@cpan.org>
+
 Andrew Bramble
 
-Andrew Ford
+Andrew Ford E<lt>A.Ford@ford-mason.co.ukE<gt>
 
 Andrew Ruthven
 
@@ -2579,26 +2884,46 @@ chansen: Christian Hansen
 
 chicks: Christopher Hicks
 
+Chisel Wright C<pause@herlpacker.co.uk>
+
+Danijel Milicevic C<me@danijel.de>
+
+David Kamholz E<lt>dkamholz@cpan.orgE<gt>
+
+David Naughton, C<naughton@umn.edu>
+
 David E. Wheeler
 
 dkubb: Dan Kubb <dan.kubb-cpan@onautopilot.com>
 
 Drew Taylor
 
+dwc: Daniel Westermann-Clark <danieltwc@cpan.org>
+
 esskar: Sascha Kiefer
 
 fireartist: Carl Franks <cfranks@cpan.org>
 
+frew: Arthur Axel "fREW" Schmidt <frioux@gmail.com>
+
 gabb: Danijel Milicevic
 
 Gary Ashton Jones
 
+Gavin Henry C<ghenry@perl.me.uk>
+
 Geoff Richards
 
+groditi: Guillermo Roditi <groditi@gmail.com>
+
+hobbs: Andrew Rodland <andrew@cleverdomain.org>
+
 ilmari: Dagfinn Ilmari MannsÃ¥ker <ilmari@ilmari.org>
 
 jcamacho: Juan Camacho
 
+jester: Jesse Sheidlower C<jester@panix.com>
+
 jhannah: Jay Hannah <jay@jays.net>
 
 Jody Belka
@@ -2607,6 +2932,12 @@ Johan Lindstrom
 
 jon: Jon Schutz <jjschutz@cpan.org>
 
+Jonathan Rockway C<< <jrockway@cpan.org> >>
+
+Kieren Diment C<kd@totaldatasolution.com>
+
+konobi: Scott McWhirter <konobi@cpan.org>
+
 marcus: Marcus Ramberg <mramberg@cpan.org>
 
 miyagawa: Tatsuhiko Miyagawa <miyagawa@bulknews.net>
@@ -2633,19 +2964,27 @@ phaylon: Robert Sedlacek <phaylon@dunkelheit.at>
 
 rafl: Florian Ragwitz <rafl@debian.org>
 
-sky: Arthur Bergman
+random: Roland Lammel <lammel@cpan.org>
 
-the_jester: Jesse Sheidlower
+Robert Sedlacek C<< <rs@474.at> >>
+
+sky: Arthur Bergman
 
 t0m: Tomas Doran <bobtfish@bobtfish.net>
 
 Ulf Edvinsson
 
+Viljo Marrandi C<vilts@yahoo.com>
+
+Will Hawes C<info@whawes.co.uk>
+
 willert: Sebastian Willert <willert@cpan.org>
 
+Yuval Kogman, C<nothingmuch@woobling.org>
+
 =head1 LICENSE
 
-This library is free software, you can redistribute it and/or modify it under
+This library is free software. You can redistribute it and/or modify it under
 the same terms as Perl itself.
 
 =cut