NAME

Class::DBI::Factory::Handler - a handler base class for Class::DBI::Factory applications

SYNOPSIS

in Apache configuration somewhere:

<Location "/handler/path">
  SetHandler perl-script
  PerlHandler Handler::Subclass
</Location>

and:

Package Handler::Subclass;
use base qw( Class::DBI::Factory::Handler );

sub build_page {
  my $self = shift;
  my $person = $self->factory->retrieve('person', $self->cookie('person'));
  $self->print('hello ' . $person->name);
  $self->print(', sir') if $person->magnificence > 6;
}

But see also the Class::DBI::Factory docs about configuration files and environment variables.

INTRODUCTION

Class::DBI::Factory::Handler (CDFH) is an off-the-peg mod_perl handler designed to function as part of a Class::DBI::Factory application. It can be used as it is, but is much more likely to be subclassed and has been written with that in mind.

It's just a convenience, really, and consists largely of utility methods that deal with cookies, headers, input, output, etc. It is meant to free authors from the dreary bits of input handling and database integration, and let them concentrate on writing application logic.

Note that if you want to subclass the handler module - and you do, you do - then mod_perl must be compiled with support for method handlers.

Authors are expected to subclass build_page(), at least, but you can use the standard version if you like. It creates a very basic bundle of useful objects and passes it to a selected template toolkit template.

(TT is not loaded until CDFH::process() is called, so you're not paying for it unless you use it.)

CONFIGURATION

See the Class::DBI::Factory documentation for information about how to configure a CDF appplication. it goes on at some length. The handler just asks the factory for configuration information, and all you really have to do is make sure that each short-lived handler object gets the right long-lived factory object.

NB. This module's original purpose was to facilitate moves between CGI and mod_perl, but I let all that go because the factory system reached a size that wasn't very CGI-friendly. It's a little slimmer now (but not, you know, slim), and if anyone is interested, it would be easy to reinstate the CGI functionality. These days it's just a base class for mod_perl handlers.

PAGE CONSTRUCTION

The Handler includes some simple methods for directing output to the request handler with or without template processing, and a fairly well-developed skeleton for processing requests and working with cdbi objects. It is all designed to be easy to subclass and extend or replace.

BASIC OUTPUT

print( )

Prints whatever it is given by way of the request handler's print method. Override if you want to, for example, print directly to STDOUT.

Triggers send_header before printing.

process( )

Accepts a (fully specified) template address and output hashref and passes them to the factory's process() method. The resulting html will be printed out via the request handler due to some magic in the template toolkit. If you are overriding process(), you will probably need to include a call to print().

report()

my $messages = $handler->report;
$handler->report('Mission accomplished.');

Any supplied values are assumed to be messages for the user, and pushed onto an array for later. A reference to the array is then returned.

message()

A simple get+set method commonly used in case of exception to pass through the main error message or some other page heading. Can be used in conjunction with report() and/or error() to liven up your day.

error()

my $errors = $handler->error;
$handler->error('No such user.');

Any supplied values are assumed to be error messages. Suggests that debug display the messages (which it will, if debug_level is 1 or more) and returns the accumulated set as an arrayref.

debug()

hands over to factory->debug, which will print messages to STDERR if debug_level is set to a sufficiently high value in the configuration of this site.

PAGE CONSTRUCTION

This is built around the idea of a task sequence: each subclass defines (or inherits) a sequence of events that the request will pass through before the page is returned. Each event in the sequence can throw an exception to halt processing and probably divert to some other view, such as a login screen. The exception types correspond to Apache return codes: OK, REDIRECT and SERVER_ERROR.

This base class includes a simple but sufficient task sequence along with create, update and delete methods that can be invoked in response to input.

build_page()

This is the main control method: it looks up the task sequence, performs each task in turn and catches any exceptions that result.

There are several ways to make use of this. You can use it exactly as it is, to get basic but comprehensive i/o. You can selectively override some of the steps - see below, you can change the list of tasks by overriding task_sequence(), or you can override build_page() to replace the whole mechanism with something more to your taste.

task_sequence()

The default sequence defined here is:

check_permission 
read_input
do_op
return_output

And each step is described below.

check_permission()

This is just a placeholder, and always returns true. It is very likely that your data classes will include a session class, and that you will want to check that suitable session tokens have been presented, but I'm not going to impose a particular way of doing that (because CDF doesn't like to make assumptions about the presence of particular data classes).

