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 { {} });
has stats => (is => 'rw');
has action => (is => 'rw');
has counter => (is => 'rw', default => sub { {} });
-has request => (is => 'rw', default => sub { $_[0]->request_class->new({}) }, required => 1, lazy => 1);
-has response => (is => 'rw', default => sub { $_[0]->response_class->new({}) }, required => 1, lazy => 1);
+has request => (
+ is => 'rw',
+ default => sub {
+ my $self = shift;
+ my %p = ( _log => $self->log );
+ $p{_uploadtmp} = $self->_uploadtmp if $self->_has_uploadtmp;
+ $self->request_class->new(\%p);
+ },
+ lazy => 1,
+);
+has response => (
+ is => 'rw',
+ default => sub {
+ my $self = shift;
+ $self->response_class->new({ _log => $self->log });
+ },
+ lazy => 1,
+);
has namespace => (is => 'rw');
sub depth { scalar @{ shift->stack || [] }; }
__PACKAGE__->mk_classdata($_)
for qw/components arguments dispatcher engine log dispatcher_class
engine_loader context_class request_class response_class stats_class
- setup_finished _psgi_app loading_psgi_file/;
+ setup_finished _psgi_app loading_psgi_file run_options/;
__PACKAGE__->dispatcher_class('Catalyst::Dispatcher');
__PACKAGE__->request_class('Catalyst::Request');
# Remember to update this in Catalyst::Runtime as well!
-our $VERSION = '5.9000';
+our $VERSION = '5.90009';
sub import {
my ( $class, @arguments ) = @_;
use Catalyst qw/-Debug/; # include plugins here as well
### In lib/MyApp/Controller/Root.pm (autocreated)
- sub foo : Global { # called for /foo, /foo/1, /foo/1/2, etc.
+ sub foo : Chained('/') Args() { # called for /foo, /foo/1, /foo/1/2, etc.
my ( $self, $c, @args ) = @_; # args are qw/1 2/ for /foo/1/2
$c->stash->{template} = 'foo.tt'; # set the template
# lookup something from db -- stash vars are passed to TT
[% END %]
# called for /bar/of/soap, /bar/of/soap/10, etc.
- sub bar : Path('/bar/of/soap') { ... }
-
- # called for all actions, from the top-most controller downwards
- sub auto : Private {
- my ( $self, $c ) = @_;
- if ( !$c->user_exists ) { # Catalyst::Plugin::Authentication
- $c->res->redirect( '/login' ); # require login
- return 0; # abort request and go immediately to end()
- }
- return 1; # success; carry on to next action
- }
+ sub bar : Chained('/') PathPart('/bar/of/soap') Args() { ... }
# called after all actions are finished
- sub end : Private {
+ sub end : Action {
my ( $self, $c ) = @_;
if ( scalar @{ $c->error } ) { ... } # handle errors
return if $c->res->body; # already have a response
$c->forward( 'MyApp::View::TT' ); # render template
}
- ### in MyApp/Controller/Foo.pm
- # called for /foo/bar
- sub bar : Local { ... }
-
- # called for /blargle
- sub blargle : Global { ... }
-
- # an index action matches /foo, but not /foo/1, etc.
- sub index : Private { ... }
-
- ### in MyApp/Controller/Foo/Bar.pm
- # called for /foo/bar/baz
- sub baz : Local { ... }
-
- # first Root auto is called, then Foo auto, then this
- sub auto : Private { ... }
-
- # powerful regular expression paths are also possible
- sub details : Regex('^product/(\w+)/details$') {
- my ( $self, $c ) = @_;
- # extract the (\w+) from the URI
- my $product = $c->req->captures->[0];
- }
-
See L<Catalyst::Manual::Intro> for additional information.
=head1 DESCRIPTION
+Fully::Qualified::Plugin::Name
/;
-Special flags like C<-Debug> and C<-Engine> can also be specified as
+Special flags like C<-Debug> can also be specified as
arguments when Catalyst is loaded:
use Catalyst qw/-Debug My::Module/;
This sets the log level to 'debug' and enables full debug output on the
error screen. If you only want the latter, see L<< $c->debug >>.
-=head2 -Engine
-
-Forces Catalyst to use a specific engine. Omit the
-C<Catalyst::Engine::> prefix of the engine name, i.e.:
-
- use Catalyst qw/-Engine=CGI/;
-
=head2 -Home
Forces Catalyst to use a specific home directory, e.g.:
MYAPP_WEB_HOME. If both variables are set, the MYAPP_HOME one will be used.
If none of these are set, Catalyst will attempt to automatically detect the
-home directory. If you are working in a development envirnoment, Catalyst
+home directory. If you are working in a development environment, Catalyst
will try and find the directory containing either Makefile.PL, Build.PL or
dist.ini. If the application has been installed into the system (i.e.
you have done C<make install>), then Catalyst will use the path to your
-application module, without the .pm extension (ie, /foo/MyApp if your
+application module, without the .pm extension (e.g., /foo/MyApp if your
application was installed at /foo/MyApp.pm)
=head2 -Log
Note that L<< forward|/"$c->forward( $action [, \@arguments ] )" >> implies
an C<< eval { } >> around the call (actually
-L<< execute|/"$c->execute( $class, $coderef )" >> does), thus de-fatalizing
-all 'dies' within the called action. If you want C<die> to propagate you
-need to do something like:
+L<< execute|/"$c->execute( $class, $coderef )" >> does), thus rendering all
+exceptions thrown by the called action non-fatal and pushing them onto
+$c->error instead. If you want C<die> to propagate you need to do something
+like:
$c->forward('foo');
die join "\n", @{ $c->error } if @{ $c->error };
sub detach { my $c = shift; $c->dispatcher->detach( $c, @_ ) }
+=head2 $c->visit( $action [, \@arguments ] )
+
=head2 $c->visit( $action [, \@captures, \@arguments ] )
+=head2 $c->visit( $class, $method, [, \@arguments ] )
+
=head2 $c->visit( $class, $method, [, \@captures, \@arguments ] )
Almost the same as L<< forward|/"$c->forward( $action [, \@arguments ] )" >>,
when they are invoked within the visited action. This is different from the
behavior of L<< forward|/"$c->forward( $action [, \@arguments ] )" >>, which
continues to use the $c->action object from the caller action even when
-invoked from the callee.
+invoked from the called action.
C<< $c->stash >> is kept unchanged.
sub visit { my $c = shift; $c->dispatcher->visit( $c, @_ ) }
+=head2 $c->go( $action [, \@arguments ] )
+
=head2 $c->go( $action [, \@captures, \@arguments ] )
+=head2 $c->go( $class, $method, [, \@arguments ] )
+
=head2 $c->go( $class, $method, [, \@captures, \@arguments ] )
The relationship between C<go> and
else { return Path::Class::File->new( $c->config->{home}, @path ) }
}
-=head2 $c->plugin( $name, $class, @args )
-
-Helper method for plugins. It creates a class data accessor/mutator and
-loads and instantiates the given class.
-
- MyApp->plugin( 'prototype', 'HTML::Prototype' );
-
- $c->prototype->define_javascript_functions;
-
-B<Note:> This method of adding plugins is deprecated. The ability
-to add plugins like this B<will be removed> in a Catalyst 5.81.
-Please do not use this functionality in new code.
-
-=cut
-
sub plugin {
my ( $class, $name, $plugin, @args ) = @_;
# 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 Catalyst 5.81/);
+ $class->log->warn(qq/Adding plugin using the ->plugin method is deprecated, and will be removed in a future release/);
$class->_register_plugin( $plugin, 1 );
MyApp->setup;
MyApp->setup( qw/-Debug/ );
+B<Note:> You B<should not> wrap this method with method modifiers
+or bad things will happen - wrap the C<setup_finalize> method instead.
+
=cut
sub setup {
}
my $action = $path;
- $path = $c->dispatcher->uri_for_action($action, $captures);
+ # ->uri_for( $action, \@captures_and_args, \%query_values? )
+ if( !@args && $action->number_of_args ) {
+ my $expanded_action = $c->dispatcher->expand_action( $action );
+
+ my $num_captures = $expanded_action->number_of_captures;
+ unshift @args, splice @$captures, $num_captures;
+ }
+
+ $path = $c->dispatcher->uri_for_action($action, $captures);
if (not defined $path) {
$c->log->debug(qq/Can't find uri_for action '$action' @$captures/)
if $c->debug;
$res;
}
-=head2 $c->uri_for_action( $path, \@captures?, @args?, \%query_values? )
+=head2 $c->uri_for_action( $path, \@captures_and_args?, @args?, \%query_values? )
-=head2 $c->uri_for_action( $action, \@captures?, @args?, \%query_values? )
+=head2 $c->uri_for_action( $action, \@captures_and_args?, @args?, \%query_values? )
=over
and it will create the URI /users/the-list.
+=item \@captures_and_args?
+
+Optional array reference of Captures (i.e. C<<CaptureArgs or $c->req->captures>)
+and arguments to the request. Usually used with L<Catalyst::DispatchType::Chained>
+to interpolate all the parameters in the URI.
+
+=item @args?
+
+Optional list of extra arguments - can be supplied in the
+C<< \@captures_and_args? >> array ref, or here - whichever is easier for your
+code.
+
+Your action can have zero, a fixed or a variable number of args (e.g.
+C<< Args(1) >> for a fixed number or C<< Args() >> for a variable number)..
+
+=item \%query_values?
+
+Optional array reference of query parameters to append. E.g.
+
+ { foo => 'bar' }
+
+will generate
+
+ /rest/of/your/uri?foo=bar
+
=back
=cut
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&mode=all">controllers</a>,
- <a href="http://cpansearch.perl.org/search?query=Catalyst%3A%3AModel%3A%3A&mode=all">models</a>, and
- <a href="http://cpansearch.perl.org/search?query=Catalyst%3A%3AView%3A%3A&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
EOF
}
+=head2 run_options
+
+Contains a hash of options passed from the application script, including
+the original ARGV the script received, the processed values from that
+ARGV and any extra arguments to the script which were not processed.
+
+This can be used to add custom options to your application's scripts
+and setup your application differently depending on the values of these
+options.
+
=head1 INTERNAL METHODS
These methods are not meant to be used by end users.
if ( !$response->has_body ) {
# Add a default body if none is already present
- $response->body(
- qq{<html><body><p>This item has moved <a href="$location">here</a>.</p></body></html>}
- );
+ $response->body(<<"EOF");
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>Moved</title>
+ </head>
+ <body>
+ <p>This item has moved <a href="$location">here</a>.</p>
+ </body>
+</html>
+EOF
+ $response->content_type('text/html; charset=utf-8');
}
}
return $status;
}
-=head2 $c->prepare( @arguments )
+=head2 $class->prepare( @arguments )
Creates a Catalyst context from an engine-specific request (Apache, CGI,
etc.).
=cut
+has _uploadtmp => (
+ is => 'ro',
+ predicate => '_has_uploadtmp',
+);
+
sub prepare {
my ( $class, @arguments ) = @_;
# into the application.
$class->context_class( ref $class || $class ) unless $class->context_class;
- my $c = $class->context_class->new({});
-
- # For on-demand data
- $c->request->_context($c);
- $c->response->_context($c);
+ my $uploadtmp = $class->config->{uploadtmp};
+ my $c = $class->context_class->new({ $uploadtmp ? (_uploadtmp => $uploadtmp) : ()});
#surely this is not the most efficient way to do things...
$c->stats($class->stats_class->new)->enable($c->use_stats);
$c->prepare_request(@arguments);
$c->prepare_connection;
$c->prepare_query_parameters;
- $c->prepare_headers;
- $c->prepare_cookies;
+ $c->prepare_headers; # Just hooks, no longer needed - they just
+ $c->prepare_cookies; # cause the lazy attribute on req to build
$c->prepare_path;
# Prepare the body for reading, either by prepare_body
$c->prepare_body;
}
}
+ $c->prepare_action;
}
# VERY ugly and probably shouldn't rely on ->finalize actually working
catch {
$c->response->status(400);
$c->response->content_type('text/plain');
$c->response->body('Bad Request');
+ # Note we call finalize and then die here, which escapes
+ # finalize being called in the enclosing block..
+ # It in fact couldn't be called, as we don't return $c..
+ # This is a mess - but I'm unsure you can fix this without
+ # breaking compat for people doing crazy things (we should set
+ # the 400 and just return the ctx here IMO, letting finalize get called
+ # above...
$c->finalize;
die $_;
};
- my $method = $c->req->method || '';
- my $path = $c->req->path;
- $path = '/' unless length $path;
- my $address = $c->req->address || '';
-
$c->log_request;
- $c->prepare_action;
-
return $c;
}
sub prepare_connection {
my $c = shift;
- $c->engine->prepare_connection( $c, @_ );
+ # XXX - This is called on the engine (not the request) to maintain
+ # Engine::PSGI back compat.
+ $c->engine->prepare_connection($c);
}
=head2 $c->prepare_cookies
-Prepares cookies.
+Prepares cookies by ensuring that the attribute on the request
+object has been built.
=cut
-sub prepare_cookies { my $c = shift; $c->engine->prepare_cookies( $c, @_ ) }
+sub prepare_cookies { my $c = shift; $c->request->cookies }
=head2 $c->prepare_headers
-Prepares headers.
+Prepares request headers by ensuring that the attribute on the request
+object has been built.
=cut
-sub prepare_headers { my $c = shift; $c->engine->prepare_headers( $c, @_ ) }
+sub prepare_headers { my $c = shift; $c->request->headers }
=head2 $c->prepare_parameters
=head2 $c->log_response_headers($headers);
-Hook method which can be wrapped by plugins to log the responseheaders.
+Hook method which can be wrapped by plugins to log the response headers.
No-op in the default implementation.
=cut
=cut
-sub read { my $c = shift; return $c->engine->read( $c, @_ ) }
+sub read { my $c = shift; return $c->request->read( @_ ) }
=head2 $c->run
Catalyst::EngineLoader->new({
application_name => $class,
(defined $requested_engine
- ? (requested_engine => $requested_engine) : ()),
+ ? (catalyst_engine_class => $requested_engine) : ()),
}),
);
}
+
$class->engine_loader->catalyst_engine_class;
}
sub setup_engine {
my ($class, $requested_engine) = @_;
- my $engine = $class->engine_class($requested_engine);
+ my $engine = do {
+ my $loader = $class->engine_loader;
+
+ if (!$loader || $requested_engine) {
+ $loader = Catalyst::EngineLoader->new({
+ application_name => $class,
+ (defined $requested_engine
+ ? (requested_engine => $requested_engine) : ()),
+ }),
+
+ $class->engine_loader($loader);
+ }
+
+ $loader->catalyst_engine_class;
+ };
# Don't really setup_engine -- see _setup_psgi_app for explanation.
return if $class->loading_psgi_file;
$meta->add_method(handler => sub {
my $r = shift;
- my $psgi_app = $class->psgi_app;
+ my $psgi_app = $class->_finalized_psgi_app;
$apache->call_app($r, $psgi_app);
});
},
);
- my $server_matches = sub {
- my ($re) = @_;
- return sub {
- my ($env) = @_;
- my $server = $env->{SERVER_SOFTWARE};
- return unless $server;
- return $server =~ $re ? 1 : 0;
- };
- };
-
# If we're running under Lighttpd, swap PATH_INFO and SCRIPT_NAME
# http://lists.scsys.co.uk/pipermail/catalyst/2006-June/008361.html
- $psgi_app = Plack::Middleware::LighttpdScriptNameFix->wrap($psgi_app);
+ $psgi_app = Plack::Middleware::Conditional->wrap(
+ $psgi_app,
+ builder => sub { Plack::Middleware::LighttpdScriptNameFix->wrap($_[0]) },
+ condition => sub {
+ my ($env) = @_;
+ return unless $env->{SERVER_SOFTWARE} && $env->{SERVER_SOFTWARE} =~ m!lighttpd[-/]1\.(\d+\.\d+)!;
+ return unless $1 < 4.23;
+ 1;
+ },
+ );
# we're applying this unconditionally as the middleware itself already makes
# sure it doesn't fuck things up if it's not running under one of the right
=head2 $c->registered_plugins
Returns a sorted list of the plugins which have either been stated in the
-import list or which have been added via C<< MyApp->plugin(@args); >>.
+import list.
If passed a given plugin name, it will report a boolean value indicating
whether or not that plugin is loaded. A fully qualified name is required if
sub write {
my $c = shift;
- # Finalize headers if someone manually writes output
+ # Finalize headers if someone manually writes output (for compat)
$c->finalize_headers;
- return $c->engine->write( $c, @_ );
+ return $c->response->write( @_ );
}
=head2 version
C<parse_on_demand> - The request body (for example file uploads) will not be parsed
until it is accessed. This allows you to (for example) check authentication (and reject
-the upload) before actually recieving all the data. See L</ON-DEMAND PARSER>
+the upload) before actually receiving all the data. See L</ON-DEMAND PARSER>
=item *
=item *
-C<use_request_uri_for_path> - Controlls if the C<REQUEST_URI> or C<PATH_INFO> environment
+C<use_request_uri_for_path> - Controls if the C<REQUEST_URI> or C<PATH_INFO> environment
variable should be used for determining the request path.
Most web server environments pass the requested path to the application using environment variables,
=item use_request_uri_for_path => 0
This is the default (and the) traditional method that Catalyst has used for determining the path information.
-The path is synthesised from a combination of the C<PATH_INFO> and C<SCRIPT_NAME> environment variables.
+The path is generated from a combination of the C<PATH_INFO> and C<SCRIPT_NAME> environment variables.
The allows the application to behave correctly when C<mod_rewrite> is being used to redirect requests
into the application, as these variables are adjusted by mod_rewrite to take account for the redirect.
If you do not wish to use the proxy support at all, you may set:
- MyApp->config(ignore_frontend_proxy => 1);
+ MyApp->config(ignore_frontend_proxy => 0);
+
+=head2 Note about psgi files
+
+Note that if you supply your own .psgi file, calling
+C<< MyApp->psgi_app(@_); >>, then B<this will not happen automatically>.
+
+You either need to apply L<Plack::Middleware::ReverseProxy> yourself
+in your psgi, for example:
+
+ builder {
+ enable "Plack::Middleware::ReverseProxy";
+ MyApp->psgi_app
+ };
+
+This will unconditionally add the ReverseProxy support, or you need to call
+C<< $app = MyApp->apply_default_middlewares($app) >> (to conditionally
+apply the support depending upon your config).
+
+See L<Catalyst::PSGI> for more information.
=head1 THREAD SAFETY