Merge master into gsoc_breadboard
Tomas Doran [Mon, 24 Oct 2011 17:33:28 +0000 (10:33 -0700)]
1  2 
Makefile.PL
TODO
lib/Catalyst.pm
lib/Catalyst/Component.pm
lib/Catalyst/Controller.pm
lib/Catalyst/Dispatcher.pm

diff --combined Makefile.PL
@@@ -13,12 -13,12 +13,13 @@@ perl_version '5.008004'
  name 'Catalyst-Runtime';
  all_from 'lib/Catalyst/Runtime.pm';
  
 +requires 'Bread::Board';
  requires 'List::MoreUtils';
  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';
@@@ -54,7 -54,6 +55,7 @@@ requires 'MRO::Compat'
  requires 'MooseX::Getopt' => '0.30';
  requires 'MooseX::Types';
  requires 'MooseX::Types::Common::Numeric';
 +requires 'MooseX::Types::LoadableClass';
  requires 'String::RewritePrefix' => '0.004'; # Catalyst::Utils::resolve_namespace
  requires 'Plack' => '0.9974'; # IIS6 fix middleware
  requires 'Plack::Middleware::ReverseProxy' => '0.04';
@@@ -90,6 -89,7 +91,7 @@@ author_requires(map {; $_ => 0 } qw
    Test::NoTabs
    Test::Pod
    Test::Pod::Coverage
+   Test::Spelling
    Pod::Coverage::TrustPod
  ));
  
diff --combined TODO
--- 1/TODO
--- 2/TODO
+++ b/TODO
@@@ -24,14 -24,8 +24,8 @@@ subclass of Catalyst::Log, no ::Plugin:
  See also: Catalyst::Plugin::Log::Dispatch and
  http://github.com/willert/catalyst-plugin-log4perl-simple/tree
  
- ## Capture arguments that the plack engine component was run with somewhere,
-     to more easily support custom args from scripts (e.g. Gitalist's
-     --git_dir)
  ## throw away the restarter and allow using the restarters Plack provides
  
- ## remove per-request state from the engine instance
  ## be smarter about how we use PSGI - not every response needs to be delayed
      and streaming
  
   * Have a look at the Devel::REPL BEFORE_PLUGIN stuff
     I wonder if what we need is that combined with plugins-as-roles
  