read_input()

Placed here as a convenience in case subclasses want to read from or adjust the input set. One common tweak is to read path_info. Any changes you make here should be by way of set_param: if you call type or id directly, for example, later steps may override your changes.

NB. Most key values are retrieved from the input set by the corresponding method (eg calling ->type) will look at the 'type' or 'moniker' parameters if it finds no other value to return.

view( view_name )

Looks for a 'view' parameter in input and calls permitted_view to compare it against the configured list of permitted views.

Can be supplied with a value. Defined but non-true values will be accepted and retained, so to clear the view setting, just call view(0).

Either way, if a view value is present, this method throws a NOT_FOUND exception unless it is allowed.

permitted_view( view_name )

Checks the supplied view name against the list of permitted views (ie the 'permitted_view' configuration parameter). Returns true if it is found there.

type( moniker )

Looks for a moniker or type parameter in input and checks it against the factory's list of monikers. Can also be supplied with a moniker.

Throws a NOT_FOUND exception if the type parameter is supplied but does not correspond to a known data class.

NB. the type, id, op and view parameters are held as request parameters: they are not copied over into the handler's internal hashref. That way we can be sure that all references to the input data return the same results.

id( int )

Looks for an 'id' parameter in input. Can be supplied with a value instead.

thing( data_object )

If both type (aka moniker) and id parameters are supplied, this method will retrieve and return the corresponding object (provided, of course, that the type matches a valid data class and the id an existing object of that class).

You can also supply an existing object.

Returns immediately if the necessary parameters are not supplied. Throws a NOT_FOUND exception if the parameters are supplied but the object cannot be retrieved.

ghost( )

Builds a ghost object (see Class::DBI::Factory::Ghost) out of the input set, which can be used to populate forms, check input values and perform other tests and confirmations before actually committing the data to the database.

Ghost objects have all the same relationships as objects of the class they shadow. So you can call $ghost->person->title as usual.

Returns undef if no type parameter is found: the ghost has to have a class to shadow.

op()

Get or set that, by default, returns the 'op' input parameter.

do_op()

This is a dispatcher: if an 'op' parameter has been supplied, it will check that against the list of permitted operations and then call the class method of the same name.

A query string of the form:

?type=cd&id=4&op=delete

will result in a call to something like Class::DBI::Factory::Handler->delete(), if delete is a permitted operation, which will presumably result in the deletion of the My::CD object with id 4.

permitted_ops()

This should return a dispatch table in the form of a hashref in which the keys are operation names and the values the associated method names (not subrefs). Note that they are handler methods, not object methods.

return_output()

This one deals with the final handover to the template processor, calling assemble_output to supply the values provided to templates and template to get the template file address.

This base class uses the Template Toolkit: override return_output to use some other templating mechanism.

assemble_output()

The variables which will be available to templates are assembled here.

extra_output()

This is called by assemble_output, and the hashref it returns is appended to the set of values passed to templates. By default it returns {}: its purpose here is to allow subclasses to add to the set of template variables rather than having to redo it from scratch.

pager( ignore_id )

If a type parameter has been supplied, and corresponds to a valid data class, this method will return a pager object attached to that class. If there's a page parameter, that will be passed on too.

Normally this method will return undef if an id parameter is also supplied, assuming that an object rather than a pager is required. Supply a true value as the first parameter and this reluctance will be overridden.

list( list_object )

If a type parameter has been supplied, this will return an object of Class::DBI::Factory::List attached to the corresponding data class.

