X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Runtime.git;a=blobdiff_plain;f=lib%2FCatalyst.pm;h=d70d4c48a3c9d69bb5d393f9c749cfe55e87bdc2;hp=d8641265bef6e5d9d579e447fe5665da081f70b8;hb=aceee8b5c5fa1198e7c77652ba58049d4582b8d8;hpb=532f0516947cf047368e91150ca2ab1cfd4c1e64 diff --git a/lib/Catalyst.pm b/lib/Catalyst.pm index d864126..d70d4c4 100644 --- a/lib/Catalyst.pm +++ b/lib/Catalyst.pm @@ -30,6 +30,7 @@ use Class::C3::Adopt::NEXT; use List::MoreUtils qw/uniq/; use attributes; use String::RewritePrefix; +use Catalyst::EngineLoader; use utf8; use Carp qw/croak carp shortmess/; use Try::Tiny; @@ -70,18 +71,17 @@ our $GO = Catalyst::Exception::Go->new; #maybe we should just make them attributes with a default? __PACKAGE__->mk_classdata($_) for qw/components arguments dispatcher engine log dispatcher_class - engine_class context_class request_class response_class stats_class - setup_finished psgi_app/; + engine_loader context_class request_class response_class stats_class + setup_finished _psgi_app/; __PACKAGE__->dispatcher_class('Catalyst::Dispatcher'); -__PACKAGE__->engine_class('Catalyst::Engine'); __PACKAGE__->request_class('Catalyst::Request'); __PACKAGE__->response_class('Catalyst::Response'); __PACKAGE__->stats_class('Catalyst::Stats'); # Remember to update this in Catalyst::Runtime as well! -our $VERSION = '5.80024'; +our $VERSION = '5.89001'; sub import { my ( $class, @arguments ) = @_; @@ -102,7 +102,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] ); @@ -367,6 +372,8 @@ or stash it like so: 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, @_ ) } @@ -430,6 +437,10 @@ 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. +@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 sub go { my $c = shift; $c->dispatcher->go( $c, @_ ) } @@ -743,7 +754,12 @@ sub view { unless ( ref($name) ) { # Direct component hash lookup to avoid costly regexps my $comps = $c->components; my $check = $appclass."::View::".$name; - return $c->_filter_component( $comps->{$check}, @args ) if exists $comps->{$check}; + 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; @@ -894,7 +910,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: @@ -905,6 +921,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 { @@ -1079,7 +1116,10 @@ sub setup { $class->setup_log( delete $flags->{log} ); $class->setup_plugins( delete $flags->{plugins} ); $class->setup_dispatcher( delete $flags->{dispatcher} ); - $class->setup_engine( delete $flags->{engine} ); + if (my $engine = delete $flags->{engine}) { + $class->log->warn("Specifying the engine in ->setup is no longer supported, XXX FIXME"); + } + $class->setup_engine(); $class->setup_stats( delete $flags->{stats} ); for my $flag ( sort keys %{$flags} ) { @@ -1229,7 +1269,9 @@ sub setup_finalize { 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 @@ -1623,7 +1665,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; @@ -1686,7 +1730,7 @@ sub _stats_start_execute { my $parent = $c->stack->[-1]; # forward, locate the caller - if ( exists $c->counter->{"$parent"} ) { + if ( defined $parent && exists $c->counter->{"$parent"} ) { $c->stats->profile( begin => $action, parent => "$parent" . $c->counter->{"$parent"}, @@ -1820,10 +1864,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 ) { @@ -2144,7 +2188,7 @@ sub log_request { $c->log->debug("Query keywords are: $keywords"); } - $c->log_request_parameters( query => $request->query_parameters, body => $request->body_parameters ); + $c->log_request_parameters( query => $request->query_parameters, $request->_has_body ? (body => $request->body_parameters) : () ); $c->log_request_uploads($request); } @@ -2334,11 +2378,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] ) @@ -2363,7 +2407,7 @@ Starts the engine. =cut -sub run { my $c = shift; return $c->engine->run( $c, @_ ) } +sub run { my $c = shift; return $c->engine->run( $c, $c->psgi_app, @_ ) } =head2 $c->set_action( $action, $code, $namespace, $attrs ) @@ -2401,8 +2445,7 @@ sub setup_components { my $config = $class->config->{ setup_components }; - my @comps = sort { length $a <=> length $b } - $class->locate_components($config); + my @comps = $class->locate_components($config); my %comps = map { $_ => 1 } @comps; my $deprecatedcatalyst_component_names = grep { /::[CMV]::/ } @comps; @@ -2457,7 +2500,8 @@ sub locate_components { %$config ); - my @comps = $locator->plugins; + # XXX think about ditching this sort entirely + my @comps = sort { length $a <=> length $b } $locator->plugins; return @comps; } @@ -2547,59 +2591,111 @@ Sets up engine. =cut -sub setup_engine { - my ($class, $engine) = @_; +sub engine_class { + my $class = shift; + $class->engine_loader->catalyst_engine_class(@_); +} - unless ($engine) { - $engine = $class->engine_class; - } - else { - $engine = String::RewritePrefix->rewrite( { '' => 'Catalyst::Engine::', '+' => '' }, $engine ); - } +sub setup_engine { + my ($class) = @_; - $engine = 'Catalyst::Engine' if $engine eq 'Catalyst::Engine::HTTP'; + $class->engine_loader(Catalyst::EngineLoader->new(application_name => $class)); + my $engine = $class->engine_class; Class::MOP::load_class($engine); - # check for old engines that are no longer compatible - my $old_engine; - if ( $engine->isa('Catalyst::Engine::Apache') - && !Catalyst::Engine::Apache->VERSION ) - { - $old_engine = 1; + if ($ENV{MOD_PERL}) { + my $apache = $class->engine_loader->auto; + # FIXME - Immutable + $class->meta->add_method(handler => sub { + my $r = shift; + my $psgi_app = $class->psgi_app; + $apache->call_app($r, $psgi_app); + }); } - elsif ( $engine->isa('Catalyst::Engine::Server::Base') - && Catalyst::Engine::Server->VERSION le '0.02' ) - { - $old_engine = 1; - } + $class->engine( $engine->new ); - elsif ($engine->isa('Catalyst::Engine::HTTP::POE') - && $engine->VERSION eq '0.01' ) - { - $old_engine = 1; - } + return; +} - elsif ($engine->isa('Catalyst::Engine::Zeus') - && $engine->VERSION eq '0.01' ) - { - $old_engine = 1; +=head2 $c->psgi_app + +Builds a PSGI application coderef for the catalyst application C<$c> using +Lsetup_psgi_app">, stores it internally, and returns it. On the next call +to this method, C won't be invoked again, but its persisted +return value of it will be returned. + +This is the top-level entrypoint for things that need a full blown Catalyst PSGI +app. If you only need the raw PSGI application, without any middlewares, use +Lraw_psgi_app"> instead. + +=cut + +sub psgi_app { + my ($app) = @_; + + unless ($app->_psgi_app) { + my $psgi_app = $app->setup_psgi_app; + $app->_psgi_app($psgi_app); } - if ($old_engine) { - Catalyst::Exception->throw( message => - qq/Engine "$engine" is not supported by this version of Catalyst/ + return $app->_psgi_app; +} + +=head2 $c->setup_psgi_app + +Builds a PSGI application coderef for the catalyst application C<$c>. + +If we're able to locate a C<${myapp}.psgi> file in the applications home +directory, we'll use that to obtain our code reference. + +Otherwise the raw psgi app, without any middlewares is created using +C and wrapped into L +conditionally. See L. + +=cut + +sub setup_psgi_app { + my ($app) = @_; + + if (my $home = Path::Class::Dir->new($app->config->{home})) { + my $psgi_file = $home->file( + Catalyst::Utils::appprefix($app) . '.psgi', ); - } - if ($ENV{MOD_PERL}) { - # FIXME - Immutable - $class->meta->add_method(handler => sub { shift->handle_request(@_) }); - } + return Plack::Util::load_psgi($psgi_file) + if -e $psgi_file; + } + + # Note - this is for back compatibility. Catalyst should not know + # or care about how it's deployed. The recommended way of + # configuring this is now to use the ReverseProxy middleware + # yourself if you want it in a .psgi file. + return Plack::Middleware::Conditional->wrap( + $app->raw_psgi_app, + builder => sub { Plack::Middleware::ReverseProxy->wrap($_[0]) }, + condition => sub { + my ($env) = @_; + return if $app->config->{ignore_frontend_proxy}; + return $env->{REMOTE_ADDR} eq '127.0.0.1' + || $app->config->{using_frontend_proxy}; + }, + ); +} - $class->engine( $engine->new ); - $class->psgi_app( $class->engine->build_psgi_app($class) ); +=head2 $c->raw_psgi_app + +Returns a PSGI application code reference for the catalyst application +C<$c>. This is the bare application without any middlewares +applied. C<${myapp}.psgi> is not taken into account. See +Lsetup_psgi_app">. + +=cut + +sub raw_psgi_app { + my ($app) = @_; + return $app->engine->build_psgi_app($app); } =head2 $c->setup_home @@ -3139,6 +3235,10 @@ wreis: Wallace Reis Yuval Kogman, C +rainboxx: Matthias Dietrich, C + +dd070: Dhaval Dhanani + =head1 LICENSE This library is free software. You can redistribute it and/or modify it under