Permalink
Browse files

Getting rid of DirController; path attribute for controllers

  • Loading branch information...
1 parent c0d2aac commit 85a9c83ad21e6ceaa8dbbc345a626f815e602ae5 Zbigniew Lukasiak committed May 3, 2012
View
@@ -18,3 +18,7 @@ Revision history for WebNano
docs improvements (Plack::Middleware::Auth::Form mentioned)
{{$NEXT}}
+ got rid of DirController (add sub search_subcontrollers { 1 } to get the functionality
+ with standard Controller
+ path attribute for the Controller class - removing all the path parameter passing
+ dispatch_to_class factored out
View
@@ -1,4 +1,2 @@
-Change controller attribute self_uri to self_path.
-
Make debug messages appear in Plack stacktrace page.
View
@@ -12,6 +12,8 @@ Plack::Request = 0.9967
Plack::Response = 0.9967
[TestRelease]
[PkgVersion]
+[PruneFiles]
+match = ^extensions
[MetaNoIndex]
directory = t/lib
@@ -3,7 +3,9 @@ use warnings;
package DvdDatabase::Controller;
-use base 'WebNano::DirController';
+use base 'WebNano::Controller';
+
+sub search_subcontrollers { 1 }
sub index_action {
my $self = shift;
@@ -16,7 +16,9 @@ sub record_action {
my( $self, $id, $method, @args ) = @_;
my $rs = $self->app->schema->resultset( 'Dvd' );
my $record = $rs->find( $id );
- return $self->local_dispatch( $method, $record, @args );
+ my @path = @{ $self->path };
+ $self->path( [ $method, $record, @path[ 2 .. $#path ] ] );
+ return $self->local_dispatch();
}
sub index_action {
@@ -7,7 +7,8 @@ extends 'WebNano::Controller';
use DvdDatabase::Controller::Dvd::Form;
around 'local_dispatch' => sub {
- my( $orig, $self, $path, $id, $method, @args ) = @_;
+ my( $orig, $self, ) = @_;
+ my( $path, $id, $method, @args ) = @{ $self->path };
if( defined $path && $path eq 'record' ){
my $rs = $self->app->schema->resultset( 'Dvd' );
my $record = $rs->find( $id );
@@ -17,16 +18,16 @@ around 'local_dispatch' => sub {
$res->body( 'No record with id: ' . $id );
return $res;
}
- unshift @args, $record;
- return $self->$orig( $method, @args );
+ $self->path( [ $method, $record, @args ] );
+ return $self->$orig();
}
if( defined $path && $path =~ m{^(view|edit|delete)} ){
my $res = $self->req->new_response(404);
$res->content_type('text/plain');
$res->body( 'No page found' );
return $res;
}
- return $self->$orig( $path, $id, $method, @args );
+ return $self->$orig();
};
sub index_action {
@@ -89,4 +90,3 @@ sub edit_action {
}
1;
-
@@ -13,7 +13,8 @@ has record_methods => (
);
around 'local_dispatch' => sub {
- my( $orig, $self, $id, $method, @args ) = @_;
+ my( $orig, $self ) = @_;
+ my( $id, $method, @args ) = @{ $self->path };
$method ||= 'view';
if( $id && $id =~ /^\d+$/ && $self->record_methods->{ $method } ){
my $rs = $self->app->schema->resultset( 'Dvd' );
@@ -26,7 +27,7 @@ around 'local_dispatch' => sub {
}
return $self->$method( $record, @args );
}
- return $self->$orig( $id, $method, @args );
+ return $self->$orig();
};
sub index_action {
@@ -12,7 +12,7 @@ use DvdDatabase::Controller::Dvd::Record;
sub handle {
my ( $class, %args ) = @_;
- my @path = @{ delete $args{path} };
+ my @path = @{ $args{path} };
my $id = $path[0];
if( $id && $id =~ /^\d+$/ ){
my $rs = $args{app}->schema->resultset( 'Dvd' );
@@ -31,7 +31,7 @@ sub handle {
);
}
my $self = $class->new( %args );
- return $self->local_dispatch( @path );
+ return $self->local_dispatch();
};
sub index_action {
@@ -12,7 +12,8 @@ use DvdDatabase::Controller::Dvd::Record;
around 'local_dispatch' => sub {
- my( $orig, $self, $id, @args ) = @_;
+ my( $orig, $self ) = @_;
+ my( $id, @args ) = @{ $self->path };
if( $id && $id =~ /^\d+$/ ){
my $rs = $self->app->schema->resultset( 'Dvd' );
my $record = $rs->find( $id );
@@ -30,7 +31,7 @@ around 'local_dispatch' => sub {
record => $record,
);
}
- return $self->$orig( $id, @args );
+ return $self->$orig();
};
@@ -10,6 +10,7 @@ use Test::WWW::Mechanize::PSGI;
use DvdDatabase;
for my $controller( qw/Dvd Dvd1 Dvd2/ ){
+ warn "Testing $controller\n";
copy('t/data/dvdzbr.db','dvdzbr.db') or die "Copy failed: $!";
my $app = DvdDatabase->new()->psgi_app;
@@ -10,6 +10,7 @@ use Plack::Middleware::Session;
use Test::WWW::Mechanize::PSGI;
for my $controller( qw/DvdSimpleUrl_HandlerOv DvdSimpleUrl DvdSimpleUrl_TwoClasses/ ){
+ warn "Testing $controller\n";
copy('t/data/dvdzbr.db','dvdzbr.db') or die "Copy failed: $!";
my $app = Plack::Middleware::Session->wrap( DvdDatabase->new()->psgi_app );
@@ -3,7 +3,7 @@ use warnings;
package MyApp::Controller;
-use base 'WebNano::DirController';
+use base 'WebNano::Controller';
sub index_action {
View
@@ -113,20 +113,21 @@ renderer object (if it is too heavy to be created per request) and general
stuff that is too heavy to be rebuilt with each request. In contrast the
controller objects are recreated for each request.
-The dispatching implemented by WebNano is a simple namespace matching
+The dispatching implemented by L<WebNano::Controller> is a simple namespace matching
of HTTP request paths into method calls as in the following examples:
'/page' -> 'MyApp::Controller->page_action()'
'/Some/Very/long/pa/th' -> 'MyApp::Controller::Some::Very->long_action( 'pa', 'th' )
-The first type of dispatching is done by the plain L<WebNano::Controller> - to get actions
-dispatched to controllers in subdirs you need to subclass L<WebNano::DirController>
-(which is also a subclass of C<WebNano::Controller>).
-Your root controllers should usually start with C<use base 'WebNano::DirController'>.
-Other controllers also can subclass C<WebNano::DirController> - but only if they
+The first type of dispatching is always available - to get actions
+dispatched to controllers in subdirs you need to override the C<search_subcontrollers>
+method and make it return a true value.
+Your root controllers should usually have C<sub search_subcontrollers { 1 }>.
+Other controllers also can do that - but only if they
do not do their own dispatching to sub-controllers. If a controller has custom
-dispatching then you should use C<WebNano::Controller> to avoid intruducing possible
-security risks from the automatic dispatching which could bypass your controller's logic.
+dispatching then you should leave the default C<search_subcontrollers> to avoid
+intruducing possible security risks from the automatic dispatching which could
+bypass your controller's logic.
Additionally if the last part of the path is empty then C<index> is added to it - so C</> is
mapped to C<index_action> and C</SomeController/> is mapped to
@@ -6,7 +6,8 @@ package WebNano::Controller;
use URI::Escape 'uri_unescape';
use Plack::Request;
-use Object::Tiny::RW qw/ app env self_url url_map _req /;
+use WebNano::FindController 'find_nested';
+use Object::Tiny::RW qw/ app env self_url url_map _req path /;
sub DEBUG { shift->app->DEBUG }
@@ -26,7 +27,8 @@ sub render {
}
sub local_dispatch {
- my ( $self, @parts ) = @_;
+ my ( $self ) = @_;
+ my @parts = @{ $self->path };
my $name = uri_unescape( shift @parts );
$name = 'index' if !defined( $name ) || !length( $name );
my $action;
@@ -48,12 +50,44 @@ sub local_dispatch {
return $out;
}
+
+sub _self_path{
+ my $class = shift;
+ my $path = $class;
+ $path =~ s/.*::Controller(?=(::|$))//;
+ $path =~ s{::}{/};
+ return $path . '/';
+}
+
+sub dispatch_to_class {
+ my ( $self, $to ) = @_;
+ $to =~ s/::|'//g if defined( $to );
+ return if !length( $to );
+ my $class = ref $self;
+ my $controller_class = find_nested( $class->_self_path . $to, $self->app->controller_search_path );
+ if( !$controller_class ){
+ warn qq{No subcontroller found in "$class" for "} . $class->_self_path . $to. qq{"\n} if $self->DEBUG;
+ return;
+ }
+ warn qq{Dispatching to "$controller_class"\n} if $self->DEBUG;
+ return $controller_class->handle(
+ path => $self->path,
+ app => $self->app,
+ self_url => $self->{self_url} . $to. '/',
+ env => $self->env,
+ );
+}
+
sub handle {
my ( $class, %args ) = @_;
- my $path = delete $args{path};
my $self = $class->new( %args );
- return $self->local_dispatch( @$path );
-};
+ my $out = $self->local_dispatch();
+ return $out if defined( $out ) || !$self->search_subcontrollers;
+ my $path_part = shift @{ $self->path };
+ return $self->dispatch_to_class( $path_part );
+}
+
+sub search_subcontrollers { 0 }
1;
@@ -91,6 +125,16 @@ to appropriate action method or to a next controller.
The action method should return a string containing the HTML page,
a Plack::Response object or a code ref.
+If there is no suitable method in the current class and the method search_subcontrollers
+returns a true value then child controller classes
+are tried out. If there is found one that matches the path part then it is
+instantiated with the current psgi env and it's handle method is called.
+
+In a path C</SomeDeepController/OtherController/LeaveController/method> all
+C<MyApp::Controoler>, C<MyApp::Controller::SomeDeepController>
+and C<MyApp::Controller::SomeDeepController::OtherController> need to
+override search_subcontrollers method to return 1.
+
=head1 METHODS
=head2 handle
@@ -115,6 +159,13 @@ Plack::Reqest made from env
=head2 template_search_path
+=head2 search_subcontrollers
+
+If search_subcontrollers returns true and there are no local actions
+then subcontrollers are searched.
+
+=head2 dispatch_to_class
+
=head2 DEBUG
By default returns the DEBUG flag from the application. When this returns C<true> then
@@ -136,4 +187,5 @@ L<PSGI environment|http://search.cpan.org/~miyagawa/PSGI/PSGI.pod#The_Environmen
=head2 self_url
+=head2 path
@@ -1,96 +0,0 @@
-use strict;
-use warnings;
-
-package WebNano::DirController;
-use WebNano::FindController 'find_nested';
-use base 'WebNano::Controller';
-
-sub _self_path{
- my $class = shift;
- my $path = $class;
- $path =~ s/.*::Controller(?=(::|$))//;
- $path =~ s{::}{/};
- return $path . '/';
-}
-
-sub dispatch_to_class {
- my ( $self, $to, $args ) = @_;
- $to =~ s/::|'//g if defined( $to );
- return if !length( $to );
- my $class = ref $self;
- my $controller_class = find_nested( $class->_self_path . $to, $args->{app}->controller_search_path );
- if( !$controller_class ){
- warn qq{No subcontroller found in "$class" for "} . $class->_self_path . $to. qq{"\n} if $self->DEBUG;
- return;
- }
- warn qq{Dispatching to "$controller_class"\n} if $self->DEBUG;
- return $controller_class->handle(
- %{ $args },
- self_url => $args->{self_url} . $to. '/',
- );
-}
-
-sub handle {
- my ( $class, %args ) = @_;
- my $path = $args{path};
- my $self = $class->new( %args );
- my $out = $self->local_dispatch( @$path );
- return $out if defined( $out );
- my $path_part = shift @$path;
- return $self->dispatch_to_class( $path_part, \%args );
-}
-
-
-1;
-
-
-
-=pod
-
-=head1 NAME
-
-WebNano::DirController - WebNano controller class for root
-
-=head1 VERSION
-
-version 0.002
-
-=head1 SYNOPSIS
-
- use base WebNano::DirController;
-
-=head1 DESCRIPTION
-
-This is the WebNano pass through base controller - used for root controllers
-and all other controllers that have sub-controllers.
-
-In a path C</SomeDeepController/OtherController/LeaveController/method> all
-C<MyApp::Controoler>, C<MyApp::Controller::SomeDeepController>
-and C<MyApp::Controller::SomeDeepController::OtherController> need to be DirControllers.
-
-If there is no suitable method in the current class, child controller classes
-are tried out. If there is found one that matches the path part then it is
-instantiated with the current psgi env and it's handle method is called.
-
-=head1 METHODS
-
-=head2 handle
-
-=head1 AUTHOR
-
-Zbigniew Lukasiak <zby@cpan.org>
-
-=head1 COPYRIGHT AND LICENSE
-
-This software is copyright (c) 2010 by Zbigniew Lukasiak <zby@cpan.org>.
-
-This is free software; you can redistribute it and/or modify it under
-the same terms as the Perl 5 programming language system itself.
-
-=cut
-
-
-__END__
-
-# ABSTRACT: WebNano controller class for root
-
Oops, something went wrong.

0 comments on commit 85a9c83

Please sign in to comment.