From 9bbeafd23117d2b489a298cc002a97b133de17bd Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Wed, 11 Feb 2004 15:46:38 +0000 Subject: [PATCH] Lots of documentation. git-svn-id: http://svn.maypole.perl.org/Maypole/trunk@60 48953598-375a-da11-a14b-00016c27c3ee --- lib/Apache/MVC.pm | 136 +++++++++++++++----------------------- lib/BeerDB.pm | 5 +- lib/Maypole.pm | 52 ++++++++------- lib/Maypole/Model/Base.pm | 21 ++++++ lib/Maypole/Workflow.pod | 12 ++-- 5 files changed, 108 insertions(+), 118 deletions(-) diff --git a/lib/Apache/MVC.pm b/lib/Apache/MVC.pm index 5d999be..68f2c0a 100644 --- a/lib/Apache/MVC.pm +++ b/lib/Apache/MVC.pm @@ -30,14 +30,13 @@ sub parse_location { =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: @@ -48,102 +47,73 @@ Apache::MVC - Web front end to a data source =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 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 package. - - SetHandler perl-script - PerlHandler ProductDatabase - +This needs to first inherit from C, and then call setup. +This will give your package an Apache-compatible C subroutine, +and then pass any parameters onto the C method of the +model class. The default model class for Maypole uses L to +map a database to classes, but this can be changed by messing with the +configuration. (B calling setup.) -And copy the templates found in F into the -F directory off the web root. When the designers get -back to you with custom templates, they are to go in -F. If you need to do override templates on a -database-table-by-table basis, put the new template in -F>. +Next, you should configure your application through the C +method. Configuration parameters at present are: -This will automatically give you C, C, C, C and -C 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 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, -but the basic idea is that a URL part like C gets -translated into a call to Clist>. This -propagates the request with a set of objects from the database, and then -calls the C template; first, a C template if it -exists, then the C and finally C. +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 on a C 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/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: + + + SetHandler perl-script + PerlHandler BeerDB + + +Copy the templates found in F into the +F directory off the web root. When the designers get +back to you with custom templates, they are to go in +F. If you need to do override templates on a +database-table-by-table basis, put the new template in +F>. + +This will automatically give you C, C, C, C and +C 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; 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 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. =head1 AUTHOR diff --git a/lib/BeerDB.pm b/lib/BeerDB.pm index fd2c704..b7961bc 100644 --- a/lib/BeerDB.pm +++ b/lib/BeerDB.pm @@ -1,14 +1,11 @@ package BeerDB; use base 'Apache::MVC'; use Class::DBI::Loader::Relationship; -# This line is required if you don't have Apache calling Perl handlers -# as methods. -sub handler { Maypole::handler(__PACKAGE__, @_) } # This is the sample application. Change this to the path to your # database. (or use mysql or something) #BeerDB->set_database("dbi:SQLite:t/beerdb.db"); -BeerDB->set_database("dbi:mysql:beerdb"); +BeerDB->setup("dbi:mysql:beerdb"); # Change this to the root of the web space. #BeerDB->config->{uri_base} = "http://localhost/beerdb/"; diff --git a/lib/Maypole.pm b/lib/Maypole.pm index deb2071..b55f4f8 100644 --- a/lib/Maypole.pm +++ b/lib/Maypole.pm @@ -13,9 +13,14 @@ __PACKAGE__->config({}); __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; @@ -64,28 +69,6 @@ sub handler { 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; @@ -118,8 +101,6 @@ sub additional_data {} sub authenticate { return OK } -1; - =head1 NAME Maypole - MVC web application framework @@ -227,6 +208,22 @@ systems and database abstraction layers as time goes on. The article at C 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, which interfaces +the Maypole framework to Apache mod_perl. + +If you are implementing Maypole subclasses, you need to provide at least +the C and C methods. See the +L 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 @@ -234,3 +231,8 @@ Simon Cozens, C =head1 LICENSE You may distribute this code under the same terms as Perl itself. + +=cut + +1; + diff --git a/lib/Maypole/Model/Base.pm b/lib/Maypole/Model/Base.pm index 0119d49..c4c959f 100644 --- a/lib/Maypole/Model/Base.pm +++ b/lib/Maypole/Model/Base.pm @@ -34,6 +34,25 @@ errors. A hash of errors will be passed to the template. sub do_edit { die "This is an abstract method" } +=head2 setup_database + + $model->setup_database($config, $namespace, @data) + +Uses the user-defined data in C<@data> to specify a database- for +example, by passing in a DSN. The model class should open the database, +and create a class for each table in the database. These classes will +then be Ced. It should also populate C<< $config->{tables} >> and +C<< $config->{classes} >> with the names of the classes and tables +respectively. The classes should be placed under the specified +namespace. For instance, C should be mapped to the class +C. + +=head2 class_of + + $model->class_of($r, $table) + +This maps between a table name and its associated class. + =head2 retrieve This turns an ID into an object of the appropriate class. @@ -71,6 +90,8 @@ similar. =cut +sub class_of { die "This is an abstract method" } +sub setup_database { die "This is an abstract method" } sub list :Exported { die "This is an abstract method" }; =pod diff --git a/lib/Maypole/Workflow.pod b/lib/Maypole/Workflow.pod index 15d87c0..5668b34 100644 --- a/lib/Maypole/Workflow.pod +++ b/lib/Maypole/Workflow.pod @@ -2,13 +2,13 @@ =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 | @@ -29,7 +29,7 @@ Apache::MVC::Workflow - Describes the progress of a request through Apache::MVC =head1 DESCRIPTION -An application based on C will provide an Apache handler, +An application based on C 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 project as our example. @@ -74,7 +74,7 @@ preferred format. =head2 Is this an applicable URL? Next, the C method works out if this is actually -something that C should care about - whether the class +something that C 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 @@ -136,7 +136,7 @@ template. It does this by looking first for C: that is, a specific template appropriate to the class. Next, it looks at C, a local modification, before looking for C, one of the default templates that came with -C. +C. =head2 Default template arguments @@ -147,7 +147,7 @@ default: =item request -The whole C request object, for people getting really dirty +The whole C request object, for people getting really dirty with the templates. =item objects -- 2.39.5