X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FCatalyst.pm;h=c66345c15cf3853549b604c2794d20210cc48b53;hb=2832ecea1f0b0fdfa6873ec9896523b72af01b77;hp=b60196207d6739bb3382abed49ff634195d362f8;hpb=829b22f766b0b1d1a09a6ce152abe48ad9bde5ec;p=catagits%2FCatalyst-Runtime.git diff --git a/lib/Catalyst.pm b/lib/Catalyst.pm index b601962..c66345c 100644 --- a/lib/Catalyst.pm +++ b/lib/Catalyst.pm @@ -4,7 +4,6 @@ use Moose; use Moose::Meta::Class (); extends 'Catalyst::Component'; use Moose::Util qw/find_meta/; -use bytes; use B::Hooks::EndOfScope (); use Catalyst::Exception; use Catalyst::Exception::Detach; @@ -15,6 +14,7 @@ use Catalyst::Request::Upload; use Catalyst::Response; use Catalyst::Utils; use Catalyst::Controller; +use Data::OptList; use Devel::InnerPackage (); use File::stat; use Module::Pluggable::Object (); @@ -27,11 +27,12 @@ use URI::https; use Tree::Simple qw/use_weak_refs/; use Tree::Simple::Visitor::FindByUID; use Class::C3::Adopt::NEXT; +use List::MoreUtils qw/uniq/; use attributes; use utf8; use Carp qw/croak carp shortmess/; -BEGIN { require 5.008001; } +BEGIN { require 5.008004; } has stack => (is => 'ro', default => sub { [] }); has stash => (is => 'rw', default => sub { {} }); @@ -66,7 +67,7 @@ our $GO = Catalyst::Exception::Go->new; #I imagine that very few of these really need to be class variables. if any. #maybe we should just make them attributes with a default? __PACKAGE__->mk_classdata($_) - for qw/components arguments dispatcher engine log dispatcher_class + for qw/container components arguments dispatcher engine log dispatcher_class engine_class context_class request_class response_class stats_class setup_finished/; @@ -78,14 +79,7 @@ __PACKAGE__->stats_class('Catalyst::Stats'); # Remember to update this in Catalyst::Runtime as well! -our $VERSION = '5.80007'; - -{ - my $dev_version = $VERSION =~ /_\d{2}$/; - *_IS_DEVELOPMENT_VERSION = sub () { $dev_version }; -} - -$VERSION = eval $VERSION; +our $VERSION = '5.80032'; sub import { my ( $class, @arguments ) = @_; @@ -97,11 +91,6 @@ sub import { my $caller = caller(); return if $caller eq 'main'; - # Kill Adopt::NEXT warnings if we're a non-RC version - unless (_IS_DEVELOPMENT_VERSION()) { - Class::C3::Adopt::NEXT->unimport(qr/^Catalyst::/); - } - my $meta = Moose::Meta::Class->initialize($caller); unless ( $caller->isa('Catalyst') ) { my @superclasses = ($meta->superclasses, $class, 'Catalyst::Controller'); @@ -111,7 +100,12 @@ sub import { $meta->superclasses(grep { $_ ne 'Moose::Object' } $meta->superclasses); unless( $meta->has_method('meta') ){ - $meta->add_method(meta => sub { Moose::Meta::Class->initialize("${caller}") } ); + if ($Moose::VERSION >= 1.15) { + $meta->_add_meta_method('meta'); + } + else { + $meta->add_method(meta => sub { Moose::Meta::Class->initialize("${caller}") } ); + } } $caller->arguments( [@arguments] ); @@ -254,6 +248,9 @@ environment with CATALYST_DEBUG or _DEBUG. The environment settings override the application, with _DEBUG having the highest 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 @@ -273,6 +270,14 @@ is replaced with the uppercased name of your application, any "::" in the name will be replaced with underscores, e.g. MyApp::Web should use MYAPP_WEB_HOME. If both variables are set, the MYAPP_HOME one will be used. +If none of these are set, Catalyst will attempt to automatically detect the +home directory. If you are working in a development envirnoment, Catalyst +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), then Catalyst will use the path to your +application module, without the .pm extension (ie, /foo/MyApp if your +application was installed at /foo/MyApp.pm) + =head2 -Log use Catalyst '-Log=warn,fatal,error'; @@ -281,14 +286,15 @@ Specifies a comma-delimited list of log levels. =head2 -Stats -Enables statistics collection and reporting. You can also force this setting -from the system environment with CATALYST_STATS or _STATS. The -environment settings override the application, with _STATS having the -highest priority. +Enables statistics collection and reporting. + + use Catalyst qw/-Stats=1/; -e.g. +You can also force this setting from the system environment with CATALYST_STATS +or _STATS. The environment settings override the application, with +_STATS having the highest priority. - use Catalyst qw/-Stats=1/ +Stats are also enabled if L<< debugging |/"-Debug" >> is enabled. =head1 METHODS @@ -332,8 +338,8 @@ call to forward. my $foodata = $c->forward('/foo'); $c->forward('index'); - $c->forward(qw/MyApp::Model::DBIC::Foo do_stuff/); - $c->forward('MyApp::View::TT'); + $c->forward(qw/Model::DBIC::Foo do_stuff/); + $c->forward('View::TT'); Note that L<< forward|/"$c->forward( $action [, \@arguments ] )" >> implies an C<< eval { } >> around the call (actually @@ -342,13 +348,30 @@ all 'dies' within the called action. If you want C to propagate you need to do something like: $c->forward('foo'); - die $c->error if $c->error; + die join "\n", @{ $c->error } if @{ $c->error }; Or make sure to always return true values from your actions and write your code like this: $c->forward('foo') || return; +Another note is that C<< $c->forward >> always returns a scalar because it +actually returns $c->state which operates in a scalar context. +Thus, something like: + + return @array; + +in an action that is forwarded to is going to return a scalar, +i.e. how many items are in that array, which is probably not what you want. +If you need to return an array then return a reference to it, +or stash it like so: + + $c->stash->{array} = \@array; + +and access it from the stash. + +Keep in mind that the C method used is that of the caller action. So a C<$c-Edetach> inside a forwarded action would run the C method from the original action requested. + =cut sub forward { my $c = shift; no warnings 'recursion'; $c->dispatcher->forward( $c, @_ ) } @@ -402,12 +425,19 @@ sub visit { my $c = shift; $c->dispatcher->visit( $c, @_ ) } =head2 $c->go( $class, $method, [, \@captures, \@arguments ] ) -Almost the same as L<< detach|/"$c->detach( $action [, \@arguments ] )" >>, but does a full dispatch like L, -instead of just calling the new C<$action> / -C<< $class->$method >>. This means that C, C and the -method you visit are called, just like a new request. +The relationship between C and +L<< visit|/"$c->visit( $action [, \@captures, \@arguments ] )" >> is the same as +the relationship between +L<< forward|/"$c->forward( $class, $method, [, \@arguments ] )" >> and +L<< detach|/"$c->detach( $action [, \@arguments ] )" >>. Like C<< $c->visit >>, +C<< $c->go >> will perform a full dispatch on the specified action or method, +with localized C<< $c->action >> and C<< $c->namespace >>. Like C, +C escapes the processing of the current request chain on completion, and +does not return to its caller. -C<< $c->stash >> is kept unchanged. +@arguments are arguments to the final destination of $action. @captures are +arguments to the intermediate steps, if any, on the way to the final sub of +$action. =cut @@ -487,6 +517,8 @@ sub error { =head2 $c->state Contains the return value of the last executed action. +Note that << $c->state >> operates in a scalar context which means that all +values it returns are scalar. =head2 $c->clear_errors @@ -504,93 +536,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; - - # 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]; - $short =~ s/.*?Model:://; - 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) @@ -612,11 +557,39 @@ If you want to search for controllers, pass in a regexp as the argument. sub controller { my ( $c, $name, @args ) = @_; + my $container = $c->container->get_sub_container('controller'); + my $appclass = ref $c || $c; if( $name ) { - 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 ); + if ( !ref $name ) { # Direct component hash lookup to avoid costly regexps + return $container->resolve(service => $name, parameters => { context => [ $c, @args ] } ) + if $container->has_service($name); + } + + return + if $c->config->{disable_component_resolution_regex_fallback} && !ref $name; + + my $query = ref $name ? $name : qr{$name}i; + $query =~ s/^${appclass}::(C|Controller):://; + my @comps = $container->get_service_list; + my @result; + for (@comps) { + push @result, $container->resolve( service => $_, parameters => { context => [ $c, @args ] } ) + if m/$query/; + } + + if (@result) { + if (!ref $name) { + $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 shift @result; + } + + return @result; + } + + return; } return $c->component( $c->action->class ); @@ -645,11 +618,38 @@ If you want to search for models, pass in a regexp as the argument. sub model { my ( $c, $name, @args ) = @_; + my $appclass = ref($c) || $c; + my $container = $c->container->get_sub_container('model'); if( $name ) { - 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 $name && $container->has_service($name)) { # Direct component hash lookup to avoid costly regexps + return $container->resolve( service => $name, parameters => { context => [ $c, @args ] } ); + } + + return + if $c->config->{disable_component_resolution_regex_fallback} && !ref $name; + + my $query = ref $name ? $name : qr{$name}i; + $query =~ s/^${appclass}::(M|Model):://; + my @comps = $container->get_service_list; + my @result; + for (@comps) { + push @result, $container->resolve( service => $_, parameters => { context => [ $c, @args ] } ) + if m/$query/; + } + + if (@result) { + if (!ref $name) { + $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 shift @result; + } + + return @result; + } + + return; } if (ref $c) { @@ -658,10 +658,11 @@ sub model { return $c->model( $c->stash->{current_model} ) if $c->stash->{current_model}; } - return $c->model( $c->config->{default_model} ) - if $c->config->{default_model}; + return $c->model( $appclass->config->{default_model} ) + if $appclass->config->{default_model}; - my( $comp, $rest ) = $c->_comp_search_prefixes( undef, qw/Model M/); +# FIXME: will this still be mantained? + my( $comp, $rest ) = $container->get_service_list; if( $rest ) { $c->log->warn( Carp::shortmess('Calling $c->model() will return a random model unless you specify one of:') ); @@ -671,7 +672,7 @@ sub model { $c->log->warn( 'NB: in version 5.81, the "random" behavior will not work at all.' ); } - return $c->_filter_component( $comp ); + return $container->resolve( service => $comp, parameters => { context => [ $c, @args ] } ); } @@ -698,11 +699,43 @@ If you want to search for views, pass in a regexp as the argument. sub view { my ( $c, $name, @args ) = @_; + my $appclass = ref($c) || $c; + my $container = $c->container->get_sub_container('view'); if( $name ) { - 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 $name ) { # Direct component hash lookup to avoid costly regexps + if ( $container->has_service($name) ) { + return $container->resolve( service => $name, parameters => { context => [ $c, @args ] } ); + } + else { + $c->log->warn( "Attempted to use view '$name', but does not exist" ); + } + } + + return + if $c->config->{disable_component_resolution_regex_fallback} && !ref $name; + + my $query = ref $name ? $name : qr{$name}i; + $query =~ s/^${appclass}::(V|View):://; + my @comps = $container->get_service_list; + my @result; + for (@comps) { + push @result, $container->resolve( service => $_, parameters => { context => [ $c, @args ] } ) + if m/$query/; + } + + if (@result) { + if (!ref $name) { + $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 shift @result; + } + + return @result; + } + + return; } if (ref $c) { @@ -711,10 +744,10 @@ sub view { return $c->view( $c->stash->{current_view} ) if $c->stash->{current_view}; } - return $c->view( $c->config->{default_view} ) - if $c->config->{default_view}; + return $c->view( $appclass->config->{default_view} ) + if $appclass->config->{default_view}; - my( $comp, $rest ) = $c->_comp_search_prefixes( undef, qw/View V/); + my( $comp, $rest ) = $container->get_service_list; if( $rest ) { $c->log->warn( 'Calling $c->view() will return a random view unless you specify one of:' ); @@ -724,7 +757,7 @@ sub view { $c->log->warn( 'NB: in version 5.81, the "random" behavior will not work at all.' ); } - return $c->_filter_component( $comp ); + return $container->resolve( service => $comp, parameters => { context => [ $c, @args ] } ); } =head2 $c->controllers @@ -735,7 +768,7 @@ Returns the available names which can be passed to $c->controller sub controllers { my ( $c ) = @_; - return $c->_comp_names(qw/Controller C/); + return $c->container->get_sub_container('controller')->get_service_list; } =head2 $c->models @@ -746,7 +779,7 @@ Returns the available names which can be passed to $c->model sub models { my ( $c ) = @_; - return $c->_comp_names(qw/Model M/); + return $c->container->get_sub_container('model')->get_service_list; } @@ -758,7 +791,7 @@ Returns the available names which can be passed to $c->view 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) @@ -773,40 +806,64 @@ should be used instead. If C<$name> is a regexp, a list of components matched against the full component name will be returned. +If Catalyst can't find a component by name, it will fallback to regex +matching by default. To disable this behaviour set +disable_component_resolution_regex_fallback to a true value. + + __PACKAGE__->config( disable_component_resolution_regex_fallback => 1 ); + =cut sub component { - my ( $c, $name, @args ) = @_; + my ( $c, $component, @args ) = @_; - if( $name ) { - my $comps = $c->components; + if ( $component ) { + my ($type, $name) = _get_component_type_name($component); - if( !ref $name ) { - # is it the exact name? - return $c->_filter_component( $comps->{ $name }, @args ) - if exists $comps->{ $name }; + if ($type && $c->container->has_sub_container($type)) { + my $container = $c->container->get_sub_container($type); + + if( !ref $component && $container->has_service($name) ) { + return $container->resolve( service => $name, parameters => { context => [ $c, @args ] } ); + } - # perhaps we just omitted "MyApp"? - my $composed = ( ref $c || $c ) . "::${name}"; - return $c->_filter_component( $comps->{ $composed }, @args ) - if exists $comps->{ $composed }; + return + if $c->config->{disable_component_resolution_regex_fallback}; - # 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; + my $query = qr{$name}i; + my @components = $container->get_service_list; + my @result = grep { m{$query} } @components; + + if (@result) { + $c->log->warn( Carp::shortmess(qq(Found results for "${component}" 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 $container->resolve( service => $result[0], parameters => { context => [$c, @args] } ); + } } + return + if $c->config->{disable_component_resolution_regex_fallback} && !ref $component; + # This is here so $c->comp( '::M::' ) works - my $query = ref $name ? $name : qr{$name}i; + my $query = ref $component ? $component : qr{$component}i; + + for my $subcontainer_name (qw/model view controller/) { + my $subcontainer = $c->container->get_sub_container($subcontainer_name); + my @components = $subcontainer->get_service_list; + my @result = grep { m{$query} } @components; - my @result = grep { m{$query} } keys %{ $c->components }; - return map { $c->_filter_component( $_, @args ) } @result if ref $name; + if (@result) { + return map { $subcontainer->resolve( service => $_, parameters => { context => [$c, @args] } ) } @result + if ref $component; - 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 ); + $c->log->warn( Carp::shortmess(qq(Found results for "${component}" 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 $subcontainer->resolve( service => $result[0], parameters => { context => [$c, @args] } ); + } } # I would expect to return an empty list here, but that breaks back-compat @@ -843,7 +900,7 @@ component is constructed. For example: MyApp->config({ 'Model::Foo' => { bar => 'baz', overrides => 'me' } }); - MyApp::Model::Foo->config({ quux => 'frob', 'overrides => 'this' }); + MyApp::Model::Foo->config({ quux => 'frob', overrides => 'this' }); will mean that C receives the following data when constructed: @@ -854,6 +911,27 @@ constructed: overrides => 'me', }); +It's common practice to use a Moose attribute +on the receiving component to access the config value. + + package MyApp::Model::Foo; + + use Moose; + + # this attr will receive 'baz' at construction time + has 'bar' => ( + is => 'rw', + isa => 'Str', + ); + +You can then get the value 'baz' by calling $c->model('Foo')->bar +(or $self->bar inside code in the model). + +B you MUST NOT call C<< $self->config >> or C<< __PACKAGE__->config >> +as a way of reading config within your code, as this B give you the +correctly merged config back. You B take the config values supplied to +the constructor and use those instead. + =cut around config => sub { @@ -902,6 +980,8 @@ You can enable debug mode in several ways: =back +The first three also set the log level to 'debug'. + Calling C<< $c->debug(1) >> has no effect. =cut @@ -1021,6 +1101,7 @@ sub setup { } } + $class->setup_config(); $class->setup_home( delete $flags->{home} ); $class->setup_log( delete $flags->{log} ); @@ -1112,7 +1193,6 @@ EOF my $name = $class->config->{name} || 'Application'; $class->log->info("$name powered by Catalyst $Catalyst::VERSION"); } - $class->log->_flush() if $class->log->can('_flush'); # Make sure that the application class becomes immutable at this point, B::Hooks::EndOfScope::on_scope_end { @@ -1132,30 +1212,37 @@ EOF . "Class::Accessor(::Fast)?\nPlease pass " . "(replace_constructor => 1)\nwhen making your class immutable.\n"; } - $meta->make_immutable(replace_constructor => 1) - unless $meta->is_immutable; + $meta->make_immutable( + replace_constructor => 1, + ) unless $meta->is_immutable; }; + if ($class->config->{case_sensitive}) { + $class->log->warn($class . "->config->{case_sensitive} is set."); + $class->log->warn("This setting is deprecated and planned to be removed in Catalyst 5.81."); + } + $class->setup_finalize; + # Should be the last thing we do so that user things hooking + # setup_finalize can log.. + $class->log->_flush() if $class->log->can('_flush'); + return 1; # Explicit return true as people have __PACKAGE__->setup as the last thing in their class. HATE. } - =head2 $app->setup_finalize -A hook to attach modifiers to. -Using C<< after setup => sub{}; >> doesn't work, because of quirky things done for plugin setup. -Also better than C< setup_finished(); >, as that is a getter method. +A hook to attach modifiers to. This method does not do anything except set the +C accessor. - sub setup_finalize { +Applying method modifiers to the C method doesn't work, because of quirky things done for plugin setup. - my $app = shift; - - ## do stuff, i.e., determine a primary key column for sessions stored in a DB - - $app->next::method(@_); +Example: + after setup_finalize => sub { + my $app = shift; - } + ## do stuff here.. + }; =cut @@ -1164,17 +1251,23 @@ sub setup_finalize { $class->setup_finished(1); } -=head2 $c->uri_for( $path, @args?, \%query_values? ) +=head2 $c->uri_for( $path?, @args?, \%query_values? ) =head2 $c->uri_for( $action, \@captures?, @args?, \%query_values? ) Constructs an absolute L object based on the application root, the provided path, and the additional arguments and query parameters provided. -When used as a string, provides a textual URI. +When used as a string, provides a textual URI. If you need more flexibility +than this (i.e. the option to provide relative URIs etc.) see +L. + +If no arguments are provided, the URI for the current action is returned. +To return the current action and also provide @args, use +C<< $c->uri_for( $c->action, @args ) >>. If the first argument is a string, it is taken as a public URI path relative to C<< $c->namespace >> (if it doesn't begin with a forward slash) or -relative to the application root (if it does). It is then merged with +relative to the application root (if it does). It is then merged with C<< $c->request->base >>; any C<@args> are appended as additional path components; and any C<%query_values> are appended as C parameters. @@ -1186,13 +1279,13 @@ once the path is resolved, C continues as though a path was provided, appending any arguments or parameters and creating an absolute URI. -The captures for the current request can be found in +The captures for the current request can be found in C<< $c->request->captures >>, and actions can be resolved using C<< Catalyst::Controller->action_for($name) >>. If you have a private action path, use C<< $c->uri_for_action >> instead. # Equivalent to $c->req->uri - $c->uri_for($c->action, $c->req->captures, + $c->uri_for($c->action, $c->req->captures, @{ $c->req->args }, $c->req->params); # For the Foo action in the Bar controller @@ -1206,10 +1299,35 @@ path, use C<< $c->uri_for_action >> instead. sub uri_for { my ( $c, $path, @args ) = @_; + if (blessed($path) && $path->isa('Catalyst::Controller')) { + $path = $path->path_prefix; + $path =~ s{/+\z}{}; + $path .= '/'; + } + + undef($path) if (defined $path && $path eq ''); + + my $params = + ( scalar @args && ref $args[$#args] eq 'HASH' ? pop @args : {} ); + + carp "uri_for called with undef argument" if grep { ! defined $_ } @args; + foreach my $arg (@args) { + utf8::encode($arg) if utf8::is_utf8($arg); + $arg =~ s/([^$URI::uric])/$URI::Escape::escapes{$1}/go; + } + if ( blessed($path) ) { # action object - my $captures = ( scalar @args && ref $args[0] eq 'ARRAY' - ? shift(@args) - : [] ); + s|/|%2F|g for @args; + my $captures = [ map { s|/|%2F|g; $_; } + ( scalar @args && ref $args[0] eq 'ARRAY' + ? @{ shift(@args) } + : ()) ]; + + foreach my $capture (@$captures) { + utf8::encode($capture) if utf8::is_utf8($capture); + $capture =~ s/([^$URI::uric])/$URI::Escape::escapes{$1}/go; + } + my $action = $path; $path = $c->dispatcher->uri_for_action($action, $captures); if (not defined $path) { @@ -1220,14 +1338,6 @@ sub uri_for { $path = '/' if $path eq ''; } - undef($path) if (defined $path && $path eq ''); - - my $params = - ( scalar @args && ref $args[$#args] eq 'HASH' ? pop @args : {} ); - - carp "uri_for called with undef argument" if grep { ! defined $_ } @args; - s/([^$URI::uric])/$URI::Escape::escapes{$1}/go for @args; - unshift(@args, $path); unless (defined $path && $path =~ s!^/!!) { # in-place strip @@ -1287,6 +1397,20 @@ $c->uri_for >>. You can also pass in a Catalyst::Action object, in which case it is passed to C<< $c->uri_for >>. +Note that although the path looks like a URI that dispatches to the wanted action, it is not a URI, but an internal path to that action. + +For example, if the action looks like: + + package MyApp::Controller::Users; + + sub lst : Path('the-list') {} + +You can use: + + $c->uri_for_action('/users/lst') + +and it will create the URI /users/the-list. + =back =cut @@ -1419,7 +1543,7 @@ sub welcome_message { models, and views; they can save you a lot of work.