Any other parameters that match columns of the data class will also be passed through, along with any of the list-control flags (sortby, sortorder, startat and step).

As with pager, if there is an id parameter then the list will only be built if you pass a true value to the method.

session( )

This is just a placeholder, and doesn't do or return anything. It is included in the default set, on the assumption that the first thing you do will be to supply a session-handling mechanism: all you have to do is override this session() method.

I'm not going to include anything specific here, becase CDF doesn't like to make any assumptions about the existence of particular data classes.

container_template( )

Returns the full path of the main template that will be used to build the page that is to be displayed. This may actually be the template that displays the object or list you want to return, but it is more commonly a generic container template that controls layout and configuration.

This value is passed to the Template Toolkit along with the bundle of value returned by assemble_output.

page_template( )

Returns the i<name> of the secondary template that will be used to display the list or object that you are returning. This is passed as a page_template variable to the primary template identified by container_template(), where the generic container will use it to pull in the specific template that is required.

By name, incidentally, I mean the filename without its suffix. A directory path will be supplied by template_prefix and a file suffix by template_suffix, if appropriate. This apparent overcomplication allows handlers to choose between html and xml, for example. Note that you will also want to set (or override) mime_type in that case.

template_prefix( )

A simple get-and-set placeholder: subclasses can either dictate the prefix per-request or override the method to direct the template processor to one or other subset of templates by returning a subdirectory address in the usual template form (ie no opening /). A trailing / will be added if necessary.

template_category( )

An older synonym of template_prefix.

default_template_prefix( )

Returns a file suffix, if appropriate. The default value is taken from the configuration parameter 'template_prefix'.

template_suffix( )

Simple get and set: accepts and holds a value, or failing that gets one from default_template_suffix. Prepends a . if necessary.

default_template_suffix( )

Returns a file suffix, if appropriate. The default value is taken from the configuration parameter 'template_suffix'.

BASIC OPERATIONS

This small set of methods provides for the most obvious operations performed on cdbi objects: create, update and delete. Most of the actual work is delegated to factory methods.

A real application will also include non-object related operations like logging in and out, registering and making changes to sets or classes all at once.

store_object()

Uses the input set to create or update an object.

The resulting object is stored in $self->thing.

delete_object()

calls delete() on the foreground object, but first creates a ghost copy and stores it in deleted_object(). The ghost should have all the values and relationships of the original.

USEFUL MACHINERY

factory()

$handler->factory->retrieve_all('artist');

returns the local factory object, or creates one if none exists yet.

factory_class()

returns the full name of the class that should be used to instantiate the factory. Defaults to Class:DBI::Factory, of course: if you subclass the factory class, you must mention the name of the subclass here.

request()

Returns the Apache::Request object that started it all.

config()

Returns the configuration object which is controlling the local factory. This method is included here to let you override configuration mechanisms in subclass, but unless you have per-handler configuration changes, it is probably more sensible to make that sort of change in the factory than here.

tt()

Returns the template object which is being used by the local factory. This method is here to make it easy to override delivery mechanisms in subclass, but this method costs nothing unless used, so if you're using some other templating engine that TT2, you will probably find it more straightforward to replace the process() method.

CONTEXT

url()

Returns the url of this request, properly escaped so that it can be included in an html tag or query string.

qs()

Returns the query string part of the address for this request, properly escaped so that it can be included in an html tag or query string.

full_url()

Returns the full address of this request (ie url?qs)

path_info()

Returns the path information that is appended to the address of this handler. if your handler address is /foo and a request is sent to:

/foo/bar/kettle/black

then the path_info will be /bar/kettle/black. Note that the opening / will cause the first variable in a split(/\/) to be undef.

read_path_info()

Returns a cleaned-up list of values in the path-info string, in the order they appear there.

It is assumed that values will be separated by a forward slash and that any file-type suffix can be ignored. This allows search-engine (and human) friendly urls.

path_suffix()

