X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Runtime.git;a=blobdiff_plain;f=lib%2FCatalyst.pm;h=28b8a27d9305726926aef1280dad0b13b62a3985;hp=c29078918ce993c8ad68cf507f12804e7b8537b7;hb=96d8d513f11c4fc078b4956a779a68f411cfbee6;hpb=fcea752e101dc4cb4af187c047fc8dec40a85032 diff --git a/lib/Catalyst.pm b/lib/Catalyst.pm index c290789..28b8a27 100644 --- a/lib/Catalyst.pm +++ b/lib/Catalyst.pm @@ -1,7 +1,9 @@ package Catalyst; -use strict; -use base 'Catalyst::Component'; +use MRO::Compat; +use mro 'c3'; +use Moose; +extends 'Catalyst::Component'; use bytes; use Catalyst::Exception; use Catalyst::Log; @@ -13,12 +15,13 @@ use Catalyst::Controller; use Devel::InnerPackage (); use File::stat; use Module::Pluggable::Object (); -use NEXT; use Text::SimpleTable (); use Path::Class::Dir (); use Path::Class::File (); use Time::HiRes qw/gettimeofday tv_interval/; use URI (); +use URI::http; +use URI::https; use Scalar::Util qw/weaken blessed/; use Tree::Simple qw/use_weak_refs/; use Tree::Simple::Visitor::FindByUID; @@ -28,21 +31,34 @@ use Carp qw/croak carp/; BEGIN { require 5.008001; } -__PACKAGE__->mk_accessors( - qw/counter request response state action stack namespace stats/ -); +has stack => (is => 'rw', default => sub { [] }); +has stash => (is => 'rw', default => sub { {} }); +has state => (is => 'rw', default => 0); +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 namespace => (is => 'rw'); + +no Moose; attributes->import( __PACKAGE__, \&namespace, 'lvalue' ); sub depth { scalar @{ shift->stack || [] }; } +sub comp { shift->component(@_) } -# Laziness++ -*comp = \&component; -*req = \&request; -*res = \&response; +sub req { + # carp "the use of req() is deprecated in favour of request()"; + my $self = shift; return $self->request(@_); +} +sub res { + # carp "the use of res() is deprecated in favour of response()"; + my $self = shift; return $self->response(@_); +} # For backwards compatibility -*finalize_output = \&finalize_body; +sub finalize_output { shift->finalize_body(@_) }; # For statistics our $COUNT = 1; @@ -50,18 +66,22 @@ our $START = time; our $RECURSION = 1000; our $DETACH = "catalyst_detach\n"; +#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 - engine_class context_class request_class response_class setup_finished/; + engine_class context_class request_class response_class stats_class + setup_finished/; __PACKAGE__->dispatcher_class('Catalyst::Dispatcher'); __PACKAGE__->engine_class('Catalyst::Engine::CGI'); __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.7007'; +our $VERSION = '5.7013'; sub import { my ( $class, @arguments ) = @_; @@ -70,11 +90,17 @@ sub import { # callers @ISA. return unless $class eq 'Catalyst'; - my $caller = caller(0); + my $caller = caller(); + return if $caller eq 'main'; + my $meta = Moose::Meta::Class->initialize($caller); + #Moose->import({ into => $caller }); #do we want to do this? unless ( $caller->isa('Catalyst') ) { - no strict 'refs'; - push @{"$caller\::ISA"}, $class, 'Catalyst::Controller'; + my @superclasses = ($meta->superclasses, $class, 'Catalyst::Controller'); + $meta->superclasses(@superclasses); + } + unless( $meta->has_method('meta') ){ + $meta->add_method(meta => sub { Moose::Meta::Class->initialize("${caller}") } ); } $caller->arguments( [@arguments] ); @@ -109,7 +135,7 @@ documentation and tutorials. ### in lib/MyApp.pm use Catalyst qw/-Debug/; # include plugins here as well - ### In lib/MyApp/Controller/Root.pm (autocreated) + ### In lib/MyApp/Controller/Root.pm (autocreated) sub foo : Global { # 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 @@ -238,6 +264,17 @@ MYAPP_WEB_HOME. If both variables are set, the MYAPP_HOME one will be used. Specifies log level. +=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. + +e.g. + + use Catalyst qw/-Stats=1/ + =head1 METHODS =head2 INFORMATION ABOUT THE CURRENT REQUEST @@ -297,7 +334,7 @@ your code like this: =cut -sub forward { my $c = shift; $c->dispatcher->forward( $c, @_ ) } +sub forward { my $c = shift; no warnings 'recursion'; $c->dispatcher->forward( $c, @_ ) } =head2 $c->detach( $action [, \@arguments ] ) @@ -343,14 +380,17 @@ sub stash { my $c = shift; if (@_) { my $stash = @_ > 1 ? {@_} : $_[0]; - croak('stash takes a hash or hashref') unless ref $stash; + croak('stash takes a hash or hashref') unless ref $stash; foreach my $key ( keys %$stash ) { - $c->{stash}->{$key} = $stash->{$key}; + #shouldn't we hold this in a var and save ourselves the subcall? + $c->next::method->{$key} = $stash->{$key}; } } - return $c->{stash}; + + return $c->next::method; } + =head2 $c->error =head2 $c->error($error, ...) @@ -667,7 +707,7 @@ sub config { $c->log->warn("Setting config after setup has been run is not a good idea.") if ( @_ and $c->setup_finished ); - $c->NEXT::config(@_); + $c->next::method(@_); } =head2 $c->log @@ -774,7 +814,7 @@ Catalyst> line. sub setup { my ( $class, @arguments ) = @_; - + Class::C3::initialize; $class->log->warn("Running setup twice is not a good idea.") if ( $class->setup_finished ); @@ -811,6 +851,7 @@ sub setup { $class->setup_plugins( delete $flags->{plugins} ); $class->setup_dispatcher( delete $flags->{dispatcher} ); $class->setup_engine( delete $flags->{engine} ); + $class->setup_stats( delete $flags->{stats} ); for my $flag ( sort keys %{$flags} ) { @@ -882,7 +923,9 @@ EOF } # Add our self to components, since we are also a component - $class->components->{$class} = $class; + if( $class->isa('Catalyst::Controller') ){ + $class->components->{$class} = $class; + } $class->setup_actions; @@ -893,6 +936,7 @@ EOF $class->log->_flush() if $class->log->can('_flush'); $class->setup_finished(1); + Class::C3::initialize; } =head2 $c->uri_for( $path, @args?, \%query_values? ) @@ -904,6 +948,9 @@ If the last argument to C is a hash reference, it is assumed to contain GET parameter key/value pairs, which will be appended to the URI in standard fashion. +Note that uri_for is destructive to the passed hashref. Subsequent calls +with the same hashref may have unintended results. + Instead of C<$path>, you can also optionally pass a C<$action> object which will be resolved to a path using C<< $c->dispatcher->uri_for_action >>; if the first element of @@ -914,11 +961,6 @@ to C. sub uri_for { my ( $c, $path, @args ) = @_; - my $base = $c->request->base->clone; - my $basepath = $base->path; - $basepath =~ s/\/$//; - $basepath .= '/'; - my $namespace = $c->namespace || ''; if ( Scalar::Util::blessed($path) ) { # action object my $captures = ( scalar @args && ref $args[0] eq 'ARRAY' @@ -929,38 +971,53 @@ sub uri_for { $path = '/' if $path eq ''; } - # massage namespace, empty if absolute path - $namespace =~ s/^\/// if $namespace; - $namespace .= '/' if $namespace; - $path = '' if !defined $path; - $namespace = '' if $path =~ /^\//; - $path =~ s/^\///; - $path =~ s/\?/%3F/g; + undef($path) if (defined $path && $path eq ''); my $params = ( scalar @args && ref $args[$#args] eq 'HASH' ? pop @args : {} ); - for my $value ( values %$params ) { - next unless defined $value; - for ( ref $value eq 'ARRAY' ? @$value : $value ) { - $_ = "$_"; - utf8::encode( $_ ); + 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 + my $namespace = $c->namespace; + if (defined $path) { # cheesy hack to handle path '../foo' + $namespace =~ s{(?:^|/)[^/]+$}{} while $args[0] =~ s{^\.\./}{}; } - }; + unshift(@args, $namespace || ''); + } # join args with '/', or a blank string - my $args = ( scalar @args ? '/' . join( '/', map { - unless (defined) { - carp "uri_for called with undefined argument"; - $_=''; - } - s/\?/%3F/g; $_ - } @args ) : '' ); - $args =~ s/^\/// unless length $path; - my $res = - URI->new_abs( URI->new_abs( "$path$args", "$basepath$namespace" ), $base ) - ->canonical; - $res->query_form(%$params); + my $args = join('/', grep { defined($_) } @args); + $args =~ s/\?/%3F/g; # STUPID STUPID SPECIAL CASE + $args =~ s!^/!!; + my $base = $c->req->base; + my $class = ref($base); + $base =~ s{(?{$_}; + $val = '' unless defined $val; + (map { + $_ = "$_"; + utf8::encode( $_ ) if utf8::is_utf8($_); + # using the URI::Escape pattern here so utf8 chars survive + s/([^A-Za-z0-9\-_.!~*'() ])/$URI::Escape::escapes{$1}/go; + s/ /+/g; + "${key}=$_"; } ( ref $val eq 'ARRAY' ? @$val : $val )); + } @keys); + } + + my $res = bless(\"${base}${args}${query}", $class); $res; } @@ -981,8 +1038,8 @@ sub welcome_message { "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> - - + + $name on Catalyst $VERSION