=head1 NAME
-Apache::MVC - Web front end to a data source
+Apache::MVC - Apache front-end to Maypole
=head1 SYNOPSIS
package BeerDB;
use base 'Apache::MVC';
- sub handler { Apache::MVC::handler("BeerDB", @_) }
- BeerDB->set_database("dbi:mysql:beerdb");
+ BeerDB->setup("dbi:mysql:beerdb");
BeerDB->config->{uri_base} = "http://your.site/";
BeerDB->config->{display_tables} = [qw[beer brewery pub style]];
# Now set up your database:
=head1 DESCRIPTION
-A large number of web programming tasks follow the same sort of pattern:
-we have some data in a datasource, typically a relational database. We
-have a bunch of templates provided by web designers. We have a number of
-things we want to be able to do with the database - create, add, edit,
-delete records, view records, run searches, and so on. We have a web
-server which provides input from the user about what to do. Something in
-the middle takes the input, grabs the relevant rows from the database,
-performs the action, constructs a page, and spits it out.
-
-This module aims to be the most generic and extensible "something in the
-middle".
-
-An example would help explain this best. You need to add a product
-catalogue to a company's web site. Users need to list the products in
-various categories, view a page on each product with its photo and
-pricing information and so on, and there needs to be a back-end where
-sales staff can add new lines, change prices, and delete out of date
-records. So, you set up the database, provide some default templates
-for the designers to customize, and then write an Apache handler like
-this:
-
- package ProductDatabase;
- use base 'Apache::MVC';
- __PACKAGE__->set_database("dbi:mysql:products");
- BeerDB->config->{uri_base} = "http://your.site/catalogue/";
- ProductDatabase::Product->has_a("category" => ProductDatabase::Category);
- # ...
-
- sub authenticate {
- my ($self, $request) = @_;
- return OK if $request->{ar}->get_remote_host() eq "sales.yourcorp.com";
- return OK if $request->{action} =~ /^(view|list)$/;
- return DECLINED;
- }
- 1;
+Maypole is a Perl web application framework to Java's struts. It is
+essentially completely abstracted, and so doesn't know anything about
+how to talk to the outside world. C<Apache::MVC> is a mod_perl based
+subclass of Maypole.
-You then put the following in your Apache config:
+To use it, you need to create a package which represents your entire
+application. In our example above, this is the C<BeerDB> package.
- <Location /catalogue>
- SetHandler perl-script
- PerlHandler ProductDatabase
- </Location>
+This needs to first inherit from C<Apache::MVC>, and then call setup.
+This will give your package an Apache-compatible C<handler> subroutine,
+and then pass any parameters onto the C<setup_database> method of the
+model class. The default model class for Maypole uses L<Class::DBI> to
+map a database to classes, but this can be changed by messing with the
+configuration. (B<Before> calling setup.)
-And copy the templates found in F<templates/factory> into the
-F<catalogue/factory> directory off the web root. When the designers get
-back to you with custom templates, they are to go in
-F<catalogue/custom>. If you need to do override templates on a
-database-table-by-table basis, put the new template in
-F<catalogue/I<table>>.
+Next, you should configure your application through the C<config>
+method. Configuration parameters at present are:
-This will automatically give you C<add>, C<edit>, C<list>, C<view> and
-C<delete> commands; for instance, a product list, go to
+=over
- http://your.site/catalogue/product/list
+=item uri_base
-For a full example, see the included "beer database" application.
+You B<must> specify this; it is the base URI of the application, which
+will be used to construct links.
-=head1 HOW IT WORKS
+=item display_tables
-There's some documentation for the workflow in L<Apache::MVC::Workflow>,
-but the basic idea is that a URL part like C<product/list> gets
-translated into a call to C<ProductDatabase::Product-E<gt>list>. This
-propagates the request with a set of objects from the database, and then
-calls the C<list> template; first, a C<product/list> template if it
-exists, then the C<custom/list> and finally C<factory/list>.
+If you do not want all of the tables in the database to be accessible,
+then set this to a list of only the ones you want to display
-If there's another action you want the system to do, you need to either
-subclass the model class, and configure your class slightly differently:
+=item rows_per_page
- package ProductDatabase::Model;
- use base 'Apache::MVC::Model::CDBI';
+List output is paged if you set this to a positive number of rows.
- sub supersearch :Exported {
- my ($self, $request) = @_;
- # Do stuff, get a bunch of objects back
- $r->objects(\@objects);
- $r->template("template_name");
- }
+=back
- ProductDatabase->config->{model_class} = "ProductDatabase::Model";
+You should also set up relationships between your classes, such that,
+for instance, calling C<brewery> on a C<BeerDB::Beer> object returns an
+object representing its associated brewery.
+
+For a full example, see the included "beer database" application.
-(The C<:Exported> attribute means that the method can be called via the
-URL C</I<table>/supersearch/...>.)
+=head1 INSTALLATION
-Alternatively, you can put the method directly into the specific model
-class for the table:
+Create a driver module like the one above.
+
+Put the following in your Apache config:
+
+ <Location /beer>
+ SetHandler perl-script
+ PerlHandler BeerDB
+ </Location>
+
+Copy the templates found in F<templates/factory> into the
+F<beer/factory> directory off the web root. When the designers get
+back to you with custom templates, they are to go in
+F<beer/custom>. If you need to do override templates on a
+database-table-by-table basis, put the new template in
+F<beer/I<table>>.
+
+This will automatically give you C<add>, C<edit>, C<list>, C<view> and
+C<delete> commands; for instance, a list of breweries, go to
- sub ProductDatabase::Product::supersearch :Exported { ... }
+ http://your.site/beer/brewery/list
-By default, the view class uses Template Toolkit as the template
-processor, and the model class uses C<Class::DBI>; it may help you to be
-familiar with these modules before going much further with this,
-although I expect there to be other subclasses for other templating
-systems and database abstraction layers as time goes on. The article at
-C<http://www.perl.com/pub/a/2003/07/15/nocode.html> is a great
-introduction to the process we're trying to automate.
+For more information about how the system works and how to extend it,
+see L<Maypole>.
=head1 AUTHOR
__PACKAGE__->init_done(0);
-sub set_database {
+sub setup {
my $calling_class = shift;
$calling_class = ref $calling_class if ref $calling_class;
+ {
+ no strict 'refs';
+ # Naughty.
+ *{$calling_class."::handler"} = sub { Maypole::handler($calling_class, @_) };
+ }
my $config = $calling_class->config;
$config->{model} ||= "Maypole::Model::CDBI";
$config->{model}->require;
return $r->view_object->process($r);
}
-sub get_request {
- my $self = shift;
- require Apache; require Apache::Request;
- $self->{ar} = Apache::Request->new(Apache->request);
-}
-
-sub parse_location {
- my $self = shift;
- $self->{path} = $self->{ar}->uri;
- my $loc = $self->{ar}->location;
- $self->{path} =~ s/^$loc//; # I shouldn't need to do this?
- $self->{path} ||= "frontpage";
- my @pi = split /\//, $self->{path};
- shift @pi while @pi and !$pi[0];
- $self->{table} = shift @pi;
- $self->{action} = shift @pi;
- $self->{args} = \@pi;
-
- $self->{params} = { $self->{ar}->content };
- $self->{query} = { $self->{ar}->args };
-}
-
sub is_applicable {
my $self = shift;
my $config = $self->config;
sub authenticate { return OK }
-1;
-
=head1 NAME
Maypole - MVC web application framework
C<http://www.perl.com/pub/a/2003/07/15/nocode.html> is a great
introduction to the process we're trying to automate.
+=head1 USING MAYPOLE
+
+You should probably not use Maypole directly. Maypole is an abstract
+class which does not specify how to communicate with the outside world.
+The most popular subclass of Maypole is L<Apache::MVC>, which interfaces
+the Maypole framework to Apache mod_perl.
+
+If you are implementing Maypole subclasses, you need to provide at least
+the C<get_request> and C<parse_location> methods. See the
+L<Maypole::Workflow> documentation for what these are expected to do.
+
+=cut
+
+sub get_request { die "Do not use Maypole directly; use Apache::MVC or similar" }
+sub parse_location { die "Do not use Maypole directly; use Apache::MVC or similar" }
+
=head1 AUTHOR
Simon Cozens, C<simon@cpan.org>
=head1 LICENSE
You may distribute this code under the same terms as Perl itself.
+
+=cut
+
+1;
+
=head1 NAME
-Apache::MVC::Workflow - Describes the progress of a request through Apache::MVC
+Maypole::Workflow - Describes the progress of a request through Maypole
=head1 SYNOPSIS
config $h
|
- Apache::MVC $r
+ Maypole $r
Apache::Request |
+---- $r->get_request ---+
$ar |
=head1 DESCRIPTION
-An application based on C<Apache::MVC> will provide an Apache handler,
+An application based on C<Maypole> will provide an Apache handler,
and eventually deliver a page. This document explains how that happens,
and how to influence it. We'll use the C<BeerDB> project as our example.
=head2 Is this an applicable URL?
Next, the C<is_applicable> method works out if this is actually
-something that C<Apache::MVC> should care about - whether the class
+something that C<Maypole> should care about - whether the class
exists in the application, whether it supports the given action, and so
on. The action is "supported" if it exists in the model class (or its
ancestors) and is marked with the C<:Exported> attribute; this stops web
specific template appropriate to the class. Next, it looks at
C</custom/foo>, a local modification, before looking for
C</factory/foo>, one of the default templates that came with
-C<Apache::MVC>.
+C<Maypole>.
=head2 Default template arguments
=item request
-The whole C<Apache::MVC> request object, for people getting really dirty
+The whole C<Maypole> request object, for people getting really dirty
with the templates.
=item objects