Returns the file-type suffix that was appended to the path info, if any. It's a useful place to put information about the format in which we should be returning data.

referer()

returns the full referring address. Misspelling preserved for the sake of tradition.

headers_in()

If a name is supplied, returns the value of that input header. Otherwise returns the set. Nothing clever here: just calls Apache::Request->headers_in().

param()

$session_id = $handler->param('session');

If a name is supplied, returns the value of that input parameter. Acts like CGI.pm in list v scalar.

Note that param() cannot be used to set values: see set_param() for that. Separating them makes it easier to limit the actions available to template authors.

fat_param()

Like param(), except that wherever it can turn a parameter value into an object, it does.

has_param()

$verbose = $handler->has_param('verbose');

Returns true if there is a defined input parameter of the name supplied (ie true for zero, not for undef).

all_param()

%parameters = $handler->all_param;

Returns a hash of (name => value) pairs. If there are several input values for a particular parameter, then value with be an arrayref. Otherwise, just a string.

all_fat_param()

Like all_param(), except that wherever it can turn a parameter value into an object, it does.

set_param()

$handler->set_param( 
   time => scalar time,
) unless $self->param('time');

Sets the named parameter to the supplied value. If no value is supplied, the parameter will be cleared but not unset (ie it will exist but not be defined).

delete_param()

$handler->delete_param('password');

Thoroughly unsets the named parameter.

delete_all_param()

Erases all input by calling delete_param() for all input parameters.

uploads()

my @upload_fields = $handler->uploads();

Returns a list of upload field names, each of which can be passed to:

upload( field_name )

my $filehandle = $handler->upload('imagefile');

Returns a filehandle connected to the relevant upload.

cookies()

my $cookies = $handler->cookies();

Returns the full set of cookies as a hashref.

my $userid = $handler->cookie('my_site_id');

Returns the value of the specified cookie.

HEADERS OUT

send_header()

$handler->send_header();
$handler->send_header('image/gif');

Sends out an http header along with any associated cookies or other optional header fields, then sets a flag to prevent any more headers being sent. If no mime-type is supplied, it will use the default returned by default_mime_type(). print() and process() both call send_header() before output, so you may not need to use this method directly at all.

no_cache()

Returns false by default. If this method is subclassed such that it returns true, then the header sent will include the pragma:no-cache and expiry fields that are used to prevent browser caching.

mime_type( $type )

A simple method that can be used to get or set the mime-type for this response, or subclassed to make some other decision altogether. Defaults to:

header_sent( $type )

Get or set method that returns true if headers have already been sent. This will cause set_cookie and redirect to bail out if they are called too late, as well as preventing duplicate headers from being sent.

default_mime_type()

Returns the mime type that will be used if no other is specified. The default default is text/html: override in subclass if that doesn't suit.

set_cookie( hashref )

$handler->set_cookie({ -name => 'id', -value => $id, -path => '/', -expires => '+100y', });

Adds one or more cookies to the set that will be returned with this page (or picture or whatever it is). Note that the cookie is not actually returned until send_header() or redirect() is called, and that a cookie set after send_header() is called will have no effect except to produce a warning in the log.

redirect( full_url )

$handler->redirect('http://www.spanner.org/cdf/')

Causes apache to return a '302 moved' response redirecting the browser to the specified address. Ignored if headers have already been sent.

Any cookies that have been defined are sent with the redirection, in accordance with doctrine and to facilitate login mechanisms, but I am not wholly convinced that all browsers will stash a cookie sent with a 302.

redirect_to_view( view_name )

$handler->redirect_to_view('login')

This is normally called from an exception handler: the task sequence is stopped and we jump straight to return_output with the view parameter set to whatever value was supplied.

SEE ALSO

Class::DBI Class::DBI::Factory Class::DBI::Factory::Config Class::DBI::Factory::List Class::DBI::Factory::Exception

AUTHOR

William Ross, wross@cpan.org

COPYRIGHT

Copyright 2001-4 William Ross, spanner ltd.

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.