]> git.decadent.org.uk Git - maypole.git/blobdiff - lib/Maypole.pm
Wrote up Changes. Added wishlist.txt - start of a list of tasks for 2.11, 2.12, 3.0
[maypole.git] / lib / Maypole.pm
index 71e01615ed859d55a662fccb0dbb75cfce3c69ff..b84360cc0262e9e4e77dc6ebf41b05f8d876fccc 100644 (file)
@@ -22,7 +22,45 @@ Maypole - MVC web application framework
 
 =head1 SYNOPSIS
 
-See L<Maypole::Application>.
+The canonical example used in the Maypole documentation is the beer database:
+
+    package BeerDB;
+    use strict;
+    use warnings; 
+    
+    # choose a frontend, initialise the config object, and load a plugin
+    use Maypole::Application qw/Relationship/;
+    
+    # get the empty config object created by Maypole::Application
+    my $config = __PACKAGE__->config;
+    
+    # basic settings
+    $config->uri_base("http://localhost/beerdb");
+    $config->template_root("/path/to/templates");
+    $config->rows_per_page(10);
+    $config->display_tables([qw[beer brewery pub style]]);
+
+    # table relationships
+    $config->relationships([
+        "a brewery produces beers",
+        "a style defines beers",
+        "a pub has beers on handpumps",
+        ]);
+        
+    # validation
+    BeerDB::Brewery->untaint_columns( printable => [qw/name notes url/] );
+    BeerDB::Pub->untaint_columns( printable => [qw/name notes url/] );
+    BeerDB::Style->untaint_columns( printable => [qw/name notes/] );
+    BeerDB::Beer->untaint_columns(
+        printable => [qw/abv name price notes/],
+        integer => [qw/style brewery score/],
+        date => [ qw/date/],
+    );
+
+    # set everything up
+    __PACKAGE__->setup("dbi:SQLite:t/beerdb.db");
+
+    1;    
 
 =head1 DESCRIPTION
 
@@ -38,8 +76,8 @@ application. This is the C<BeerDB> package used as an example in the manual.
 
 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>. Then, the driver calls C<setup>.  This sets up the model classes and
-configures your application. The default model class for Maypole uses
+C<CGI::Maypole>. Then, the driver calls C<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.)
 
@@ -168,10 +206,47 @@ __PACKAGE__->config( Maypole::Config->new() );
 
 __PACKAGE__->init_done(0);
 
+=head1 HOOKABLE METHODS
+
+As a framework, Maypole provides a number of B<hooks> - methods that are
+intended to be overridden. Some of these methods come with useful default
+behaviour, others do nothing by default. Likely hooks include:
+
+    Class methods
+    -------------
+    debug 
+    setup 
+    setup_model 
+    load_model_subclass
+    init
+    
+    Instance methods
+    ----------------
+    start_request_hook
+    is_model_applicable
+    get_session
+    authenticate
+    exception
+    additional_data
+    preprocess_path
+
 =head1 CLASS METHODS
 
 =over 4
 
+=item debug
+
+    sub My::App::debug {1}
+
+Returns the debugging flag. Override this in your application class to
+enable/disable debugging.
+
+You can also set the C<debug> flag via L<Maypole::Application>.
+
+=cut
+
+sub debug { 0 }      
+
 =item config
 
 Returns the L<Maypole::Config> object
@@ -205,38 +280,79 @@ Called by C<setup>. This method builds the Maypole model hierarchy.
 
 A likely target for over-riding, if you need to build a customised model.
 
+This method also ensures any code in custom model classes is loaded, so you
+don't need to load them in the driver.
+
 =cut
 
 sub setup_model 
 {
-    my $calling_class = shift;
+    my $class = shift;
     
-    $calling_class = ref $calling_class if ref $calling_class;
+    $class = ref $class if ref $class;
     
-    my $config = $calling_class->config;
+    my $config = $class->config;
     
     $config->model || $config->model('Maypole::Model::CDBI');
     
     $config->model->require or die sprintf 
         "Couldn't load the model class %s: %s", $config->model, $@;
     
-    $config->model->setup_database($config, $calling_class, @_);
+    # among other things, this populates $config->classes
+    $config->model->setup_database($config, $class, @_);
     
     foreach my $subclass ( @{ $config->classes } ) 
     {
         no strict 'refs';
         unshift @{ $subclass . "::ISA" }, $config->model;
-        $config->model->adopt($subclass)
-          if $config->model->can("adopt");
-       
-       # TODO: I think we should also load these classes, in case there is any 
-       # custom code. It would save the developer from needing to put 
-       # lots of use MyApp::SomeTable statements in the driver, and should 
-       # help eliminate some of those annoying silent errors if there's a 
-       # syntax error.
+        
+        # Load custom model code, if it exists - nb this must happen after the 
+        # unshift, to allow code attributes to work, but before adopt(),  
+        # in case adopt() calls overridden methods on $subclass
+        $class->load_model_subclass($subclass);
+        
+        $config->model->adopt($subclass) if $config->model->can("adopt");
+
+#      eval "use $subclass"; 
+#      die "Error loading $subclass: $@"  
+#            if $@ and $@ !~ /Can\'t locate \S+ in \@INC/;
     }
 }
 
