use Maypole::Constants;
use Maypole::Headers;
-our $VERSION = '2.09';
+our $VERSION = '2.10';
+
+# proposed privacy conventions:
+# - no leading underscore - public to custom application code and plugins
+# - single leading underscore - private to the main Maypole stack - *not* including plugins
+# - double leading underscore - private to the current package
__PACKAGE__->mk_classdata($_) for qw( config init_done view_object );
__PACKAGE__->mk_accessors(
- qw( ar params query objects model_class template_args output path
+ qw( params query objects model_class template_args output path
args action template error document_encoding content_type table
headers_in headers_out )
);
sub debug { 0 }
-sub setup {
+sub setup
+{
my $calling_class = shift;
+
$calling_class = ref $calling_class if ref $calling_class;
- {
- no strict 'refs';
- no warnings 'redefine';
-
- # Naughty.
- *{ $calling_class . "::handler" } =
- sub { Maypole::handler( $calling_class, @_ ) };
- }
+
my $config = $calling_class->config;
- $config->model || $config->model("Maypole::Model::CDBI");
- $config->model->require;
- die "Couldn't load the model class $config->model: $@" if $@;
- $config->model->setup_database( $config, $calling_class, @_ );
- for my $subclass ( @{ $config->classes } ) {
+
+ $config->model || $config->model('Maypole::Model::CDBI');
+
+ $config->model->require or die
+ "Couldn't load the model class $config->{model}: $@";
+
+ $config->model->setup_database($config, $calling_class, @_);
+
+ foreach my $subclass ( @{ $config->classes } )
+ {
no strict 'refs';
unshift @{ $subclass . "::ISA" }, $config->model;
$config->model->adopt($subclass)
}
}
-sub init {
+sub init
+{
my $class = shift;
my $config = $class->config;
$config->view || $config->view("Maypole::View::TT");
}
-sub handler {
-
+# handler() has a method attribute so that mod_perl will invoke
+# BeerDB->handler() as a method rather than a plain function
+# BeerDB::handler() and so this inherited implementation will be
+# found. See e.g. "Practical mod_perl" by Bekman & Cholet for
+# more information <http://modperlbook.org/html/ch25_01.html>
+sub handler : method
+{
# See Maypole::Workflow before trying to understand this.
- my ( $class, $req ) = @_;
+ my ($class, $req) = @_;
+
$class->init unless $class->init_done;
# Create the request object
template_args => {},
config => $class->config
}, $class;
+
$r->headers_out(Maypole::Headers->new);
+
$r->get_request($req);
+
$r->parse_location();
+
my $status = $r->handler_guts();
+
return $status unless $status == OK;
+
$r->send_output;
+
return $status;
}
# The root of all evil
-sub handler_guts {
- my $r = shift;
- $r->model_class( $r->config->model->class_of( $r, $r->{table} ) );
+sub handler_guts
+{
+ my ($r) = @_;
+
+ $r->__load_model;
my $applicable = $r->is_applicable;
- unless ( $applicable == OK ) {
-
+
+ unless ( $applicable == OK )
+ {
# It's just a plain template
- delete $r->{model_class};
- $r->{path} =~ s{/$}{}; # De-absolutify
- $r->template( $r->{path} );
+ $r->model_class(undef);
+
+ my $path = $r->path;
+ $path =~ s{/$}{}; # De-absolutify
+ $r->path($path);
+
+ $r->template($r->path);
}
# We authenticate every request, needed for proper session management
my $status;
+
eval { $status = $r->call_authenticate };
- if ( my $error = $@ ) {
+
+ if ( my $error = $@ )
+ {
$status = $r->call_exception($error);
- if ( $status != OK ) {
+
+ if ( $status != OK )
+ {
warn "caught authenticate error: $error";
- return $r->debug ? $r->view_object->error( $r, $error ) : ERROR;
+ return $r->debug ? $r->view_object->error($r, $error) : ERROR;
}
}
- if ( $r->debug and $status != OK and $status != DECLINED ) {
+
+ if ( $r->debug and $status != OK and $status != DECLINED )
+ {
$r->view_object->error( $r,
"Got unexpected status $status from calling authentication" );
}
+
return $status unless $status == OK;
# We run additional_data for every request
$r->additional_data;
- if ( $applicable == OK ) {
+
+ if ( $applicable == OK )
+ {
eval { $r->model_class->process($r) };
- if ( my $error = $@ ) {
+
+ if ( my $error = $@ )
+ {
$status = $r->call_exception($error);
- if ( $status != OK ) {
+ if ( $status != OK )
+ {
warn "caught model error: $error";
- return $r->debug ? $r->view_object->error( $r, $error ) : ERROR;
+ return $r->debug ? $r->view_object->error($r, $error) : ERROR;
}
}
}
- if ( !$r->{output} ) { # You might want to do it yourself
+
+ if ( !$r->output )
+ { # You might want to do it yourself
eval { $status = $r->view_object->process($r) };
- if ( my $error = $@ ) {
+
+ if ( my $error = $@ )
+ {
$status = $r->call_exception($error);
- if ( $status != OK ) {
+
+ if ( $status != OK )
+ {
warn "caught view error: $error" if $r->debug;
- return $r->debug ? $r->view_object->error( $r, $error ) : ERROR;
+ return $r->debug ? $r->view_object->error($r, $error) : ERROR;
}
}
+
return $status;
}
- else { return OK; }
+
+ return OK;
+}
+
+sub __load_model
+{
+ my ($r) = @_;
+ $r->model_class( $r->config->model->class_of($r, $r->table) );
}
sub is_applicable {
die "Do not use Maypole directly; use Apache::MVC or similar";
}
+# Session and Repeat Submission Handling
+
+sub make_random_id {
+ use Maypole::Session;
+ return Maypole::Session::generate_unique_id();
+}
+
=head1 NAME
Maypole - MVC web application framework
This documents the Maypole request object. See the L<Maypole::Manual>, for a
detailed guide to using Maypole.
-Maypole is a Perl web application framework to Java's struts. It is
+Maypole is a Perl web application framework similar to Java's struts. It is
essentially completely abstracted, and so doesn't know anything about
how to talk to the outside world.
can handle exceptions the user, or falls back to the default
exception method of your Maypole application.
+=head3 make_random_id
+
+returns a unique id for this request can be used to prevent or detect repeat submissions.
=head3 handler