Show Contents Previous Page Next Page
Chapter 4 - Content Handlers Method Handlers
It should come as no surprise that between the Apache distribution and third-party modules, there exist dozens of authentication modules, several directory indexing modules, and a couple of extended server-side include modules. All of these modules contain code that was copied and pasted from each other. In some cases all but a minuscule portion of the module consists of duplicated code.
Code duplication is not bad in and of itself, but it is wasteful of memory resources and, more important, of developers' time. It would be much better if code could be reused rather than duplicated, by using a form of object-oriented subclassing. For the C-language API there's not much hope of this. Vanilla C doesn't provide object-oriented features, while C++ would require both the Apache core and every extension module to adopt the same class hierarchy--and it's a little late in the game for this to happen.
Fortunately, the Perl language does support a simple object-oriented model that doesn't require that everyone buy into the same class hierarchy. This section describes how these object-oriented features can be used by Perl API modules to reuse code instead of duplicating it.
We've already looked at piecing together documents in various ways. Here we will explore an implementation using method handlers. There are two classes involved with this example: My::PageBase and My::Page.
Example 4-26 shows the My::PageBase
class, which provides the base functionality for the family of documents derived
from this class. My::PageBase stitches together a document by calling
four methods: the header() method sends the HTTP headers, the top()
method emits the beginning of an HTML document, including the title, the body()
method emits the main contents of the page, and the bottom() method
adds a common footer. My::PageBase includes generic definitions for
header(), top(), body(), and bottom(), each
of which can be overridden by its subclasses. These are all very simple methods.
See Example 4-26 for the definitions.
The My::PageBase handler() method looks like this:
sub handler ($$) {
my($self, $r) = @_;
unless (ref($self)) {
$self = $self->new;
}
for my $meth (qw(header top body bottom)) {
$self->$meth($r);
}
return OK;
}
The key to using My::PageBase in an object-oriented way is the handler() subroutine's use of the ($$) function prototype. This tells mod_perl that the handler wants two arguments: the static class name or object, followed by the Apache request object that is normally passed to handlers. When the handler is called, it retrieves its class name or object reference and stores it in the lexical variable $self . It checks whether $self is an object reference, and if not, it calls its own new() method to create a new object. It then invokes the header(), top(), body(), and bottom() methods in turn.
The My::PageBase new() method turn the arguments passed to it into a blessed hash in the My::PageBase package. Each key in the hash is an attribute that can be used to construct the page. We do not define any default attributes:
sub new {
my $class = shift;
bless {@_}, $class;
}
We will see later why this method is useful.
As we saw in the section on the Apache::Forward module, method handlers are configured just like any other:
<Location /my>
PerlHandler My::PageBase
SetHandler perl-script
</Location>
However, for clarity's sake, or if you use a handler method named something other than handler(), you can use Perl's standard -> method-calling notation. You will have to load the module first with the PerlModule directive:
PerlModule My::PageBase
<Location /my>
PerlHandler My::PageBase->handler
SetHandler perl-script
</Location>
When My::PageBase is installed in this way and you request URI /my,
you will see the exciting screen shown in Figure 4-12.
Figure 4-12. The generic document produced
by My::PageBase
Naturally, we'll want to add a bit more spice to this page. Because the page is modularized, we can do so one step at a time by subclassing Apache::PageBase's methods. The My::Page class does so by inheriting from the My::PageBase class and simply overriding the body() method.
package My::Page;
# file: My/Page.pm
use strict;
use vars qw(@ISA);
use My::PageBase ();
@ISA = qw(My::PageBase);
sub body {
my($self, $r) = @_;
$r->print(<<END);
<p><img src="/icons/cover.gif" align=CENTER>
This is My homepage</p>
<br clear=all>
END
}
1;
__END__
Then change the configuration to invoke the handler() method via My::Page rather than My::PageBase:
PerlModule My::Page
<Location /my>
PerlHandler My::Page->handler
SetHandler perl-script
</Location>
Things look almost the same, but the body text has changed (Figure 4-13).
Figure 4-13. My::Page overrides the body()
method of My::PageBase, creating a more interesting document. Now we need a better title for our document. We could override the top()
method as we did for body(), but that would involve cutting and pasting
a significant amount of HTML (see Example 4-26).
Instead, we can make use of the object's title attribute, which is
used by the top() method in this way:
my $title = $self->{title} || "untitled document";
So how do we set the title attribute? This is where the My::PageBase new() method comes in. When it is called with a set of attribute=value pairs, it blesses them into a hash reference and returns the new object. To set the title attribute, we just have to call the new() method like this:
use My::Page ();
$My::Homepage = My::Page->new(title => 'My Homepage');
This will create a global scalar variable in the My namespace named $My::Homepage . It's most convenient to do this during server startup--for instance, in the Perl startup file.
Now we just change the configuration section to use the object as the handler rather than the class name:
<Location /my>
PerlHandler $My::Homepage->handler
SetHandler perl-script
</Location>
The object will be retrieved by mod_perl and used to invoke the
handler, which will lead to the creation of the page shown in Figure 4-14.
Figure 4-14. After creating a My::Page object
with a title attribute defined, the page displays a custom title and level 1
header. Example 4-26. Using a Method Handler
for Object-Oriented Programming Techniques
package My::PageBase;
# file: My/PageBase.pm
use strict;
use Apache::Constants qw(:common);
sub new {
my $class = shift;
bless {@_}, $class;
}
sub handler ($$) {
my($self, $r) = @_;
unless (ref($self)) {
$self = $self->new;
}
for my $meth (qw(header top body bottom)) {
$self->$meth($r);
}
return OK;
}
sub header {
my($self, $r) = @_;
$r->content_type($self->{type} || "text/html");
$r->send_http_header;
}
sub top {
my($self, $r) = @_;
my $title = $self->{title} || "untitled document"; $r->print(<<EOF);
<html>
<head>
<title>$title</title>
</head>
<body>
<h1>$title</h1>
<hr>
EOF
}
sub bottom {
my($self, $r) = @_;
my $admin = $r->server->server_admin;
$r->print(<<EOF);
<hr>
<i><a href="mailto:$admin">$admin</a></i>
</body>
</html>
EOF
}
sub body {
my($self, $r) = @_;
$r->print("<p>This is the document body<p>");
}
1;
__END__
This wraps up our discussion of the basic techniques for generating page content, filtering files, and processing user input. The next chapter ventures into the perilous domain of imposing state on the stateless HTTP protocol. You'll learn techniques for setting up user sessions, interacting with databases, and managing long-term relationships with users.
Show Contents Previous Page Next Page Copyright © 1999 by O'Reilly & Associates, Inc. |