Merge branch 'master' into gsoc_breadboard
André Walker [Wed, 22 Feb 2012 23:14:18 +0000 (21:14 -0200)]
1  2 
Makefile.PL
lib/Catalyst.pm
lib/Catalyst/Utils.pm

diff --combined Makefile.PL
@@@ -8,16 -8,15 +8,16 @@@ use Module::Install::AuthorRequires
  use Module::Install::CheckConflicts;
  use Module::Install::AuthorTests;
  
- perl_version '5.008004';
+ perl_version '5.008003';
  
  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 'B::Hooks::EndOfScope' => '0.10';
  requires 'MooseX::Emulate::Class::Accessor::Fast' => '0.00903';
  requires 'Class::Load' => '0.12';
  requires 'Class::MOP' => '0.95';
@@@ -55,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';
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 ();
@@@ -37,7 -38,7 +37,7 @@@ use Plack::Middleware::ReverseProxy
  use Plack::Middleware::IIS6ScriptNameFix;
  use Plack::Middleware::LighttpdScriptNameFix;
  
- BEGIN { require 5.008004; }
+ BEGIN { require 5.008003; }
  
  has stack => (is => 'ro', default => sub { [] });
  has stash => (is => 'rw', default => sub { {} });
@@@ -88,7 -89,7 +88,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 run_options/;
  
@@@ -99,7 -100,7 +99,7 @@@ __PACKAGE__->stats_class('Catalyst::Sta
  
  # Remember to update this in Catalyst::Runtime as well!
  
- our $VERSION = '5.90007';
+ our $VERSION = '5.90010';
  
  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
@@@ -535,6 -525,98 +535,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)
@@@ -554,7 -636,23 +554,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)
  
@@@ -577,7 -675,42 +577,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)
  
@@@ -600,23 -733,46 +600,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
@@@ -627,7 -783,7 +627,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
@@@ -638,7 -794,7 +638,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;
  }
  
  
@@@ -650,7 -806,7 +650,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)
@@@ -665,50 -821,57 +665,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
  
@@@ -859,7 -1022,6 +859,7 @@@ sub path_to 
  sub plugin {
      my ( $class, $name, $plugin, @args ) = @_;
  
 +    # See block comment in t/aggregate/unit_core_plugin.t
      # 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/);
  
@@@ -927,7 -1089,6 +927,7 @@@ 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;
@@@ -1384,18 -1553,18 +1384,18 @@@ sub welcome_message 
                      We do, however, provide you with a few starting points.</p>
                   <p>If you want to jump right into web development with Catalyst
                      you might want to start with a tutorial.</p>
- <pre>perldoc <a href="http://cpansearch.perl.org/dist/Catalyst-Manual/lib/Catalyst/Manual/Tutorial.pod">Catalyst::Manual::Tutorial</a></code>
+ <pre>perldoc <a href="https://metacpan.org/module/Catalyst::Manual::Tutorial">Catalyst::Manual::Tutorial</a></code>
  </pre>
  <p>Afterwards you can go on to check out a more complete look at our features.</p>
  <pre>
- <code>perldoc <a href="http://cpansearch.perl.org/dist/Catalyst-Manual/lib/Catalyst/Manual/Intro.pod">Catalyst::Manual::Intro</a>
+ <code>perldoc <a href="https://metacpan.org/module/Catalyst::Manual::Intro">Catalyst::Manual::Intro</a>
  <!-- Something else should go here, but the Catalyst::Manual link seems unhelpful -->
  </code></pre>
                   <h2>What to do next?</h2>
                   <p>Next it's time to write an actual application. Use the
-                     helper scripts to generate <a href="http://cpansearch.perl.org/search?query=Catalyst%3A%3AController%3A%3A&amp;mode=all">controllers</a>,
-                     <a href="http://cpansearch.perl.org/search?query=Catalyst%3A%3AModel%3A%3A&amp;mode=all">models</a>, and
-                     <a href="http://cpansearch.perl.org/search?query=Catalyst%3A%3AView%3A%3A&amp;mode=all">views</a>;
+                     helper scripts to generate <a href="https://metacpan.org/search?q=Catalyst%3A%3AController">controllers</a>,
+                     <a href="https://metacpan.org/search?q=Catalyst%3A%3AModel">models</a>, and
+                     <a href="https://metacpan.org/search?q=Catalyst%3A%3AView">views</a>;
                      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
@@@ -1446,23 -1615,6 +1446,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.
@@@ -2315,67 -2467,137 +2315,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
@@@ -2823,6 -3045,14 +2823,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 >>.
@@@ -2850,6 -3080,12 +2850,6 @@@ 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.
  
@@@ -3047,8 -3283,6 +3047,8 @@@ Andrew Ford E<lt>A.Ford@ford-mason.co.u
  
  Andrew Ruthven
  
 +AndrĂ© Walker
 +
  andyg: Andy Grundman <andy@hybridized.org>
  
  audreyt: Audrey Tang
diff --combined lib/Catalyst/Utils.pm
@@@ -6,9 -6,10 +6,10 @@@ use HTTP::Request
  use Path::Class;
  use URI;
  use Carp qw/croak/;
- use Cwd;
+ use FindBin qw/ $Bin /;
  use Class::MOP;
  use String::RewritePrefix;
+ use List::MoreUtils qw/ any /;
  
  use namespace::clean;
  
@@@ -84,20 -85,6 +85,20 @@@ sub class2classsuffix 
      return $class;
  }
  
 +=head2 class2classshortsuffix($class)
 +
 +    MyApp::Controller::Foo::Bar becomes Foo::Bar
 +
 +=cut
 +
 +sub class2classshortsuffix {
 +    my $class  = shift || '';
 +    my $prefix = class2classprefix($class) || '';
 +    $class =~ s/$prefix\:://;
 +    return $class;
 +}
 +
 +
  =head2 class2env($class);
  
  Returns the environment name for class.