-
script/${prefix}_create.pl -help
+
script/${prefix}_create.pl --help

Also, be sure to check out the vast and growing collection of plugins for Catalyst on CPAN; you are likely to find what you need there. @@ -1458,6 +1582,36 @@ These methods are not meant to be used by end users. Returns a hash of components. +=cut + +around components => sub { + my $orig = shift; + my $class = shift; + my $comps = shift; + + return $class->$orig if ( !$comps ); + +# FIXME: should this ugly kludge exist? + $class->setup_config unless defined $class->container; + +# FIXME: should there be a warning here, not to use this accessor to create the components? + my $components = {}; + + my $containers; + $containers->{$_} = $class->container->get_sub_container($_) for qw(model view controller); + + for my $component ( keys %$comps ) { + $components->{ $component } = $comps->{$component}; + + my ($type, $name) = _get_component_type_name($component); + +# FIXME: shouldn't the service name be $name? + $containers->{$type}->add_service(Catalyst::BlockInjection->new( name => $name, block => sub { return $class->setup_component($component) } )); + } + + return $class->$orig($components); +}; + =head2 $c->context_class Returns or sets the context class. @@ -1529,7 +1683,9 @@ sub execute { push( @{ $c->stack }, $code ); no warnings 'recursion'; - eval { $c->state( $code->execute( $class, $c, @{ $c->req->args } ) || 0 ) }; + # N.B. This used to be combined, but I have seen $c get clobbered if so, and + # I have no idea how, ergo $ret (which appears to fix the issue) + eval { my $ret = $code->execute( $class, $c, @{ $c->req->args } ) || 0; $c->state( $ret ) }; $c->_stats_finish_execute( $stats_info ) if $c->use_stats and $stats_info; @@ -1551,17 +1707,17 @@ sub execute { $error = qq/Caught exception in $class->$name "$error"/; } $c->error($error); - $c->state(0); } + $c->state(0); } return $c->state; } sub _stats_start_execute { my ( $c, $code ) = @_; - + my $appclass = ref($c) || $c; return if ( ( $code->name =~ /^_.*/ ) - && ( !$c->config->{show_internal_actions} ) ); + && ( !$appclass->config->{show_internal_actions} ) ); my $action_name = $code->reverse(); $c->counter->{$action_name}++; @@ -1589,9 +1745,10 @@ sub _stats_start_execute { # is this a root-level call or a forwarded call? if ( $callsub =~ /forward$/ ) { + my $parent = $c->stack->[-1]; # forward, locate the caller - if ( my $parent = $c->stack->[-1] ) { + if ( defined $parent && exists $c->counter->{"$parent"} ) { $c->stats->profile( begin => $action, parent => "$parent" . $c->counter->{"$parent"}, @@ -1661,6 +1818,8 @@ sub finalize { $c->finalize_body; } + $c->log_response; + if ($c->use_stats) { my $elapsed = sprintf '%f', $c->stats->elapsed; my $av = $elapsed == 0 ? '??' : sprintf '%.3f', 1 / $elapsed; @@ -1723,10 +1882,10 @@ sub finalize_headers { } # Content-Length - if ( $response->body && !$response->content_length ) { + if ( defined $response->body && length $response->body && !$response->content_length ) { # get the length from a filehandle - if ( blessed( $response->body ) && $response->body->can('read') ) + if ( blessed( $response->body ) && $response->body->can('read') || ref( $response->body ) eq 'GLOB' ) { my $stat = stat $response->body; if ( $stat && $stat->size > 0 ) { @@ -1738,7 +1897,7 @@ sub finalize_headers { } else { # everything should be bytes at this point, but just in case - $response->content_length( bytes::length( $response->body ) ); + $response->content_length( length( $response->body ) ); } } @@ -1793,7 +1952,7 @@ namespaces. sub get_actions { my $c = shift; $c->dispatcher->get_actions( $c, @_ ) } -=head2 $c->handle_request( $class, @arguments ) +=head2 $app->handle_request( @arguments ) Called to handle each HTTP request. @@ -1853,7 +2012,7 @@ sub prepare { #surely this is not the most efficient way to do things... $c->stats($class->stats_class->new)->enable($c->use_stats); - if ( $c->debug ) { + if ( $c->debug || $c->config->{enable_catalyst_header} ) { $c->res->headers->header( 'X-Catalyst' => $Catalyst::VERSION ); } @@ -1875,7 +2034,7 @@ sub prepare { $c->prepare_read; # Parse the body unless the user wants it on-demand - unless ( $c->config->{parse_on_demand} ) { + unless ( ref($c)->config->{parse_on_demand} ) { $c->prepare_body; } } @@ -1885,8 +2044,7 @@ sub prepare { $path = '/' unless length $path; my $address = $c->req->address || ''; - $c->log->debug(qq/"$method" request for "$path" from "$address"/) - if $c->debug; + $c->log_request; $c->prepare_action; @@ -1916,17 +2074,6 @@ sub prepare_body { $c->engine->prepare_body( $c, @_ ); $c->prepare_parameters; $c->prepare_uploads; - - if ( $c->debug && keys %{ $c->req->body_parameters } ) { - my $t = Text::SimpleTable->new( [ 35, 'Parameter' ], [ 36, 'Value' ] ); - for my $key ( sort keys %{ $c->req->body_parameters } ) { - my $param = $c->req->body_parameters->{$key}; - my $value = defined($param) ? $param : ''; - $t->row( $key, - ref $value eq 'ARRAY' ? ( join ', ', @$value ) : $value ); - } - $c->log->debug( "Body Parameters are:\n" . $t->draw ); - } } =head2 $c->prepare_body_chunk( $chunk ) @@ -2010,55 +2157,156 @@ sub prepare_query_parameters { my $c = shift; $c->engine->prepare_query_parameters( $c, @_ ); +} - if ( $c->debug && keys %{ $c->request->query_parameters } ) { - my $t = Text::SimpleTable->new( [ 35, 'Parameter' ], [ 36, 'Value' ] ); - for my $key ( sort keys %{ $c->req->query_parameters } ) { - my $param = $c->req->query_parameters->{$key}; - my $value = defined($param) ? $param : ''; - $t->row( $key, - ref $value eq 'ARRAY' ? ( join ', ', @$value ) : $value ); - } - $c->log->debug( "Query Parameters are:\n" . $t->draw ); +=head2 $c->log_request + +Writes information about the request to the debug logs. This includes: + +=over 4 + +=item * Request method, path, and remote IP address + +=item * Query keywords (see L) + +=item * Request parameters + +=item * File uploads + +=back + +=cut + +sub log_request { + my $c = shift; + + return unless $c->debug; + + my($dump) = grep {$_->[0] eq 'Request' } $c->dump_these; + my $request = $dump->[1]; + + my ( $method, $path, $address ) = ( $request->method, $request->path, $request->address ); + $method ||= ''; + $path = '/' unless length $path; + $address ||= ''; + $c->log->debug(qq/"$method" request for "$path" from "$address"/); + + $c->log_request_headers($request->headers); + + if ( my $keywords = $request->query_keywords ) { + $c->log->debug("Query keywords are: $keywords"); } + + $c->log_request_parameters( query => $request->query_parameters, $request->_has_body ? (body => $request->body_parameters) : () ); + + $c->log_request_uploads($request); } -=head2 $c->prepare_read +=head2 $c->log_response -Prepares the input for reading. +Writes information about the response to the debug logs by calling +C<< $c->log_response_status_line >> and C<< $c->log_response_headers >>. =cut -sub prepare_read { my $c = shift; $c->engine->prepare_read( $c, @_ ) } +sub log_response { + my $c = shift; -=head2 $c->prepare_request + return unless $c->debug; -Prepares the engine request. + my($dump) = grep {$_->[0] eq 'Response' } $c->dump_these; + my $response = $dump->[1]; + + $c->log_response_status_line($response); + $c->log_response_headers($response->headers); +} + +=head2 $c->log_response_status_line($response) + +Writes one line of information about the response to the debug logs. This includes: + +=over 4 + +=item * Response status code + +=item * Content-Type header (if present) + +=item * Content-Length header (if present) + +=back =cut -sub prepare_request { my $c = shift; $c->engine->prepare_request( $c, @_ ) } +sub log_response_status_line { + my ($c, $response) = @_; -=head2 $c->prepare_uploads + $c->log->debug( + sprintf( + 'Response Code: %s; Content-Type: %s; Content-Length: %s', + $response->status || 'unknown', + $response->headers->header('Content-Type') || 'unknown', + $response->headers->header('Content-Length') || 'unknown' + ) + ); +} -Prepares uploads. +=head2 $c->log_response_headers($headers); + +Hook method which can be wrapped by plugins to log the responseheaders. +No-op in the default implementation. =cut -sub prepare_uploads { - my $c = shift; +sub log_response_headers {} - $c->engine->prepare_uploads( $c, @_ ); +=head2 $c->log_request_parameters( query => {}, body => {} ) + +Logs request parameters to debug logs + +=cut + +sub log_request_parameters { + my $c = shift; + my %all_params = @_; + + return unless $c->debug; + + my $column_width = Catalyst::Utils::term_width() - 44; + foreach my $type (qw(query body)) { + my $params = $all_params{$type}; + next if ! keys %$params; + my $t = Text::SimpleTable->new( [ 35, 'Parameter' ], [ $column_width, 'Value' ] ); + for my $key ( sort keys %$params ) { + my $param = $params->{$key}; + my $value = defined($param) ? $param : ''; + $t->row( $key, ref $value eq 'ARRAY' ? ( join ', ', @$value ) : $value ); + } + $c->log->debug( ucfirst($type) . " Parameters are:\n" . $t->draw ); + } +} + +=head2 $c->log_request_uploads - if ( $c->debug && keys %{ $c->request->uploads } ) { +Logs file uploads included in the request to the debug logs. +The parameter name, filename, file type, and file size are all included in +the debug logs. + +=cut + +sub log_request_uploads { + my $c = shift; + my $request = shift; + return unless $c->debug; + my $uploads = $request->uploads; + if ( keys %$uploads ) { my $t = Text::SimpleTable->new( [ 12, 'Parameter' ], [ 26, 'Filename' ], [ 18, 'Type' ], [ 9, 'Size' ] ); - for my $key ( sort keys %{ $c->request->uploads } ) { - my $upload = $c->request->uploads->{$key}; + for my $key ( sort keys %$uploads ) { + my $upload = $uploads->{$key}; for my $u ( ref $upload eq 'ARRAY' ? @{$upload} : ($upload) ) { $t->row( $key, $u->filename, $u->type, $u->size ); } @@ -2067,6 +2315,68 @@ sub prepare_uploads { } } +=head2 $c->log_request_headers($headers); + +Hook method which can be wrapped by plugins to log the request headers. +No-op in the default implementation. + +=cut + +sub log_request_headers {} + +=head2 $c->log_headers($type => $headers) + +Logs L (either request or response) to the debug logs. + +=cut + +sub log_headers { + my $c = shift; + my $type = shift; + my $headers = shift; # an HTTP::Headers instance + + return unless $c->debug; + + my $column_width = Catalyst::Utils::term_width() - 28; + my $t = Text::SimpleTable->new( [ 15, 'Header Name' ], [ $column_width, 'Value' ] ); + $headers->scan( + sub { + my ( $name, $value ) = @_; + $t->row( $name, $value ); + } + ); + $c->log->debug( ucfirst($type) . " Headers:\n" . $t->draw ); +} + + +=head2 $c->prepare_read + +Prepares the input for reading. + +=cut + +sub prepare_read { my $c = shift; $c->engine->prepare_read( $c, @_ ) } + +=head2 $c->prepare_request + +Prepares the engine request. + +=cut + +sub prepare_request { my $c = shift; $c->engine->prepare_request( $c, @_ ) } + +=head2 $c->prepare_uploads + +Prepares uploads. + +=cut + +sub prepare_uploads { + my $c = shift; + + $c->engine->prepare_uploads( $c, @_ ); +} + =head2 $c->prepare_write Prepares the output for writing. @@ -2077,11 +2387,11 @@ sub prepare_write { my $c = shift; $c->engine->prepare_write( $c, @_ ) } =head2 $c->request_class -Returns or sets the request class. +Returns or sets the request class. Defaults to L. =head2 $c->response_class -Returns or sets the response class. +Returns or sets the response class. Defaults to L. =head2 $c->read( [$maxlength] ) @@ -2124,42 +2434,61 @@ 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::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 +} + +=head $c->finalize_config + +=cut + +sub finalize_config { } + =head2 $c->setup_components -Sets up components. Specify a C config option to pass -additional options directly to L. To add additional -search paths, specify a key named C as an array -reference. Items in the array beginning with C<::> will have the -application class name prepended to them. +This method is called internally to set up the application's components. + +It finds modules by calling the L method, expands them to +package names with the L method, and then installs +each component into the application. + +The C config option is passed to both of the above methods. -All components found will also have any -L loaded and set up as components. -Note, that modules which are B an I of the main -file namespace loaded will not be instantiated as components. +Installation of each component is performed by the L method, +below. =cut sub setup_components { my $class = shift; - my @paths = qw( ::Controller ::C ::Model ::M ::View ::V ); my $config = $class->config->{ setup_components }; - my $extra = delete $config->{ search_extra } || []; - - push @paths, @$extra; - - my $locator = Module::Pluggable::Object->new( - search_path => [ map { s/^(?=::)/$class/; $_; } @paths ], - %$config - ); - my @comps = sort { length $a <=> length $b } $locator->plugins; + my @comps = $class->locate_components($config); my %comps = map { $_ => 1 } @comps; - my $deprecated_component_names = grep { /::[CMV]::/ } @comps; + my $deprecatedcatalyst_component_names = grep { /::[CMV]::/ } @comps; $class->log->warn(qq{Your application is using the deprecated ::[MVC]:: type naming scheme.\n}. qq{Please switch your class names to ::Model::, ::View:: and ::Controller: as appropriate.\n} - ) if $deprecated_component_names; + ) if $deprecatedcatalyst_component_names; for my $component ( @comps ) { @@ -2168,36 +2497,100 @@ sub setup_components { # we know M::P::O found a file on disk so this is safe Catalyst::Utils::ensure_class_loaded( $component, { ignore_loaded => 1 } ); - #Class::MOP::load_class($component); - - my $module = $class->setup_component( $component ); - my %modules = ( - $component => $module, - map { - $_ => $class->setup_component( $_ ) - } grep { - not exists $comps{$_} - } Devel::InnerPackage::list_packages( $component ) - ); + } + + my $containers; + $containers->{$_} = $class->container->get_sub_container($_) for qw(model view controller); + + for my $component (@comps) { + my $instance = $class->components->{ $component } = $class->setup_component($component); + if ( my ($type, $name) = _get_component_type_name($component) ) { + $containers->{$type}->add_service(Catalyst::BlockInjection->new( name => $name, block => sub { return $instance } )); + } + 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}; + + $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; + + if (my ($type, $name) = _get_component_type_name($component)) { + $containers->{$type}->add_service(Catalyst::BlockInjection->new( name => $name, block => sub { return $class->setup_component($component) } )); + } - for my $key ( keys %modules ) { - $class->components->{ $key } = $modules{ $key }; + $class->components->{ $component } = $class->setup_component($component); } } } -=head2 $c->setup_component +sub _get_component_type_name { + my $component = shift; + my @parts = split /::/, $component; -=cut + while (my $type = shift @parts) { + return ('controller', join '::', @parts) + if $type =~ /^(c|controller)$/i; -sub _controller_init_base_classes { - my ($app_class, $component) = @_; - foreach my $class ( reverse @{ mro::get_linear_isa($component) } ) { - Moose::Meta::Class->initialize( $class ) - unless find_meta($class); + return ('model', join '::', @parts) + if $type =~ /^(m|model)$/i; + + return ('view', join '::', @parts) + if $type =~ /^(v|view)$/i; } } +=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. + +Specify a C config option to pass additional options directly +to L. To add additional search paths, specify a key named +C as an array reference. Items in the array beginning with C<::> +will have the application class name prepended to them. + +=cut + +sub locate_components { + my $class = shift; + my $config = shift; + + my @paths = qw( ::Controller ::C ::Model ::M ::View ::V ); + my $extra = delete $config->{ search_extra } || []; + + push @paths, @$extra; + + my $locator = Module::Pluggable::Object->new( + search_path => [ map { s/^(?=::)/$class/; $_; } @paths ], + %$config + ); + + # XXX think about ditching this sort entirely + my @comps = sort { length $a <=> length $b } $locator->plugins; + + return @comps; +} + +=head2 $c->expand_component_module( $component, $setup_component_config ) + +Components found by C will be passed to this method, which +is expected to return a list of component (package) names to be set up. + +=cut + +sub expand_component_module { + my ($class, $module) = @_; + return Devel::InnerPackage::list_packages( $module ); +} + +=head2 $c->setup_component + +=cut + sub setup_component { my( $class, $component ) = @_; @@ -2205,20 +2598,13 @@ sub setup_component { return $component; } - # FIXME - Ugly, ugly hack to ensure the we force initialize non-moose base classes - # nearest to Catalyst::Controller first, no matter what order stuff happens - # to be loaded. There are TODO tests in Moose for this, see - # f2391d17574eff81d911b97be15ea51080500003 - if ($component->isa('Catalyst::Controller')) { - $class->_controller_init_base_classes($component); - } - my $suffix = Catalyst::Utils::class2classsuffix( $component ); my $config = $class->config->{ $suffix } || {}; - $config->{_component_name} = $component; # Put this in args here, rather - # than in COMPONENT as there - # are lots of custom COMPONENT - # methods.. + # 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 = $@ ) { @@ -2227,8 +2613,7 @@ sub setup_component { message => qq/Couldn't instantiate component "$component", "$error"/ ); } - - unless (blessed $instance) { + elsif (!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; @@ -2238,6 +2623,7 @@ sub setup_component { qq/Couldn't instantiate component "$component", COMPONENT() method (from $component_method_from) didn't return an object-like value (value was $value)./ ); } + return $instance; } @@ -2501,16 +2887,12 @@ the plugin name does not begin with C. my $class = ref $proto || $proto; Class::MOP::load_class( $plugin ); - + $class->log->warn( "$plugin inherits from 'Catalyst::Component' - this is deprecated and will not work in 5.81" ) + if $plugin->isa( 'Catalyst::Component' ); $proto->_plugins->{$plugin} = 1; unless ($instant) { - no strict 'refs'; - if ( my $meta = Class::MOP::get_metaclass_by_name($class) ) { - my @superclasses = ($plugin, $meta->superclasses ); - $meta->superclasses(@superclasses); - } else { - unshift @{"$class\::ISA"}, $plugin; - } + my $meta = Class::MOP::get_metaclass_by_name($class); + $meta->superclasses($plugin, $meta->superclasses); } return $class; } @@ -2519,22 +2901,29 @@ the plugin name does not begin with C. my ( $class, $plugins ) = @_; $class->_plugins( {} ) unless $class->_plugins; - $plugins ||= []; + $plugins = Data::OptList::mkopt($plugins || []); - my @plugins = Catalyst::Utils::resolve_namespace($class . '::Plugin', 'Catalyst::Plugin', @$plugins); + my @plugins = map { + [ Catalyst::Utils::resolve_namespace( + $class . '::Plugin', + 'Catalyst::Plugin', $_->[0] + ), + $_->[1], + ] + } @{ $plugins }; for my $plugin ( reverse @plugins ) { - Class::MOP::load_class($plugin); - my $meta = find_meta($plugin); + Class::MOP::load_class($plugin->[0], $plugin->[1]); + my $meta = find_meta($plugin->[0]); next if $meta && $meta->isa('Moose::Meta::Role'); - $class->_register_plugin($plugin); + $class->_register_plugin($plugin->[0]); } my @roles = - map { $_->name } - grep { $_ && blessed($_) && $_->isa('Moose::Meta::Role') } - map { find_meta($_) } + map { $_->[0]->name, $_->[1] } + grep { blessed($_->[0]) && $_->[0]->isa('Moose::Meta::Role') } + map { [find_meta($_->[0]), $_->[1]] } @plugins; Moose::Util::apply_all_roles( @@ -2548,15 +2937,24 @@ the plugin name does not begin with C. Returns an arrayref of the internal execution stack (actions that are currently executing). +=head2 $c->stats + +Returns the current timing statistics object. By default Catalyst uses +L, but can be set otherwise with +L<< stats_class|/"$c->stats_class" >>. + +Even if L<< -Stats|/"-Stats" >> is not enabled, the stats object is still +available. By enabling it with C< $c->stats->enabled(1) >, it can be used to +profile explicitly, although MyApp.pm still won't profile nor output anything +by itself. + =head2 $c->stats_class -Returns or sets the stats (timing statistics) class. +Returns or sets the stats (timing statistics) class. L is used by default. =head2 $c->use_stats -Returns 1 when stats collection is enabled. Stats collection is enabled -when the -Stats options is set, debug is on or when the _STATS -environment variable is set. +Returns 1 when L<< stats collection|/"-Stats" >> is enabled. Note that this is a static method, not an accessor and should be overridden by declaring C in your MyApp.pm, not by calling C<< $c->use_stats(1) >>. @@ -2592,6 +2990,78 @@ messages in template systems. sub version { return $Catalyst::VERSION } +=head1 CONFIGURATION + +There are a number of 'base' config variables which can be set: + +=over + +=item * + +C - The default model picked if you say C<< $c->model >>. See L<< /$c->model($name) >>. + +=item * + +C - The default view to be rendered or returned when C<< $c->view >> is called. See L<< /$c->view($name) >>. + +=item * + +C - 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 will be returned. + +=item * + +C - The application home directory. In an uninstalled application, +this is the top level application directory. In an installed application, +this will be the directory containing C<< MyApp.pm >>. + +=item * + +C - See L + +=item * + +C - The name of the application in debug messages and the debug and +welcome screens + +=item * + +C - 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 + +=item * + +C - The root directory for templates. Usually this is just a +subdirectory of the home directory, but you can set it to change the +templates to a different directory. + +=item * + +C - 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 - If true, causes internal actions such as C<< _DISPATCH >> +to be shown in hit debug tables in the test server. + +=item * + +C - Controlls if the C or C environment +variable should be used for determining the request path. See L +for more information. + +=item * + +C - See L. + +=back + =head1 INTERNAL ACTIONS Catalyst uses internal actions like C<_DISPATCH>, C<_BEGIN>, C<_AUTO>, @@ -2600,16 +3070,6 @@ action table, but you can make them visible with a config parameter. MyApp->config(show_internal_actions => 1); -=head1 CASE SENSITIVITY - -By default Catalyst is not case sensitive, so C is -mapped to C. You can activate case sensitivity with a config -parameter. - - MyApp->config(case_sensitive => 1); - -This causes C to map to C. - =head1 ON-DEMAND PARSER The request body is usually parsed at the beginning of a request, @@ -2637,6 +3097,18 @@ changes are made to the request. The host value for $c->req->base and $c->req->uri is set to the real host, as read from the HTTP X-Forwarded-Host header. +Additionally, you may be running your backend application on an insecure +connection (port 80) while your frontend proxy is running under SSL. If there +is a discrepancy in the ports, use the HTTP header C to +tell Catalyst what port the frontend listens on. This will allow all URIs to +be created properly. + +In the case of passing in: + + X-Forwarded-Port: 443 + +All calls to C will result in an https link, as is expected. + Obviously, your web server must support these headers for this to work. In a more complex server farm environment where you may have your @@ -2707,9 +3179,11 @@ abw: Andy Wardley acme: Leon Brocard +abraxxa: Alexander Hartmaier + Andrew Bramble -Andrew Ford +Andrew Ford EA.Ford@ford-mason.co.ukE Andrew Ruthven @@ -2725,8 +3199,18 @@ chansen: Christian Hansen chicks: Christopher Hicks +Chisel Wright C + +Danijel Milicevic C + +David Kamholz Edkamholz@cpan.orgE + +David Naughton, C + David E. Wheeler +dhoss: Devin Austin + dkubb: Dan Kubb Drew Taylor @@ -2737,19 +3221,25 @@ esskar: Sascha Kiefer fireartist: Carl Franks +frew: Arthur Axel "fREW" Schmidt + gabb: Danijel Milicevic Gary Ashton Jones +Gavin Henry C + Geoff Richards +groditi: Guillermo Roditi + hobbs: Andrew Rodland ilmari: Dagfinn Ilmari Mannsåker jcamacho: Juan Camacho -jester: Jesse Sheidlower +jester: Jesse Sheidlower C jhannah: Jay Hannah @@ -2759,6 +3249,10 @@ Johan Lindstrom jon: Jon Schutz +Jonathan Rockway C<< >> + +Kieren Diment C + konobi: Scott McWhirter marcus: Marcus Ramberg @@ -2779,6 +3273,8 @@ numa: Dan Sully obra: Jesse Vincent +Octavian Rasnita + omega: Andreas Marienborg Oleg Kostyuk @@ -2789,14 +3285,36 @@ rafl: Florian Ragwitz random: Roland Lammel +Robert Sedlacek C<< >> + +SpiceMan: Marcel Montes + sky: Arthur Bergman +szbalint: Balint Szilakszi + t0m: Tomas Doran Ulf Edvinsson +Viljo Marrandi C + +Will Hawes C + willert: Sebastian Willert +wreis: Wallace Reis + +Yuval Kogman, C + +rainboxx: Matthias Dietrich, C + +dd070: Dhaval Dhanani + +=head1 COPYRIGHT + +Copyright (c) 2005, the above named PROJECT FOUNDER and CONTRIBUTORS. + =head1 LICENSE This library is free software. You can redistribute it and/or modify it under