]> git.decadent.org.uk Git - maypole.git/blobdiff - lib/Maypole.pm
Refactor to move out ->{ar} to Apache::MVC.
[maypole.git] / lib / Maypole.pm
index deb207193303e6f3366caed0786f78f8ce83fdbf..d5d932c4816a32c8e78c4650e6042ffc9b04f938 100644 (file)
@@ -2,20 +2,29 @@ package Maypole;
 use base qw(Class::Accessor Class::Data::Inheritable);
 use attributes ();
 use UNIVERSAL::require;
-use Apache::Constants ":common";
 use strict;
 use warnings;
-our $VERSION = "0.2";
+our $VERSION = "1.1";
 __PACKAGE__->mk_classdata($_) for qw( config init_done view_object );
 __PACKAGE__->mk_accessors ( qw( ar params query objects model_class
 args action template ));
 __PACKAGE__->config({});
 __PACKAGE__->init_done(0);
 
+# Ape Apache::Constants interface
+use constant OK => 0;
+use constant DECLINED => -1;
 
-sub set_database {
+sub debug { 0 }
+
+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;
@@ -51,39 +60,27 @@ sub handler {
     my $status = $r->is_applicable;
     if ($status == OK) { 
         $status = $r->call_authenticate;
+        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;
         $r->additional_data();
     
         $r->model_class->process($r);
     } else { 
         # Otherwise, it's just a plain template.
+        $r->call_authenticate; # No harm in it
         delete $r->{model_class};
         $r->{path} =~ s{/}{}; # De-absolutify
         $r->template($r->{path});
     }
-    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 };
+    $status = OK;
+    if (!$r->{output}) { # You might want to do it yourself
+        $status = $r->view_object->process($r);
+    }
+    $r->send_output;
+    return $status;
 }
 
 sub is_applicable {
@@ -91,17 +88,18 @@ sub is_applicable {
     my $config = $self->config;
     $config->{ok_tables} = {map {$_ => 1} @{$config->{display_tables}}};
     warn "We don't have that table ($self->{table})"
-        unless $config->{ok_tables}{$self->{table}};
+        if $self->debug and not $config->{ok_tables}{$self->{table}};
     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})" unless $cv;
+    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";
+    do { warn "$self->{action} not exported" if $self->debug;
     return DECLINED() 
      } unless $self->{method_attribs} =~ /\bExported\b/i;
     return OK();
@@ -110,16 +108,14 @@ sub is_applicable {
 sub call_authenticate {
     my $self = shift;
     return $self->model_class->authenticate($self) if 
-        $self->model_class->can("authenticate");
-    return $self->authenticate();
+        $self->model_class->can("authenticate"); 
+    return $self->authenticate($self); # Interface consistency is a Good Thing
 }
 
 sub additional_data {}
 
 sub authenticate { return OK }
 
-1;
-
 =head1 NAME
 
 Maypole - MVC web application framework
@@ -152,9 +148,9 @@ for the designers to customize, and then write an Apache handler like
 this:
 
     package ProductDatabase;
-    use base 'Maypole';
+    use base 'Apache::MVC';
     __PACKAGE__->set_database("dbi:mysql:products");
-    BeerDB->config->{uri_base} = "http://your.site/catalogue/";
+    ProductDatabase->config->{uri_base} = "http://your.site/catalogue/";
     ProductDatabase::Product->has_a("category" => ProductDatabase::Category); 
     # ...
 
@@ -209,6 +205,9 @@ subclass the model class, and configure your class slightly differently:
         $r->template("template_name");
     }
 
+Then your top-level application package should change the model class:
+(Before calling C<setup>)
+
     ProductDatabase->config->{model_class} = "ProductDatabase::Model";
 
 (The C<:Exported> attribute means that the method can be called via the
@@ -227,6 +226,25 @@ 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.
 
+=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<parse_location> and C<send_output> methods. You may also want to
+provide C<get_request> and C<get_template_root>. See the
+L<Maypole::Workflow> documentation for what these are expected to do.
+
+=cut
+
+sub get_template_root { "." }
+sub get_request { }
+sub parse_location { die "Do not use Maypole directly; use Apache::MVC or similar" }
+sub send_output{ die "Do not use Maypole directly; use Apache::MVC or similar" }
+
 =head1 AUTHOR
 
 Simon Cozens, C<simon@cpan.org>
@@ -234,3 +252,8 @@ Simon Cozens, C<simon@cpan.org>
 =head1 LICENSE
 
 You may distribute this code under the same terms as Perl itself.
+
+=cut
+
+1;
+