@@@ -168,10 -155,22 +169,22 @@@ sub class2tempdir 
      return $tmpdir->stringify;
  }
  
+ =head2 dist_indicator_file_list
+ Returns a list of files which can be tested to check if you're inside a checkout
+ =cut
+ sub dist_indicator_file_list {
+     qw/ Makefile.PL Build.PL dist.ini /;
+ }
  =head2 home($class)
  
  Returns home directory for given class.
  
+ Note that the class must be loaded for the home directory to be found using this function.
  =cut
  
  sub home {
  
              # find the @INC entry in which $file was found
              (my $path = $inc_entry) =~ s/$file$//;
-             $path ||= cwd() if !defined $path || !length $path;
-             my $home = dir($path)->absolute->cleanup;
-             # pop off /lib and /blib if they're there
-             $home = $home->parent while $home =~ /b?lib$/;
-             # only return the dir if it has a Makefile.PL or Build.PL or dist.ini
-             if (-f $home->file("Makefile.PL") or -f $home->file("Build.PL")
-                 or -f $home->file("dist.ini")) {
-                 # clean up relative path:
-                 # MyApp/script/.. -> MyApp
-                 my $dir;
-                 my @dir_list = $home->dir_list();
-                 while (($dir = pop(@dir_list)) && $dir eq '..') {
-                     $home = dir($home)->parent->parent;
-                 }
-                 return $home->stringify;
-             }
+             my $home = find_home_unloaded_in_checkout($path);
+             return $home if $home;
          }
  
          {
      return 0;
  }
  
+ =head2 find_home_unloaded_in_checkout ($path)
+ Tries to determine if C<$path> (or $FindBin::Bin if not supplied)
+ looks like a checkout. Any leading lib, script or blib components
+ will be removed, then the directory produced will be checked
+ for the existence of a C<< dist_indicator_file_list() >>.
+ If one is found, the directory will be returned, otherwise false.
+ =cut
+ sub find_home_unloaded_in_checkout {
+     my ($path) = @_;
+     $path ||= $Bin if !defined $path || !length $path;
+     my $home = dir($path)->absolute->cleanup;
+     # pop off /lib and /blib if they're there
+     $home = $home->parent while $home =~ /b?lib$/;
+     # pop off /script if it's there.
+     $home = $home->parent while $home =~ /b?script$/;
+     # only return the dir if it has a Makefile.PL or Build.PL or dist.ini
+     if (any { $_ } map { -f $home->file($_) } dist_indicator_file_list()) {
+         # clean up relative path:
+         # MyApp/script/.. -> MyApp
+         my $dir;
+         my @dir_list = $home->dir_list();
+         while (($dir = pop(@dir_list)) && $dir eq '..') {
+             $home = dir($home)->parent->parent;
+         }
+         return $home->stringify;
+     }
+ }
  =head2 prefix($class, $name);
  
  Returns a prefixed action.