Merge branch 'master' into gsoc_breadboard
André Walker [Mon, 25 Jul 2011 23:08:02 +0000 (20:08 -0300)]
1  2 
lib/Catalyst.pm

diff --combined lib/Catalyst.pm
@@@ -67,7 -67,7 +67,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_class context_class request_class response_class stats_class
    setup_finished/;
  
@@@ -79,7 -79,7 +79,7 @@@ __PACKAGE__->stats_class('Catalyst::Sta
  
  # Remember to update this in Catalyst::Runtime as well!
  
- our $VERSION = '5.80032';
+ our $VERSION = '5.80033';
  
  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
@@@ -547,6 -536,98 +547,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)
@@@ -569,9 -650,19 +569,9 @@@ If you want to search for controllers, 
  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 );
 -    }
 +    $name ||= Catalyst::Utils::class2classshortsuffix( $c->action->class );
  
 -    return $c->component( $c->action->class );
 +    return $c->container->get_component_from_sub_container( 'controller', $name, $c, @args);
  }
  
  =head2 $c->model($name)
@@@ -597,16 -688,38 +597,16 @@@ If you want to search for models, pass 
  
  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) {
 +    if (ref $c && !$name) {
          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};
 +            if $c->stash->{current_model_instance};
  
 -    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.' );
 +        $name = $c->stash->{current_model}
 +            if $c->stash->{current_model};
      }
  
 -    return $c->_filter_component( $comp );
 +    return $c->container->get_component_from_sub_container( 'model', $name, $c, @args);
  }
  
  
@@@ -634,15 -747,43 +634,15 @@@ If you want to search for views, pass i
  sub view {
      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."::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) {
 +    if (ref $c && !$name) {
          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};
 -
 -    my( $comp, $rest ) = $c->_comp_search_prefixes( undef, qw/View V/);
 +            if $c->stash->{current_view_instance};
  
 -    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 = $c->stash->{current_view}
 +            if $c->stash->{current_view};
      }
  
 -    return $c->_filter_component( $comp );
 +    return $c->container->get_component_from_sub_container( 'view', $name, $c, @args);
  }
  
  =head2 $c->controllers
@@@ -653,7 -794,7 +653,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
@@@ -664,7 -805,7 +664,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;
  }
  
  
@@@ -676,7 -817,7 +676,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)
@@@ -691,50 -832,57 +691,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
  
@@@ -900,7 -1048,7 +900,7 @@@ Please do not use this functionality i
  sub plugin {
      my ( $class, $name, $plugin, @args ) = @_;
  
 -    # See block comment in t/unit_core_plugin.t
 +    # 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/);
  
      $class->_register_plugin( $plugin, 1 );
@@@ -964,7 -1112,6 +964,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_types = $class->container->get_components_types
 +    ) {
          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( @$_ ) for @comps_types;
  
 -    # 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;
@@@ -1437,25 -1592,6 +1437,25 @@@ 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(
 +            $_, $class
 +        ) for keys %$comps;
 +    }
 +
 +    return $container->get_all_components();
 +}
 +
  =head2 $c->context_class
  
  Returns or sets the context class.
@@@ -2278,34 -2414,6 +2278,34 @@@ Sets up actions for a component
  
  sub setup_actions { my $c = shift; $c->dispatcher->setup_actions( $c, @_ ) }
  
 +=head2 $c->setup_config
 +
 +=cut
 +
 +sub setup_config {
 +    my $class = shift;
 +
 +    my %args = %{ $class->config || {} };
 +
 +    my @container_classes = ( "${class}::Container", 'Catalyst::IOC::Container');
 +    unshift @container_classes, delete $args{container_class} if exists $args{container_class};
 +
 +    my $container_class = Class::MOP::load_first_existing_class(@container_classes);
 +
 +    my $container = $container_class->new( %args, name => "$class" );
 +    $class->container($container);
 +
 +    my $config = $container->resolve(service => 'config');
 +    $class->config($config);
 +    $class->finalize_config; # back-compat
 +}
 +
 +=head2 $c->finalize_config
 +
 +=cut
 +
 +sub finalize_config { }
 +
  =head2 $c->setup_components
  
  This method is called internally to set up the application's components.
@@@ -2316,6 -2424,9 +2316,6 @@@ 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.
 -
  =cut
  
  sub setup_components {
  
      my $config  = $class->config->{ setup_components };
  
 +    Catalyst::Exception->throw(
 +        qq{You are using search_extra config option. That option is\n} .
 +        qq{deprecated, please refer to the documentation for\n} .
 +        qq{other ways of achieving the same results.\n}
 +    ) if delete $config->{ search_extra };
 +
      my @comps = $class->locate_components($config);
      my %comps = map { $_ => 1 } @comps;
  
          Catalyst::Utils::ensure_class_loaded( $component, { ignore_loaded => 1 } );
      }
  
 +    my $container = $class->container;
 +
      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 );
 +        $container->add_component( $component, $class );
 +# FIXME - $instance->expand_modules() is broken
 +        my @expanded_components = $class->expand_component_module( $component, $config );
          for my $component (@expanded_components) {
              next if $comps{$component};
 -            $class->components->{ $component } = $class->setup_component($component);
 +
 +            $deprecatedcatalyst_component_names = grep { /::[CMV]::/ } @expanded_components;
 +            $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;
 +
 +            $container->add_component( $component, $class );
          }
      }
 +
 +    $container->get_sub_container('model')->make_single_default;
 +    $container->get_sub_container('view')->make_single_default;
  }
  
 +
  =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.
 +to L<Module::Pluggable>.
  
  =cut
  
@@@ -2384,6 -2480,9 +2384,6 @@@ sub locate_components 
      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 ],
@@@ -2408,6 -2507,46 +2408,6 @@@ sub expand_component_module 
      return Devel::InnerPackage::list_packages( $module );
  }
  
 -=head2 $c->setup_component
 -
 -=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;
 -
 -    my $instance = eval { $component->COMPONENT( $class, $config ); };
 -
 -    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;
 -}
 -
  =head2 $c->setup_dispatcher
  
  Sets up dispatcher.
@@@ -2787,6 -2926,14 +2787,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 >>.
@@@ -2814,6 -2961,12 +2814,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.
  
@@@ -2954,8 -3107,6 +2954,8 @@@ Andrew Ford E<lt>A.Ford@ford-mason.co.u
  
  Andrew Ruthven
  
 +AndrĂ© Walker
 +
  andyg: Andy Grundman <andy@hybridized.org>
  
  audreyt: Audrey Tang