use File::stat;
use NEXT;
use Text::SimpleTable;
-use Path::Class;
+use Path::Class::Dir;
+use Path::Class::File;
use Time::HiRes qw/gettimeofday tv_interval/;
use URI;
use Scalar::Util qw/weaken blessed/;
use Tree::Simple qw/use_weak_refs/;
use Tree::Simple::Visitor::FindByUID;
use attributes;
+use utf8;
+use Carp qw/croak/;
__PACKAGE__->mk_accessors(
qw/counter request response state action stack namespace/
my $foodata = $c->forward('/foo');
$c->forward('index');
- $c->forward(qw/MyApp::Model::CDBI::Foo do_stuff/);
+ $c->forward(qw/MyApp::Model::DBIC::Foo do_stuff/);
$c->forward('MyApp::View::TT');
=cut
$c->error('Something bad happened');
-Clear errors. You probably don't want to clear the errors unless you are
-implementing a custom error screen.
-
- $c->error(0);
-
=cut
sub error {
my $c = shift;
if ( $_[0] ) {
my $error = ref $_[0] eq 'ARRAY' ? $_[0] : [@_];
+ croak @$error unless ref $c;
push @{ $c->{error} }, @$error;
}
elsif ( defined $_[0] ) { $c->{error} = undef }
return $c->{error} || [];
}
+=head2 $c->clear_errors
+
+Clear errors. You probably don't want to clear the errors unless you are
+implementing a custom error screen.
+
+This is equivalent to running
+
+ $c->error(0);
+
+=cut
+
+sub clear_errors {
+ my $c = shift;
+ $c->error(0);
+}
+
=head2 $c->response
=head2 $c->res
# search via regex
sub _comp_search {
- my ($c, @names) = @_;
+ my ( $c, @names ) = @_;
foreach my $name (@names) {
foreach my $component ( keys %{ $c->components } ) {
- my $comp = $c->components->{$component} if $component =~ /$name/i;
- if ($comp) {
- if ( eval { $comp->can('ACCEPT_CONTEXT'); } ) {
- return $comp->ACCEPT_CONTEXT($c);
- }
- else { return $comp }
- }
+ return $c->components->{$component} if $component =~ /$name/i;
}
}
# try explicit component names
sub _comp_explicit {
- my ($c, @names) = @_;
+ my ( $c, @names ) = @_;
foreach my $try (@names) {
- if ( exists $c->components->{$try} ) {
- my $comp = $c->components->{$try};
- if ( eval { $comp->can('ACCEPT_CONTEXT'); } ) {
- return $comp->ACCEPT_CONTEXT($c);
- }
- else { return $comp }
- }
+ return $c->components->{$try} if ( exists $c->components->{$try} );
}
return undef;
# like component, but try just these prefixes before regex searching,
# and do not try to return "sort keys %{ $c->components }"
sub _comp_prefixes {
- my ($c, $name, @prefixes) = @_;
+ my ( $c, $name, @prefixes ) = @_;
my $appclass = ref $c || $c;
return $comp;
}
+# Find possible names for a prefix
+
+sub _comp_names {
+ my ( $c, @prefixes ) = @_;
+
+ my $appclass = ref $c || $c;
+
+ my @pre = map { "${appclass}::${_}::" } @prefixes;
+
+ my @names;
+
+ COMPONENT: foreach my $comp ($c->component) {
+ foreach my $p (@pre) {
+ if ($comp =~ s/^$p//) {
+ push(@names, $comp);
+ next COMPONENT;
+ }
+ }
+ }
+
+ return @names;
+}
+
# Return a component if only one matches.
sub _comp_singular {
- my ($c, @prefixes) = @_;
+ my ( $c, @prefixes ) = @_;
my $appclass = ref $c || $c;
- my ($comp,$rest) = map { $c->_comp_search("^${appclass}::${_}::") }
- @prefixes;
+ my ( $comp, $rest ) =
+ map { $c->_comp_search("^${appclass}::${_}::") } @prefixes;
return $comp unless $rest;
}
+# 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 );
+ }
+ else { return $comp }
+}
+
=head2 COMPONENT ACCESSORS
=head2 $c->comp($name)
);
my $comp = $c->_comp_explicit(@names);
- return $comp if defined($comp);
+ return $c->_filter_component( $comp, @_ ) if defined($comp);
$comp = $c->_comp_search($name);
- return $comp if defined($comp);
+ return $c->_filter_component( $comp, @_ ) if defined($comp);
}
return sort keys %{ $c->components };
=cut
sub controller {
- my ( $c, $name ) = @_;
- return $c->_comp_prefixes($name, qw/Controller C/)
- if ($name);
- return $c->component($c->action->class);
+ my ( $c, $name, @args ) = @_;
+ return $c->_filter_component( $c->_comp_prefixes( $name, qw/Controller C/ ),
+ @args )
+ if ($name);
+ return $c->component( $c->action->class );
+}
+
+=head2 $c->controllers
+
+Returns the available names which can be passed to $c->controller
+
+=cut
+
+sub controllers {
+ my ( $c ) = @_;
+ return $c->_comp_names(qw/Controller C/);
}
=head2 $c->model($name)
=cut
sub model {
- my ( $c, $name ) = @_;
- return $c->_comp_prefixes($name, qw/Model M/)
- if $name;
- return $c->comp($c->config->{default_model})
- if $c->config->{default_model};
- return $c->_comp_singular(qw/Model M/);
+ my ( $c, $name, @args ) = @_;
+ return $c->_filter_component( $c->_comp_prefixes( $name, qw/Model M/ ),
+ @args )
+ if $name;
+ return $c->component( $c->config->{default_model} )
+ if $c->config->{default_model};
+ return $c->_filter_component( $c->_comp_singular(qw/Model M/), @args );
+
+}
+=head2 $c->models
+
+Returns the available names which can be passed to $c->model
+
+=cut
+
+sub models {
+ my ( $c ) = @_;
+ return $c->_comp_names(qw/Model M/);
}
=head2 $c->view($name)
=cut
sub view {
- my ( $c, $name ) = @_;
- return $c->_comp_prefixes($name, qw/View V/)
- if $name;
- return $c->comp($c->config->{default_view})
- if $c->config->{default_view};
- return $c->_comp_singular(qw/View V/);
+ my ( $c, $name, @args ) = @_;
+ return $c->_filter_component( $c->_comp_prefixes( $name, qw/View V/ ),
+ @args )
+ if $name;
+ return $c->component( $c->config->{default_view} )
+ if $c->config->{default_view};
+ return $c->_filter_component( $c->_comp_singular(qw/View V/) );
+}
+
+=head2 $c->views
+
+Returns the available names which can be passed to $c->view
+
+=cut
+
+sub views {
+ my ( $c ) = @_;
+ return $c->_comp_names(qw/View V/);
}
=head2 Class data and helper classes
sub path_to {
my ( $c, @path ) = @_;
- my $path = dir( $c->config->{home}, @path );
+ my $path = Path::Class::Dir->new( $c->config->{home}, @path );
if ( -d $path ) { return $path }
- else { return file( $c->config->{home}, @path ) }
+ else { return Path::Class::File->new( $c->config->{home}, @path ) }
}
=head2 $c->plugin( $name, $class, @args )
$class->setup_finished(1);
}
-=head2 $c->uri_for( $path, [ @args ] )
+=head2 $c->uri_for( $path, @args?, \%query_values? )
Merges path with C<$c-E<gt>request-E<gt>base> for absolute uri's and
with C<$c-E<gt>namespace> for relative uri's, then returns a
my $params =
( scalar @args && ref $args[$#args] eq 'HASH' ? pop @args : {} );
+ for my $value ( values %$params ) {\r
+ my $isa_ref = ref $value;\r
+ if( $isa_ref and $isa_ref ne 'ARRAY' ) {\r
+ croak( "Non-array reference ($isa_ref) passed to uri_for()" );\r
+ }\r
+ utf8::encode( $_ ) for $isa_ref ? @$value : $value;\r
+ };
+
# join args with '/', or a blank string
my $args = ( scalar @args ? '/' . join( '/', @args ) : '' );
$args =~ s/^\/// unless $path;
$class = $c->component($class) || $class;
$c->state(0);
- if ($c->depth >= $RECURSION) {
+ if ( $c->depth >= $RECURSION ) {
my $action = "$code";
$action = "/$action" unless $action =~ /\-\>/;
my $error = qq/Deep recursion detected calling "$action"/;
return $c->state;
}
-
if ( $c->debug ) {
my $action = "$code";
$action = "/$action" unless $action =~ /\-\>/;
return $c->state;
}
+=head2 $c->_localize_fields( sub { }, \%keys );
+
+=cut
+
+sub _localize_fields {
+ my ( $c, $localized, $code ) = ( @_ );
+
+ my $request = delete $localized->{request} || {};
+ my $response = delete $localized->{response} || {};
+
+ local @{ $c }{ keys %$localized } = values %$localized;
+ local @{ $c->request }{ keys %$request } = values %$request;
+ local @{ $c->response }{ keys %$response } = values %$response;
+
+ $code->();
+}
+
=head2 $c->finalize
Finalizes the request.
$c->log->error($error);
}
- $c->finalize_uploads;
-
- # Error
- if ( $#{ $c->error } >= 0 ) {
- $c->finalize_error;
+ # Allow engine to handle finalize flow (for POE)
+ if ( $c->engine->can('finalize') ) {
+ $c->engine->finalize( $c );
}
+ else {
- $c->finalize_headers;
+ $c->finalize_uploads;
- # HEAD request
- if ( $c->request->method eq 'HEAD' ) {
- $c->response->body('');
- }
+ # Error
+ if ( $#{ $c->error } >= 0 ) {
+ $c->finalize_error;
+ }
+
+ $c->finalize_headers;
- $c->finalize_body;
+ # HEAD request
+ if ( $c->request->method eq 'HEAD' ) {
+ $c->response->body('');
+ }
+
+ $c->finalize_body;
+ }
return $c->response->status;
}
if ( $c->response->body && !$c->response->content_length ) {
# get the length from a filehandle
- if ( blessed($c->response->body) && $c->response->body->can('read') ) {
+ if ( blessed( $c->response->body ) && $c->response->body->can('read') )
+ {
if ( my $stat = stat $c->response->body ) {
$c->response->content_length( $stat->size );
}
$c->res->headers->header( 'X-Catalyst' => $Catalyst::VERSION );
}
- $c->prepare_request(@arguments);
- $c->prepare_connection;
- $c->prepare_query_parameters;
- $c->prepare_headers;
- $c->prepare_cookies;
- $c->prepare_path;
-
- # On-demand parsing
- $c->prepare_body unless $c->config->{parse_on_demand};
+ # Allow engine to direct the prepare flow (for POE)
+ if ( $c->engine->can('prepare') ) {
+ $c->engine->prepare( $c, @arguments );
+ }
+ else {
+ $c->prepare_request(@arguments);
+ $c->prepare_connection;
+ $c->prepare_query_parameters;
+ $c->prepare_headers;
+ $c->prepare_cookies;
+ $c->prepare_path;
+
+ # On-demand parsing
+ $c->prepare_body unless $c->config->{parse_on_demand};
+ }
my $method = $c->req->method || '';
my $path = $c->req->path || '';
if ($home) {
$class->config->{home} ||= $home;
- $class->config->{root} ||= dir($home)->subdir('root');
+ $class->config->{root} ||= Path::Class::Dir->new($home)->subdir('root');
}
}
sub registered_plugins {
my $proto = shift;
- return sort keys %{$proto->_plugins} unless @_;
+ return sort keys %{ $proto->_plugins } unless @_;
my $plugin = shift;
return 1 if exists $proto->_plugins->{$plugin};
return exists $proto->_plugins->{"Catalyst::Plugin::$plugin"};
message => qq/Couldn't load ${type}plugin "$plugin", $error/ );
}
- $proto->_plugins->{$plugin} = 1;
+ $proto->_plugins->{$plugin} = 1;
unless ($instant) {
no strict 'refs';
unshift @{"$class\::ISA"}, $plugin;