use Moose::Meta::Class ();
extends 'Catalyst::Component';
use Moose::Util qw/find_meta/;
-use B::Hooks::EndOfScope ();
+use namespace::clean -except => 'meta';
use Catalyst::Exception;
use Catalyst::Exception::Detach;
use Catalyst::Exception::Go;
use URI ();
use URI::http;
use URI::https;
+use HTML::Entities;
use Tree::Simple qw/use_weak_refs/;
use Tree::Simple::Visitor::FindByUID;
use Class::C3::Adopt::NEXT;
use utf8;
use Carp qw/croak carp shortmess/;
use Try::Tiny;
+use Safe::Isa;
use Plack::Middleware::Conditional;
use Plack::Middleware::ReverseProxy;
use Plack::Middleware::IIS6ScriptNameFix;
+use Plack::Middleware::IIS7KeepAliveFix;
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;
+ $self->request_class->new($self->_build_request_constructor_args);
+ },
+ lazy => 1,
+);
+sub _build_request_constructor_args {
+ my $self = shift;
+ my %p = ( _log => $self->log );
+ $p{_uploadtmp} = $self->_uploadtmp if $self->_has_uploadtmp;
+ \%p;
+}
+
+has response => (
+ is => 'rw',
+ default => sub {
+ my $self = shift;
+ $self->response_class->new($self->_build_response_constructor_args);
+ },
+ lazy => 1,
+);
+sub _build_response_constructor_args {
+ my $self = shift;
+ { _log => $self->log };
+}
+
has namespace => (is => 'rw');
sub depth { scalar @{ shift->stack || [] }; }
# Remember to update this in Catalyst::Runtime as well!
-our $VERSION = '5.90005';
+our $VERSION = '5.90019';
sub import {
my ( $class, @arguments ) = @_;
sub _application { $_[0] }
+=encoding UTF-8
+
=head1 NAME
Catalyst - The Elegant MVC Web Application Framework
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 ] )" >>,
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
# undef for a name will return all
return keys %eligible if !defined $name;
- my $query = ref $name ? $name : qr/^$name$/i;
+ my $query = $name->$_isa('Regexp') ? $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;
+ return if $name->$_isa('Regexp');
# skip regexp fallback if configured
return
my $appclass = ref($c) || $c;
if( $name ) {
- unless ( ref($name) ) { # Direct component hash lookup to avoid costly regexps
+ unless ( $name->$_isa('Regexp') ) { # 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 ( $c, $name, @args ) = @_;
my $appclass = ref($c) || $c;
if( $name ) {
- unless ( ref($name) ) { # Direct component hash lookup to avoid costly regexps
+ unless ( $name->$_isa('Regexp') ) { # 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 $appclass = ref($c) || $c;
if( $name ) {
- unless ( ref($name) ) { # Direct component hash lookup to avoid costly regexps
+ unless ( $name->$_isa('Regexp') ) { # Direct component hash lookup to avoid costly regexps
my $comps = $c->components;
my $check = $appclass."::View::".$name;
if( exists $comps->{$check} ) {
$class->log->info("$name powered by Catalyst $Catalyst::VERSION");
}
- # Make sure that the application class becomes immutable at this point,
- B::Hooks::EndOfScope::on_scope_end {
- return if $@;
- my $meta = Class::MOP::get_metaclass_by_name($class);
- if (
- $meta->is_immutable
- && ! { $meta->immutable_options }->{replace_constructor}
- && (
- $class->isa('Class::Accessor::Fast')
- || $class->isa('Class::Accessor')
- )
- ) {
- warn "You made your application class ($class) immutable, "
- . "but did not inline the\nconstructor. "
- . "This will break catalyst, as your app \@ISA "
- . "Class::Accessor(::Fast)?\nPlease pass "
- . "(replace_constructor => 1)\nwhen making your class immutable.\n";
- }
- $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.");
sub uri_for {
my ( $c, $path, @args ) = @_;
- if (blessed($path) && $path->isa('Catalyst::Controller')) {
+ if ( $path->$_isa('Catalyst::Controller') ) {
$path = $path->path_prefix;
$path =~ s{/+\z}{};
$path .= '/';
$arg =~ s/([^$URI::uric])/$URI::Escape::escapes{$1}/go;
}
- if ( blessed($path) ) { # action object
+ if ( $path->$_isa('Catalyst::Action') ) { # action object
s|/|%2F|g for @args;
my $captures = [ map { s|/|%2F|g; $_; }
( scalar @args && ref $args[0] eq 'ARRAY'
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
=head2 run_options
Contains a hash of options passed from the application script, including
-the original ARGV the script receieved, the processed values from that
+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
$c->finalize_error;
}
- $c->finalize_headers;
+ $c->finalize_headers unless $c->response->finalized_headers;
# HEAD request
if ( $c->request->method eq 'HEAD' ) {
if ( !$response->has_body ) {
# Add a default body if none is already present
+ my $encoded_location = encode_entities($location);
$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">
<title>Moved</title>
</head>
<body>
- <p>This item has moved <a href="$location">here</a>.</p>
+ <p>This item has moved <a href="$encoded_location">here</a>.</p>
</body>
</html>
EOF
$c->finalize_cookies;
- $c->engine->finalize_headers( $c, @_ );
+ $c->response->finalize_headers();
# Done
$response->finalized_headers(1);
=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({});
+ my $uploadtmp = $class->config->{uploadtmp};
+ my $c = $class->context_class->new({ $uploadtmp ? (_uploadtmp => $uploadtmp) : ()});
- # For on-demand data
- $c->request->_context($c);
$c->response->_context($c);
#surely this is not the most efficient way to do things...
$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
=cut
-sub read { my $c = shift; return $c->engine->read( $c, @_ ) }
+sub read { my $c = shift; return $c->request->read( @_ ) }
=head2 $c->run
sub run {
my $app = shift;
+ $app->_make_immutable_if_needed;
$app->engine_loader->needs_psgi_engine_compat_hack ?
$app->engine->run($app, @_) :
$app->engine->run( $app, $app->_finalized_psgi_app, @_ );
}
+sub _make_immutable_if_needed {
+ my $class = shift;
+ my $meta = Class::MOP::get_metaclass_by_name($class);
+ my $isa_ca = $class->isa('Class::Accessor::Fast') || $class->isa('Class::Accessor');
+ if (
+ $meta->is_immutable
+ && ! { $meta->immutable_options }->{replace_constructor}
+ && $isa_ca
+ ) {
+ warn("You made your application class ($class) immutable, "
+ . "but did not inline the\nconstructor. "
+ . "This will break catalyst, as your app \@ISA "
+ . "Class::Accessor(::Fast)?\nPlease pass "
+ . "(replace_constructor => 1)\nwhen making your class immutable.\n");
+ }
+ unless ($meta->is_immutable) {
+ # XXX - FIXME warning here as you should make your app immutable yourself.
+ $meta->make_immutable(
+ replace_constructor => 1,
+ );
+ }
+}
+
=head2 $c->set_action( $action, $code, $namespace, $attrs )
Sets an action in a given namespace.
$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);
});
# 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
# IIS versions
$psgi_app = Plack::Middleware::IIS6ScriptNameFix->wrap($psgi_app);
+ # And another IIS issue, this time with IIS7.
+ $psgi_app = Plack::Middleware::Conditional->wrap(
+ $psgi_app,
+ builder => sub { Plack::Middleware::IIS7KeepAliveFix->wrap($_[0]) },
+ condition => sub {
+ my ($env) = @_;
+ return $env->{SERVER_SOFTWARE} && $env->{SERVER_SOFTWARE} =~ m!IIS/7\.[0-9]!;
+ },
+ );
+
return $psgi_app;
}
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) {
+ my $plugin_meta = Moose::Meta::Class->create($plugin);
+ if (!$plugin_meta->has_method('new')
+ && ( $plugin->isa('Class::Accessor::Fast') || $plugin->isa('Class::Accessor') ) ) {
+ $plugin_meta->add_method('new', Moose::Object->meta->get_method('new'))
+ }
+ if (!$instant && !$proto->_plugins->{$plugin}) {
my $meta = Class::MOP::get_metaclass_by_name($class);
$meta->superclasses($plugin, $meta->superclasses);
}
+ $proto->_plugins->{$plugin} = 1;
return $class;
}
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
=head2 L<Catalyst::Test> - The test suite.
-=begin stopwords
-
=head1 PROJECT FOUNDER
sri: Sebastian Riedel <sri@cpan.org>
dd070: Dhaval Dhanani <dhaval070@gmail.com>
-=end stopwords
-
=head1 COPYRIGHT
Copyright (c) 2005, the above named PROJECT FOUNDER and CONTRIBUTORS.