package Maypole;
use base qw(Class::Accessor::Fast Class::Data::Inheritable);
-use attributes ();
use UNIVERSAL::require;
use strict;
use warnings;
use Maypole::Config;
-our $VERSION = '2.0';
+use Maypole::Constants;
+use Maypole::Headers;
+
+our $VERSION = '2.10';
+
__PACKAGE__->mk_classdata($_) for qw( config init_done view_object );
__PACKAGE__->mk_accessors(
qw( ar params query objects model_class template_args output path
- args action template error document_encoding content_type table)
+ args action template error document_encoding content_type table
+ headers_in headers_out )
);
__PACKAGE__->config( Maypole::Config->new() );
__PACKAGE__->init_done(0);
-use Maypole::Constants;
sub debug { 0 }
$calling_class = ref $calling_class if ref $calling_class;
{
no strict 'refs';
+ no warnings 'redefine';
# Naughty.
*{ $calling_class . "::handler" } =
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 $@;
+ die "Couldn't load the model class $config->{model}: $@" if $@;
$config->model->setup_database( $config, $calling_class, @_ );
for my $subclass ( @{ $config->classes } ) {
no strict 'refs';
$config->view->require;
die "Couldn't load the view class " . $config->view . ": $@" if $@;
$config->display_tables
- || $config->display_tables( [ $class->config->tables ] );
+ || $config->display_tables( $class->config->tables );
$class->view_object( $class->config->view->new );
$class->init_done(1);
# See Maypole::Workflow before trying to understand this.
my ( $class, $req ) = @_;
$class->init unless $class->init_done;
- my $r = bless { config => $class->config }, $class;
+
+ # Create the request object
+ my $r = bless {
+ template_args => {},
+ config => $class->config
+ }, $class;
+ $r->headers_out(Maypole::Headers->new);
$r->get_request($req);
$r->parse_location();
my $status = $r->handler_guts();
sub handler_guts {
my $r = shift;
$r->model_class( $r->config->model->class_of( $r, $r->{table} ) );
+
my $applicable = $r->is_applicable;
unless ( $applicable == OK ) {
$config->ok_tables || $config->ok_tables( $config->display_tables );
$config->ok_tables( { map { $_ => 1 } @{ $config->ok_tables } } )
if ref $config->ok_tables eq "ARRAY";
- warn "We don't have that table ($self->{table})"
+ warn "We don't have that table ($self->{table}).\n"
+ . "Available tables are: "
+ . join( ",", @{ $config->{display_tables} } )
if $self->debug
- and not $config->ok_tables->{ $self->{table} };
+ and not $config->ok_tables->{ $self->{table} }
+ and $self->{action};
return DECLINED() unless exists $config->ok_tables->{ $self->{table} };
- # Does the action method exist?
- my $cv = $self->model_class->can( $self->{action} );
- warn "We don't have that action ($self->{action})"
- if $self->debug and not $cv;
- return DECLINED() unless $cv;
-
- # Is it exported?
- $self->{method_attribs} = join " ", attributes::get($cv);
- do {
- warn "$self->{action} not exported" if $self->debug;
- return DECLINED();
- } unless $self->{method_attribs} =~ /\bExported\b/i;
+ # Is it public?
+ return DECLINED unless $self->model_class->is_public( $self->{action} );
return OK();
}
sub parse_path {
my $self = shift;
$self->{path} ||= "frontpage";
- my @pi = split /\//, $self->{path};
- shift @pi while @pi and !$pi[0];
+ my @pi = $self->{path} =~ m{([^/]+)/?}g;
$self->{table} = shift @pi;
$self->{action} = shift @pi;
+ $self->{action} ||= "index";
$self->{args} = \@pi;
}
+sub param { # like CGI::param(), but read-only
+ my $r = shift;
+ my ($key) = @_;
+ if (defined $key) {
+ unless (exists $r->{params}{$key}) {
+ return wantarray() ? () : undef;
+ }
+ my $val = $r->{params}{$key};
+ if (wantarray()) {
+ return ref $val ? @$val : $val;
+ } else {
+ return ref $val ? $val->[0] : $val;
+ }
+ } else {
+ return keys %{$r->{params}};
+ }
+}
+
sub get_template_root { "." }
sub get_request { }
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
=head1 DESCRIPTION
-This documents the Maypole request object. For user documentation, see
-L<Maypole::Tutorial>.
+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 similar to Java's struts. It is
+essentially completely abstracted, and so doesn't know anything about
+how to talk to the outside world.
+
+To use it, you need to create a package which represents your entire
+application. In our example above, this is the C<BeerDB> package.
+
+This needs to first use L<Maypole::Application> which will make your package
+inherit from the appropriate platform driver such as C<Apache::MVC> or
+C<CGI::Maypole>, and then call setup. This sets up the model classes and
+configures your application. The default model class for Maypole uses
+L<Class::DBI> to map a database to classes, but this can be changed by altering
+configuration. (B<Before> calling setup.)
=head2 CLASS METHODS
=head3 setup
- My::App->setup();
+ My::App->setup($data_source, $user, $password, \%attr);
- Initialise the maypole application and model classes. Your
- application should
- call this after setting configuration via L<"config">
+Initialise the maypole application and model classes. Your application should
+call this after setting configuration via L<"config">
=head3 init
sub My::App::debug {1}
- Returns the debugging flag. Override this in your application class
- to
- enable/disable debugging.
+Returns the debugging flag. Override this in your application class to
+enable/disable debugging.
=head2 INSTANCE METHODS
have been
removed
+=head3 headers_in
+
+A L<Maypole::Headers> object containing HTTP headers for the request
+
+=head3 headers_out
+
+A L<HTTP::Headers> object that contains HTTP headers for the output
+
=head3 parse_args
Turns post data and query string paramaters into a hash of C<params>.
Maypole
backend.
+=head3 param
+
+An accessor for request parameters. It behaves similarly to CGI::param() for
+accessing CGI parameters.
+
=head3 params
-Returns a hash of request parameters. The source of the parameters may
-vary
-depending on the Maypole backend, but they are usually populated from
-request
+Returns a hash of request parameters. The source of the parameters may vary
+depending on the Maypole backend, but they are usually populated from request
query string and POST data.
B<Note:> Where muliple values of a parameter were supplied, the
=head3 get_template_root
-Implimentation-specific path to template root.
+Implementation-specific path to template root.
You should only need to define this method if you are writing a new
Maypole
backend. Otherwise, see L<Maypole::Config/"template_root">
+=head3 get_request
+
+You should only need to define this method if you are writing a new
+Maypole backend. It should return something that looks like an Apache
+or CGI request object, it defaults to blank.
+
+
=head3 is_applicable
Returns a Maypole::Constant to indicate whether the request is valid.
-The default implimentation checks that C<$r-E<gt>table> is publicly
+The default implementation checks that C<$r-E<gt>table> is publicly
accessible
and that the model class is configured to handle the C<$r-E<gt>action>
authenticated for
the Maypole request.
-The default implimentation returns C<OK>
+The default implementation returns C<OK>
=head3 model_class
$r->template_args->{foo} = 'bar';
- Get/set a hash of template variables.
+Get/set a hash of template variables.
=head3 template
Sends the output and additional headers to the user.
-=head1 SEE ALSO
+=head3 call_authenticate
+
+This method first checks if the relevant model class
+can authenticate the user, or falls back to the default
+authenticate method of your Maypole application.
+
+
+=head3 call_exception
+
+This model is called to catch exceptions, first after authenticate, then after
+processing the model class, and finally to check for exceptions from the view
+class.
-There's more documentation, examples, and a wiki at the Maypole web
-site:
+This method first checks if the relevant model class
+can handle exceptions the user, or falls back to the default
+exception method of your Maypole application.
-http://maypole.perl.org/
+=head3 make_random_id
-L<Maypole::Application>,L<Apache::MVC>, L<CGI::Maypole>.
+returns a unique id for this request can be used to prevent or detect repeat submissions.
-=head1 MAINTAINER
+=head3 handler
-Sebastian Riedel, c<sri@oook.de>
+This method sets up the class if it's not done yet, sets some
+defaults and leaves the dirty work to handler_guts.
+
+=head3 handler_guts
+
+This is the core of maypole. You don't want to know.
+
+=head1 SEE ALSO
+
+There's more documentation, examples, and a information on our mailing lists
+at the Maypole web site:
+
+L<http://maypole.perl.org/>
+
+L<Maypole::Application>, L<Apache::MVC>, L<CGI::Maypole>.
=head1 AUTHOR
-Simon Cozens, C<simon@cpan.org>
+Maypole is currently maintained by Simon Flack C<simonflk#cpan.org>
+
+=head1 AUTHOR EMERITUS
+
+Simon Cozens, C<simon#cpan.org>
+
+Sebastian Riedel, C<sri#oook.de> maintained Maypole from 1.99_01 to 2.04
-=head1 THANK YOU
+=head1 THANKS TO
-Danijel Milicevic, Jesse Scheidlower, Jody Belka, Marcus Ramberg,
-Mickael Joanne, Simon Flack, Veljko Vidovic and all the others who've
-helped.
+Sebastian Riedel, Danijel Milicevic, Dave Slack, Jesse Sheidlower, Jody Belka,
+Marcus Ramberg, Mickael Joanne, Randal Schwartz, Simon Flack, Steve Simms,
+Veljko Vidovic and all the others who've helped.
=head1 LICENSE