+=item load_model_subclass($subclass)
+
+This method is called from C<setup_model()>. It attempts to load the
+C<$subclass> package, if one exists. So if you make a customized C<BeerDB::Beer>
+package, you don't need to explicitly load it. 
+
+If, perhaps during development, you don't want to load up custom classes, you 
+can override this method and load them manually. 
+
+=cut
+
+sub load_model_subclass
+{
+    my ($class, $subclass) = @_;
+    
+    my $config = $class->config;
+    
+    # Load any external files for the model base class or subclasses
+    # (e.g. BeerDB/DBI.pm or BeerDB/Beer.pm) based on code borrowed from
+    # Maypole::Plugin::Loader and Class::DBI.
+    if ( $subclass->require ) 
+    {
+        warn "Loaded external module for '$subclass'\n" if $class->debug > 1;
+    } 
+    else 
+    {
+        (my $filename = $subclass) =~ s!::!/!g;
+        die "Loading '$subclass' failed: $@\n"
+               unless $@ =~ /Can\'t locate \Q$filename\E\.pm/;
+        warn "Did not find external module for '$subclass'\n" 
+            if $class->debug > 1;
+   }
+}
+
 =item init
 
 Loads the view class and instantiates the view object.
@@ -281,30 +397,6 @@ sub new
 
 Get/set the Maypole::View object
 
-=item debug
-
-    sub My::App::debug {1}
-
-Returns the debugging flag. Override this in your application class to
-enable/disable debugging.
-
-You can also set the C<debug> flag via L<Maypole::Application>.
-
-=cut
-
-sub debug { 0 }      
-
-=item get_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">
-
-=cut
-
-sub get_template_root {'.'}
-
 =back
 
 =head1 INSTANCE METHODS
@@ -373,7 +465,7 @@ sub handler_guts
 {
     my ($self) = @_;
     
-    $self->__load_model;
+    $self->__load_request_model;
 
     my $applicable = $self->is_model_applicable;
     
@@ -430,7 +522,7 @@ sub handler_guts
     return $self->__call_process_view;
 }
 
-sub __load_model
+sub __load_request_model
 {
     my ($self) = @_;
     $self->model_class( $self->config->model->class_of($self, $self->table) );
@@ -665,6 +757,7 @@ sub call_exception
     return $self->exception($error);
 }
 
+
 =item exception
 
 This method is called if any exceptions are raised during the authentication or