- #  PSGI
- ##  To do at release time
-   - Release psgi branch of Catalyst-Devel
-   - Release new Task::Catalyst
-   - Release 5.9 branch of Catalyst-Manual
-   - Release Catalyst::Engine::HTTP::Prefork with deprecation notice
-     + exit in Makefile.PL if Catalyst > 5.89 is installed.
- ##  Blockers
-   * I've noticed a small difference with Catalyst::Test. The latest stable
-     version include two headers, 'host' and 'https'. They are missing from
-     this version - Pedro Melo on list
-     ^^ Cannot replicate this? Mailed back to ask for tests..
  # App / ctx split:
  
    NOTE - these are notes that t0m thought up after doing back compat for
    - Profit! (Things like changing the complete app config per vhost, i.e.
      writing a config loader / app class role which dispatches per vhost to
      differently configured apps is piss easy)
 +
 +## GSOC
 +
 +### Final steps for GSOC
 +
 +##### Things that work:
 +
 +    - the default container loads all components, calls ACCEPT_CONTEXT() when appropriate, and COMPONENT() when appropriate, behaving like current Catalyst does
 +
 +    - its possible to create a custom container, and override the components you want. Lifecycle, class, dependencies, all overridable.
 +
 +    - config files are loaded without Catalyst::Plugin::ConfigLoader
 +
 +    - per request life cycle somewhat works
 +
 +    - external modules are loaded just using a custom container, much like Catalyst::Model::Adaptor
 +
 +##### Things that don't work:
 +
 +    - expand_component_module
 +
 +    - Some back compat
 +        - wrappers around setup_component, setup_components in Catalyst.pm
 +        - $instance->expand_modules
 +        - search_extra
 +        - Crazy tests for things such as:
 +           sub COMPONENT {
 +             ...
 +             *${appclass}::Model::TopLevel::GENERATED::ACCEPT_CONTEXT = sub { ... };
 +             ...
 +           }
 +
 +##### Need planning, have questions:
 +
 +    - per request life cycle
 +
 +    - sugar - we should discuss the syntax with rafl and edenc
 +        - what's missing?
 +
 +    - when / when not COMPONENT should be called
 +
 +    - locate_components service vs setup_components method
 +      - can we be more lazy?
 +      - should setup_components be a service that things like the ->component lookup
 +        can depend on?
 +
 +    - There are a few more FIXMEs, idk if any relevant here
 +
 +### Next steps - planned:
 +
 +  - some imports need to get the importing package in Catalyst::IOC
 +    - done - needs testing
 +
 +  - Back compat for Catalyst.pm moved methods (locate_components)
 +    - done - needs testing
 +
 +  - Test custom container
 +    - writing some tests which verify that the models you think should be
 +      there are there, and that they received their dependencies as arguments
 +    - i.e. Model::Bar should get params { foo => $model_foo } when being
 +      constructed, etc
 +    - Need to test that if you have a standard component Frotz
 +      and a customized component Fnar, and Fnar depends on Frotz
 +    - And yeah, some tests that the customised components actually work via
 +      $c->model('Foo'), and that COMPONENT is called (or not called)
 +      as appropiate and that ACCEPT_CONTEXT is called (or not) as appropriate
 +
 +### Next steps - less planned:
 +
 +  - make ACCEPT_CONTEXT and COMPONENT optional in Catalyst::IOC::BlockInjection and Catalyst::IOC::ConstructorInjection
 +    - Create COMPONENTSingleton life cycle
 +
 +  - Test cases for extending the container in an application.
 +    - Using the sugar added in the previous item
 +    - Test when Model::Foo depends_on Model::Bar
 +    - Test for component Foo => ( lifecycle => 'Singleton', class => 'My::External::Class', dependencies => { config => depends_on("config") } )
 +    - Fix ^^ so that you can get your component's namespaced config nicely.
 +
 +  - Tests for using the container outside of Catalyst
 +    - Custom container which adds some (very simple) services which are initialized from
 +      the application config file (note plain services, not components)
 +    - Depend on (and test) these inside Catalyst
 +    - Test loading container outside Catalyst, and these services working
 +    - Test Catalyst / MyApp is not loaded
 +
 +#### Extending my app, notes
 +
 +Basically try to implement something like this (starting out without the sugar!), and see how it breaks
 +and what needs to be done to fix it!
 +
 +##### Eventual syntax
 +
 +package MyApp::Container;
 +use Catalyst::IOC;
 +    
 +    container $self, as {
 +            container model => as {
 +                component Foo => (); # As per default!
 +                component Bar => (dependencies => ['/model/Foo']); # Magic!
 +                component Baz => ( lifecycle => 'InstancePerContext );
 +                component Quux => ( lifecycle => 'Singleton' ); # ACCEPT_CONTEXT not called
 +                # Catalyst::Model::Adaptor example
 +                conponent Fnar => ( lifecycle => 'Singleton', class => 'My::External::Class', dependencies => { config => depends_on('config')} );
 +                #                                                                                               ^^ FIXME - gets whole config, not Model::Foo
 +                #                                                                                                  There should be a 'nice' way to get the 'standard' config
 +            };                    
 +            # Note - implementation of BB may need to be changed to support making sure existing 
 +            # services actually get overridden. not sure how the default container behaves when doing that
 +            # above code would build the constructor injection as it currently does,
 +            # defaulting to the class name in the right namespace as declared by the surrounding container
 +            # as well as adding using the catalyst-specific service class
 +    };
 +
 +1;
diff --combined lib/Catalyst.pm
@@@ -15,7 -15,8 +15,7 @@@ use Catalyst::Response
  use Catalyst::Utils;
  use Catalyst::Controller;
  use Data::OptList;
 -use Devel::InnerPackage ();
 -use Module::Pluggable::Object ();
 +use File::stat;
  use Text::SimpleTable ();
  use Path::Class::Dir ();
  use Path::Class::File ();
@@@ -72,7 -73,7 +72,7 @@@ our $GO        = Catalyst::Exception::G
  #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
 +  for qw/container arguments dispatcher engine log dispatcher_class
    engine_loader context_class request_class response_class stats_class
    setup_finished _psgi_app loading_psgi_file/;
  
@@@ -83,7 -84,7 +83,7 @@@ __PACKAGE__->stats_class('Catalyst::Sta
  
  # Remember to update this in Catalyst::Runtime as well!
  
- our $VERSION = '5.90004';
+ our $VERSION = '5.90005';
  
  sub import {
      my ( $class, @arguments ) = @_;
      return if $caller eq 'main';
  
      my $meta = Moose::Meta::Class->initialize($caller);
 -    unless ( $caller->isa('Catalyst') ) {
 -        my @superclasses = ($meta->superclasses, $class, 'Catalyst::Controller');
 -        $meta->superclasses(@superclasses);
 -    }
 +
 +    unless ( $caller->isa('Catalyst') ) { # XXX - Remove!
 +        my @superclasses = ($meta->superclasses, $class, 'Catalyst::Component'); # XXX - Remove!
 +        $meta->superclasses(@superclasses); # XXX - Remove!
 +    } # XXX - Remove!
 +
      # Avoid possible C3 issues if 'Moose::Object' is already on RHS of MyApp
      $meta->superclasses(grep { $_ ne 'Moose::Object' } $meta->superclasses);
  
      $caller->setup_home;
  }
  
 +sub MODIFY_CODE_ATTRIBUTES {
 +    Catalyst::Exception->throw(
 +        "Catalyst applications (aka MyApp) cannot be controllers anymore. " .
 +        "That has been deprecated and removed. You should create a " .
 +        "controller class called Root.pm, and move relevant code to that class."
 +    );
 +}
 +
 +
  sub _application { $_[0] }
  
  =head1 NAME
@@@ -158,7 -148,7 +158,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
      [% 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
@@@ -246,7 -202,7 +212,7 @@@ fully qualify the name by using a unar
          +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/;
@@@ -266,13 -222,6 +232,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.:
@@@ -286,11 -235,11 +245,11 @@@ the name will be replaced with undersco
  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
@@@ -358,9 -307,10 +317,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 };
@@@ -422,7 -372,7 +382,7 @@@ L<reverse|Catalyst::Action/reverse> ret
  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.
  
@@@ -551,6 -501,98 +511,6 @@@ sub clear_errors 
      $c->error(0);
  }
  
 -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; }
 -        grep { /$filter/ } keys %{ $c->components };
 -
 -    # undef for a name will return all
 -    return keys %eligible if !defined $name;
 -
 -    my $query  = ref $name ? $name : qr/^$name$/i;
 -    my @result = grep { $eligible{$_} =~ m{$query} } keys %eligible;
 -
 -    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 = grep { $eligible{ $_ } =~ m{$query} } keys %eligible;
 -
 -    # no results? try against full names
 -    if( !@result ) {
 -        @result = grep { m{$query} } keys %eligible;
 -    }
 -
 -    # don't warn if we didn't find any results, it just might not exist
 -    if( @result ) {
 -        # 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];
 -        # 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 " .
 -              "plugin's config";
 -        } elsif ($shortmess =~ m#Catalyst/lib/(View|Controller)#) {
 -           $msg .= " You probably need to set '$short' instead of '${name}' in this " .
 -              "component's config";
 -        } else {
 -           $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->${warn_for}(qr/${name}/)";
 -        }
 -        $c->log->warn( "${msg}$shortmess" );
 -    }
 -
 -    return @result;
 -}
 -
 -# 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_names_search_prefixes( undef, @prefixes );
 -
 -    return @names;
 -}
 -
 -# Filter a component before returning by calling ACCEPT_CONTEXT if available
 -sub _filter_component {
 -    my ( $c, $comp, @args ) = @_;
 -
 -    if ( eval { $comp->can('ACCEPT_CONTEXT'); } ) {
 -        return $comp->ACCEPT_CONTEXT( $c, @args );
 -    }
 -
 -    return $comp;
 -}
 -
  =head2 COMPONENT ACCESSORS
  
  =head2 $c->controller($name)
@@@ -570,7 -612,23 +530,7 @@@ If you want to search for controllers, 
  
  =cut
  
 -sub controller {
 -    my ( $c, $name, @args ) = @_;
 -
 -    my $appclass = ref($c) || $c;
 -    if( $name ) {
 -        unless ( ref($name) ) { # Direct component hash lookup to avoid costly regexps
 -            my $comps = $c->components;
 -            my $check = $appclass."::Controller::".$name;
 -            return $c->_filter_component( $comps->{$check}, @args ) if exists $comps->{$check};
 -        }
 -        my @result = $c->_comp_search_prefixes( $name, qw/Controller C/ );
 -        return map { $c->_filter_component( $_, @args ) } @result if ref $name;
 -        return $c->_filter_component( $result[ 0 ], @args );
 -    }
 -
 -    return $c->component( $c->action->class );
 -}
 +sub controller { shift->_lookup_mvc('controller', @_) }
  
  =head2 $c->model($name)
  
@@@ -593,7 -651,42 +553,7 @@@ If you want to search for models, pass 
  
  =cut
  
 -sub model {
 -    my ( $c, $name, @args ) = @_;
 -    my $appclass = ref($c) || $c;
 -    if( $name ) {
 -        unless ( ref($name) ) { # Direct component hash lookup to avoid costly regexps
 -            my $comps = $c->components;
 -            my $check = $appclass."::Model::".$name;
 -            return $c->_filter_component( $comps->{$check}, @args ) if exists $comps->{$check};
 -        }
 -        my @result = $c->_comp_search_prefixes( $name, qw/Model M/ );
 -        return map { $c->_filter_component( $_, @args ) } @result if ref $name;
 -        return $c->_filter_component( $result[ 0 ], @args );
 -    }
 -
 -    if (ref $c) {
 -        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( $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->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.' );
 -    }
 -
 -    return $c->_filter_component( $comp );
 -}
 -
 +sub model { shift->_lookup_mvc('model', @_) }
  
  =head2 $c->view($name)
  
@@@ -616,23 -709,46 +576,23 @@@ If you want to search for views, pass i
  
  =cut
  
 -sub view {
 -    my ( $c, $name, @args ) = @_;
 +sub view { shift->_lookup_mvc('view', @_) }
  
 -    my $appclass = ref($c) || $c;
 -    if( $name ) {
 -        unless ( ref($name) ) { # Direct component hash lookup to avoid costly regexps
 -            my $comps = $c->components;
 -            my $check = $appclass."::View::".$name;
 -            if( exists $comps->{$check} ) {
 -                return $c->_filter_component( $comps->{$check}, @args );
 -            }
 -            else {
 -                $c->log->warn( "Attempted to use view '$check', but does not exist" );
 -            }
 -        }
 -        my @result = $c->_comp_search_prefixes( $name, qw/View V/ );
 -        return map { $c->_filter_component( $_, @args ) } @result if ref $name;
 -        return $c->_filter_component( $result[ 0 ], @args );
 -    }
 -
 -    if (ref $c) {
 -        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( $appclass->config->{default_view} )
 -      if $appclass->config->{default_view};
 +sub _lookup_mvc {
 +    my ( $c, $type, $name, @args ) = @_;
  
 -    my( $comp, $rest ) = $c->_comp_search_prefixes( undef, qw/View V/);
 +    if (ref $c && !$name) {
 +        my $current_instance = $c->stash->{"current_${type}_instance"};
 +        return $current_instance
 +            if $current_instance && $type ne 'controller';
  
 -    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->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.' );
 +        $name = $type eq 'controller'
 +              ? Catalyst::Utils::class2classshortsuffix($c->action->class)
 +              : $c->stash->{"current_${type}"}
 +              ;
      }
  
 -    return $c->_filter_component( $comp );
 +    return $c->container->get_component_from_sub_container($type, $name, $c, @args);
  }
  
  =head2 $c->controllers
@@@ -643,7 -759,7 +603,7 @@@ Returns the available names which can b
  
  sub controllers {
      my ( $c ) = @_;
 -    return $c->_comp_names(qw/Controller C/);
 +    return $c->container->get_sub_container('controller')->get_service_list;
  }
  
  =head2 $c->models
@@@ -654,7 -770,7 +614,7 @@@ Returns the available names which can b
  
  sub models {
      my ( $c ) = @_;
 -    return $c->_comp_names(qw/Model M/);
 +    return $c->container->get_sub_container('model')->get_service_list;
  }
  
  
@@@ -666,7 -782,7 +626,7 @@@ Returns the available names which can b
  
  sub views {
      my ( $c ) = @_;
 -    return $c->_comp_names(qw/View V/);
 +    return $c->container->get_sub_container('view')->get_service_list;
  }
  
  =head2 $c->comp($name)
@@@ -681,50 -797,57 +641,50 @@@ should be used instead
  If C<$name> is a regexp, a list of components matched against the full
  component name will be returned.
  
  =cut
  
  sub component {
 -    my ( $c, $name, @args ) = @_;
 +    my ( $c, $component, @args ) = @_;
  
 -    if( $name ) {
 -        my $comps = $c->components;
 +    unless ($component) {
 +        $c->log->warn('Calling $c->component with no args is deprecated and ');
 +        $c->log->warn('will be removed in a future release.');
 +        $c->log->warn('Use $c->component_list instead.');
 +        return $c->component_list;
 +    }
  
 -        if( !ref $name ) {
 -            # is it the exact name?
 -            return $c->_filter_component( $comps->{ $name }, @args )
 -                       if exists $comps->{ $name };
 +    my @result = $c->container->find_component( $component, $c, @args );
  
 -            # perhaps we just omitted "MyApp"?
 -            my $composed = ( ref $c || $c ) . "::${name}";
 -            return $c->_filter_component( $comps->{ $composed }, @args )
 -                       if exists $comps->{ $composed };
 +    # list context for regexp searches
 +    return @result if ref $component;
  
 -            # search all of the models, views and controllers
 -            my( $comp ) = $c->_comp_search_prefixes( $name, qw/Model M Controller C View V/ );
 -            return $c->_filter_component( $comp, @args ) if $comp;
 -        }
 +    # only one component (if it's found) for string searches
 +    return shift @result if @result;
  
 -        return
 -            if $c->config->{disable_component_resolution_regex_fallback};
 +    if (ref $c eq $component) {
 +        $c->log->warn('You are calling $c->comp("MyApp"). This behaviour is');
 +        $c->log->warn('deprecated, and will be removed in a future release.');
 +        return $c;
 +    }
  
 -        # This is here so $c->comp( '::M::' ) works
 -        my $query = ref $name ? $name : qr{$name}i;
 +    $c->log->warn("Looking for '$component', but nothing was found.");
  
 -        my @result = grep { m{$query} } keys %{ $c->components };
 -        return map { $c->_filter_component( $_, @args ) } @result if ref $name;
 +    # I would expect to return an empty list here, but that breaks back-compat
 +    $c->log->warn('Component not found, returning the list of existing');
 +    $c->log->warn('components. This behavior is deprecated and will be');
 +    $c->log->warn('removed in a future release. Use $c->component_list');
 +    $c->log->warn('instead.');
  
 -        if( $result[ 0 ] ) {
 -            $c->log->warn( Carp::shortmess(qq(Found results for "${name}" using regexp fallback)) );
 -            $c->log->warn( 'Relying on the regexp fallback behavior for component resolution' );
 -            $c->log->warn( 'is unreliable and unsafe. You have been warned' );
 -            return $c->_filter_component( $result[ 0 ], @args );
 -        }
 +    return $c->component_list;
 +}
  
 -        # I would expect to return an empty list here, but that breaks back-compat
 -    }
 +=head2 $c->component_list
  
 -    # fallback
 -    return sort keys %{ $c->components };
 -}
 +Returns the sorted list of the component names of the application.
 +
 +=cut
 +
 +sub component_list { sort keys %{ shift->components } }
  
  =head2 CLASS DATA AND HELPER CLASSES
  
@@@ -872,26 -995,11 +832,12 @@@ sub path_to 
      else { return Path::Class::File->new( $c->config->{home}, @path ) }
  }
  
  sub plugin {
      my ( $class, $name, $plugin, @args ) = @_;
  
 +    # See block comment in t/aggregate/unit_core_plugin.t
-     $class->log->warn(qq/Adding plugin using the ->plugin method is deprecated, and will be removed in Catalyst 5.81/);
+     # 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 a future release/);
  
      $class->_register_plugin( $plugin, 1 );
  
@@@ -920,6 -1028,9 +866,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 {
          }
      }
  
 +    $class->setup_config();
      $class->setup_home( delete $flags->{home} );
  
      $class->setup_log( delete $flags->{log} );
          $class->setup unless $Catalyst::__AM_RESTARTING;
      }
  
 -    # Initialize our data structure
 -    $class->components( {} );
 -
      $class->setup_components;
  
 -    if ( $class->debug ) {
 +    if (
 +        $class->debug and
 +        my $comps = $class->container->get_all_components($class)
 +    ) {
          my $column_width = Catalyst::Utils::term_width() - 8 - 9;
          my $t = Text::SimpleTable->new( [ $column_width, 'Class' ], [ 8, 'Type' ] );
 -        for my $comp ( sort keys %{ $class->components } ) {
 -            my $type = ref $class->components->{$comp} ? 'instance' : 'class';
 -            $t->row( $comp, $type );
 -        }
 -        $class->log->debug( "Loaded components:\n" . $t->draw . "\n" )
 -          if ( keys %{ $class->components } );
 -    }
 +        $t->row( $_ => ref($comps->{$_}) ? 'instance' : 'class' ) for keys %$comps;
  
 -    # Add our self to components, since we are also a component
 -    if( $class->isa('Catalyst::Controller') ){
 -      $class->components->{$class} = $class;
 +        $class->log->debug( "Loaded components:\n" . $t->draw . "\n" );
      }
  
      $class->setup_actions;
@@@ -1177,7 -1295,15 +1126,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;
      $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
  
@@@ -1259,6 -1385,30 +1216,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
@@@ -1430,23 -1580,6 +1411,23 @@@ These methods are not meant to be used 
  
  Returns a hash of components.
  
 +=cut
 +
 +sub components {
 +    my ( $class, $comps ) = @_;
 +
 +    # people create components calling this sub directly, before setup
 +    $class->setup_config unless $class->container;
 +
 +    my $container = $class->container;
 +
 +    if ( $comps ) {
 +        $container->add_component( $_ ) for keys %$comps;
 +    }
 +
 +    return $container->get_all_components($class);
 +}
 +
  =head2 $c->context_class
  
  Returns or sets the context class.
@@@ -2105,7 -2238,7 +2086,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
@@@ -2292,67 -2425,137 +2273,67 @@@ Sets up actions for a component
  
  sub setup_actions { my $c = shift; $c->dispatcher->setup_actions( $c, @_ ) }
  
 -=head2 $c->setup_components
 -
 -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.
 -
 -Installation of each component is performed by the L<setup_component> method,
 -below.
 +=head2 $c->setup_config
  
  =cut
  
 -sub setup_components {
 +sub setup_config {
      my $class = shift;
  
 -    my $config  = $class->config->{ setup_components };
 -
 -    my @comps = $class->locate_components($config);
 -    my %comps = map { $_ => 1 } @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 ) {
 +    my %args = %{ $class->config || {} };
  
 -        # We pass ignore_loaded here so that overlay files for (e.g.)
 -        # Model::DBI::Schema sub-classes are loaded - if it's in @comps
 -        # we know M::P::O found a file on disk so this is safe
 +    my $container_class;
  
 -        Catalyst::Utils::ensure_class_loaded( $component, { ignore_loaded => 1 } );
 +    if ( exists $args{container_class} ) {
 +        $container_class = delete $args{container_class};
 +        Class::MOP::load_class($container_class);
      }
 -
 -    for my $component (@comps) {
 -        my $instance = $class->components->{ $component } = $class->setup_component($component);
 -        my @expanded_components = $instance->can('expand_modules')
 -            ? $instance->expand_modules( $component, $config )
 -            : $class->expand_component_module( $component, $config );
 -        for my $component (@expanded_components) {
 -            next if $comps{$component};
 -            $class->components->{ $component } = $class->setup_component($component);
 -        }
 +    else {
 +        $container_class = Class::MOP::load_first_existing_class("${class}::Container", 'Catalyst::IOC::Container');
      }
 -}
  
 -=head2 $c->locate_components( $setup_component_config )
 +    my $container = $container_class->new( %args, application_name => "$class", name => "$class" );
 +    $class->container($container);
  
 -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>.
 +    my $config = $container->resolve( service => 'config' );
 +    $class->config($config);
 +    $class->finalize_config; # back-compat
 +}
  
 -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.
 +=head2 $c->finalize_config
  
  =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 } || [];
 +sub finalize_config { }
  
 -    push @paths, @$extra;
 -
 -    my $locator = Module::Pluggable::Object->new(
 -        search_path => [ map { s/^(?=::)/$class/; $_; } @paths ],
 -        %$config
 -    );
 -
 -    # XXX think about ditching this sort entirely
 -    my @comps = sort { length $a <=> length $b } $locator->plugins;
 +=head2 $c->setup_components
  
 -    return @comps;
 -}
 +This method is called internally to set up the application's components.
  
 -=head2 $c->expand_component_module( $component, $setup_component_config )
 +It finds modules by calling the L<locate_components> method, expands them to
 +package names with the $container->expand_component_module method, and then
 +installs each component into the application.
  
 -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.
 +The C<setup_components> config option is passed to both of the above methods.
  
  =cut
  
 -sub expand_component_module {
 -    my ($class, $module) = @_;
 -    return Devel::InnerPackage::list_packages( $module );
 -}
 +sub setup_components { shift->container->setup_components }
  
 -=head2 $c->setup_component
 +=head2 locate_components
  
  =cut
  
 -sub setup_component {
 -    my( $class, $component ) = @_;
 -
 -    unless ( $component->can( 'COMPONENT' ) ) {
 -        return $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;
 +sub locate_components {
 +    my $class = shift;
  
 -    my $instance = eval { $component->COMPONENT( $class, $config ); };
 +    $class->log->warn('The locate_components method has been deprecated.');
 +    $class->log->warn('Please read Catalyst::IOC::Container documentation to');
 +    $class->log->warn('update your application.');
  
 -    if ( my $error = $@ ) {
 -        chomp $error;
 -        Catalyst::Exception->throw(
 -            message => qq/Couldn't instantiate component "$component", "$error"/
 -        );
 -    }
 -
 -    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;
 +    # XXX think about ditching this sort entirely
 +    return sort { length $a <=> length $b }
 +        @{ $class->container->resolve( service => 'locate_components' ) };
  }
  
  =head2 $c->setup_dispatcher
@@@ -2645,7 -2848,7 +2626,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
@@@ -2791,6 -2994,14 +2772,6 @@@ C<default_view> - The default view to b
  
  =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 >>.
@@@ -2808,7 -3019,7 +2789,7 @@@ welcome screen
  
  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 *
  
@@@ -2818,12 -3029,18 +2799,12 @@@ 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<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,
@@@ -2838,7 -3055,7 +2819,7 @@@ is determined by the C<< $c->config(use
  =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.
  
@@@ -2978,6 -3195,8 +2959,8 @@@ Wiki
  
  =head2 L<Catalyst::Test> - The test suite.
  
+ =begin stopwords
  =head1 PROJECT FOUNDER
  
  sri: Sebastian Riedel <sri@cpan.org>
@@@ -2996,8 -3215,6 +2979,8 @@@ Andrew Ford E<lt>A.Ford@ford-mason.co.u
  
  Andrew Ruthven
  
 +AndrĂ© Walker
 +
  andyg: Andy Grundman <andy@hybridized.org>
  
  audreyt: Audrey Tang
@@@ -3122,6 -3339,8 +3105,8 @@@ rainboxx: Matthias Dietrich, C<perl@rai
  
  dd070: Dhaval Dhanani <dhaval070@gmail.com>
  
+ =end stopwords
  =head1 COPYRIGHT
  
  Copyright (c) 2005, the above named PROJECT FOUNDER and CONTRIBUTORS.
@@@ -53,7 -53,7 +53,7 @@@ Catalyst::Component - Catalyst Componen
  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
@@@ -63,12 -63,14 +63,12 @@@ __PACKAGE__->mk_classdata('_config')
  
  has catalyst_component_name => ( is => 'ro' ); # Cannot be required => 1 as context
                                         # class @ISA component - HATE
 -# Make accessor callable as a class method, as we need to call setup_actions
 -# on the application class, which we don't have an instance of, ewwwww
 -# Also, naughty modules like Catalyst::View::JSON try to write to _everything_,
 +# Naughty modules like Catalyst::View::JSON try to write to _everything_,
  # so spit a warning, ignore that (and try to do the right thing anyway) here..
  around catalyst_component_name => sub {
      my ($orig, $self) = (shift, shift);
      Carp::cluck("Tried to write to the catalyst_component_name accessor - is your component broken or just mad? (Write ignored - using default value.)") if scalar @_;
 -    blessed($self) ? $self->$orig() || blessed($self) : $self;
 +    return $self->$orig() || blessed($self);
  };
  
  sub BUILDARGS {
@@@ -178,7 -180,7 +178,7 @@@ The arguments are expected to be a hash
  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 {
@@@ -154,12 -154,6 +154,12 @@@ around action_namespace => sub 
  
      my $class = ref($self) || $self;
      my $appclass = ref($c) || $c;
 +
 +    # FIXME - catalyst_component_name is no longer a class accessor, because
 +    # 'MyApp as a controller' behavior is removed. But is this call to
 +    # catalyst_component_name necessary, or is it always the same as $class?
 +    my $component_name = ref($self) ? $self->catalyst_component_name : $self;
 +
      if( ref($self) ){
          return $self->$orig if $self->has_action_namespace;
      } else {
          }
      }
  
 -    my $namespace = Catalyst::Utils::class2prefix($self->catalyst_component_name, $case_s) || '';
 +    my $namespace = Catalyst::Utils::class2prefix($component_name, $case_s) || '';
      $self->$orig($namespace) if ref($self);
      return $namespace;
  };
@@@ -488,7 -482,7 +488,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).
  
@@@ -308,6 -308,9 +308,6 @@@ sub _invoke_as_path 
  sub _find_component {
      my ( $self, $c, $component ) = @_;
  
 -    # fugly, why doesn't ->component('MyApp') work?
 -    return $c if ($component eq blessed($c));
 -
      return blessed($component)
          ? $component
          : $c->component($component);
@@@ -585,8 -588,8 +585,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