@@ -697,39 +790,6 @@ sub send_output {
 
 
 
-=item redirect_request
-
-Sets output headers to redirect based on the arguments provided
-
-Accepts either a single argument of the full url to redirect to, or a hash of
-named parameters :
-
-$r->redirect_request('http://www.example.com/path');
-
-or
-
-$r->redirect_request(protocol=>'https', domain=>'www.example.com', path=>'/path/file?arguments', status=>'302', url=>'..');
-
-The named parameters are protocol, domain, path, status and url
-
-Only 1 named parameter is required but other than url, they can be combined as
-required and current values (from the request) will be used in place of any
-missing arguments. The url argument must be a full url including protocol and
-can only be combined with status.
-
-=cut
-
-sub redirect_request {
-  die "redirect_request is a virtual method. Do not use Maypole directly; use Apache::MVC or similar";
-}
-
-=item redirect_internal_request 
-
-=cut
-
-sub redirect_internal_request {
-
-}
 
 =back
 
@@ -759,11 +819,11 @@ sub parse_path
     $self->$_(undef) for qw/action table args/;
     
     $self->preprocess_path;
-
     $self->path || $self->path('frontpage');
-    
+
     my @pi = grep {length} split '/', $self->path;
-    
+
+
     $self->table  || $self->table(shift @pi);
     $self->action || $self->action( shift @pi or 'index' );
     $self->args   || $self->args(\@pi);
@@ -882,6 +942,25 @@ Turns post data and query string paramaters into a hash of C<params>.
 You should only need to define this method if you are writing a new Maypole
 backend.
 
+=cut 
+
+sub parse_args
+{
+    die "parse_args() is a virtual method. Do not use Maypole directly; ".
+            "use Apache::MVC or similar";
+}
+
+=item get_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">
+
+=cut
+
+sub get_template_root {'.'}
+
 =back
 
 =head2 Request properties
@@ -1034,6 +1113,47 @@ will be an array reference.
 
 Alias for C<params>.
 
+=back
+
+=head3 Utility methods
+
+=over 4
+
+=item redirect_request
+
+Sets output headers to redirect based on the arguments provided
+
+Accepts either a single argument of the full url to redirect to, or a hash of
+named parameters :
+
+$r->redirect_request('http://www.example.com/path');
+
+or
+
+$r->redirect_request(protocol=>'https', domain=>'www.example.com', path=>'/path/file?arguments', status=>'302', url=>'..');
+
+The named parameters are protocol, domain, path, status and url
+
+Only 1 named parameter is required but other than url, they can be combined as
+required and current values (from the request) will be used in place of any
+missing arguments. The url argument must be a full url including protocol and
+can only be combined with status.
+
+=cut
+
+sub redirect_request {
+  die "redirect_request is a virtual method. Do not use Maypole directly; use Apache::MVC or similar";
+}
+
+=item redirect_internal_request 
+
+=cut
+
+sub redirect_internal_request {
+
+}
+
+
 =item make_random_id
 
 returns a unique id for this request can be used to prevent or detect repeat
@@ -1049,9 +1169,110 @@ sub make_random_id {
 
 =back
 
+=head1 SEQUENCE DIAGRAMS
+
+See L<Maypole::Manual::Workflow> for a detailed discussion of the sequence of 
+calls during processing of a request. This is a brief summary:
+
+    INITIALIZATION
+                               Model e.g.
+         BeerDB           Maypole::Model::CDBI
+           |                        |
+   setup   |                        |
+ o-------->||                       |
+           || setup_model           |     setup_database() creates
+           ||------+                |      a subclass of the Model
+           |||<----+                |        for each table
+           |||                      |                |
+           |||   setup_database     |                |
+           |||--------------------->|| 'create'      *
+           |||                      ||----------> $subclass
+           |||                      |                  |
+           ||| load_model_subclass  |                  |
+ foreach   |||------+  ($subclass)  |                  |
+ $subclass ||||<----+               |    require       |
+           ||||--------------------------------------->|
+           |||                      |                  |
+           |||   adopt($subclass)   |                  |
+           |||--------------------->||                 |
+           |                        |                  |
+           |                        |                  |
+           |-----+ init             |                  |
+           ||<---+                  |                  |
+           ||                       |     new          |     view_object: e.g
+           ||---------------------------------------------> Maypole::View::TT
+           |                        |                  |          |
+           |                        |                  |          |
+           |                        |                  |          |
+           |                        |                  |          |
+           |                        |                  |          |
+           
+
+
+    HANDLING A REQUEST
+
+
+          BeerDB                                Model  $subclass  view_object
+            |                                      |       |         |
+    handler |                                      |       |         |
+  o-------->| new                                  |       |         |
+            |-----> r:BeerDB                       |       |         |
+            |         |                            |       |         |
+            |         |                            |       |         |
+            |         ||                           |       |         |
+            |         ||-----+ parse_location      |       |         |
+            |         |||<---+                     |       |         |
+            |         ||                           |       |         |
+            |         ||-----+ start_request_hook  |       |         |
+            |         |||<---+                     |       |         |
+            |         ||                           |       |         |
+            |         ||-----+ get_session         |       |         |
+            |         |||<---+                     |       |         |
+            |         ||                           |       |         |
+            |         ||-----+ handler_guts        |       |         |
+            |         |||<---+                     |       |         |
+            |         |||     class_of($table)     |       |         |
+            |         |||------------------------->||      |         |
+            |         |||       $subclass          ||      |         |
+            |         |||<-------------------------||      |         |
+            |         |||                          |       |         |
+            |         |||-----+ is_model_applicable|       |         |
+            |         ||||<---+                    |       |         |
+            |         |||                          |       |         |
+            |         |||-----+ call_authenticate  |       |         |
+            |         ||||<---+                    |       |         |
+            |         |||                          |       |         |
+            |         |||-----+ additional_data    |       |         |
+            |         ||||<---+                    |       |         |
+            |         |||             process      |       |   fetch_objects
+            |         |||--------------------------------->||-----+  |
+            |         |||                          |       |||<---+  |
+            |         |||                          |       ||        |
+            |         |||                          |       ||   $action
+            |         |||                          |       ||-----+  |
+            |         |||                          |       |||<---+  |
+            |         |||                          |       |         |
+            |         |||         process          |       |         |
+            |         |||------------------------------------------->|| template
+            |         |||                          |       |         ||-----+
+            |         |||                          |       |         |||<---+
+            |         |||                          |       |         |
+            |         ||     send_output           |       |         |
+            |         ||-----+                     |       |         |
+            |         |||<---+                     |       |         |
+   $status  |         ||                           |       |         |
+   <------------------||                           |       |         |
+            |         |                            |       |         |
+            |         X                            |       |         |           
+            |                                      |       |         |
+            |                                      |       |         |
+            |                                      |       |         |
+           
+           
+
 =head1 SEE ALSO
 
-There's more documentation, examples, and information on our mailing lists
+There's more documentation, examples, and information on our mailing lists
 at the Maypole web site:
 
 L<http://maypole.perl.org/>
@@ -1060,12 +1281,15 @@ L<Maypole::Application>, L<Apache::MVC>, L<CGI::Maypole>.
 
 =head1 AUTHOR
 
-Maypole is currently maintained by Aaron Trevena
+Maypole is currently maintained by Aaron Trevena, David Baird, Dave Howorth and
+Peter Speltz.
 
 =head1 AUTHOR EMERITUS
 
 Simon Cozens, C<simon#cpan.org>
 
+Simon Flack maintained Maypole from 2.05 to 2.09
+
 Sebastian Riedel, C<sri#oook.de> maintained Maypole from 1.99_01 to 2.04
 
 =head1 THANKS TO