The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

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.

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()

Most important variables are retrieved from and stored in the input set. Calling moniker() will get or set the value of the moniker intput parameter, for example.

So this method tries to populate those parameter slots *if they're not already filled*.

I've recently separated out the ways it tries to do that, so that they can be selectively overridden. The methods that do the work are:

find_moniker()

Looks for input in the form moniker=id or moniker=all, by scanning the input parameters for monikers that the factory recognises.

translate_path_info()

Then it will examine the path information, looking for /moniker/id or /moniker/all. If it finds neither, but there is path info, we assume that the path info is a view name. This allows urls like /browse/welcome.html to pick out views.

Any suffix is removed from the path info, so you can put arbitrary .somethings on the end of your urls if you like. The removed suffix can be retrieved by calling handler->suffix if you need it.

adjust_input( )

Placeholder for a sub that will change input values immediately after read_input has populated them.

This version just turns type parameters into moniker parameters to paper over an old syntax change (when monikers were first introduced to cdbi).

adjust_input() differs from find_moniker() and translate_path_info() in that it is meant to revise the input set once it has been discovered, rather than helping with its discovery. It shouldn't care how the parameter was supplied.

view( view_name )

Most applications based on CDF will have some sort of view mechanism that will determine what template is used to display the objects or lists requested. This method just retrieves the view parameter and calls check_view.

None of this (simple) machinery is invoked unless a template asks for a view, so you can ignore it if it's not relevant to your application.

viewdir( view_name )

Returns the name of the directory in which we should look for view templates. Sometimes a subclass likes to change this, usually just for filing reasons.

check_view( view_name )

This is a simple method that checks the supplied view parameter against a list of permitted views (via the permitted_view configuration parameter). It returns true if the view is permitted, false if not. It is very likely that you will override this method in subclass to add session-based permissions.

default_view( view_name )

Returns the name of the view we should default to if no other instruction is found. This is a relatively low-key instructions that will not be followed until aother avenues have been exhausted: it's normally used by templates looking for something to do. Defaults to the 'default_view' configuration parameter then 'welcome'.

Note that this often means that the usual view-permission check is not applied to the default view.

prefer_view( view_name )

Returns the name of a view that should be imposed if no view parameter is found in input. This is useful for handler subclasses that perform a narrower range of functions and want to preempt a view-choice mechanism that would otherwise be invoked. Does nothing here.

The preferred view will be subject to the usual permission checks.

moniker( $moniker )

Looks for a moniker 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 moniker, 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.

check_moniker( $moniker )

Checks that the supplied moniker is among those managed by the local factory. Subclasses will hopefully have stricter criteria for who can see what.

id( int )

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

thing( data_object )

If both moniker and id parameters are supplied, this method will retrieve and return the corresponding object (provided, of course, that the moniker 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.

check_thing( data_object )

Just a placeholder: checks object-type visibility.

Without a session mechanism we can't control access to individual objects, but subclasses will want to, so this method is invoked as part of retrieving the foreground object (ie in $self->thing) and an AUTH_REQUIRED exception is thrown unless it returns true.

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 if no moniker 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:

 ?moniker=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.

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.

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 replace these with something less crappy and 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 the object already held as $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, which will create it if it doesn't exist 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.

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.

config()

Returns the configuration object which is controlling the local factory. The first time this is called in each request, it will call refresh() on the configuration object, which will cause configuration files to be re-read if they have changed.

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.

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.

REPORTING

debug( $importance, @messages )

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.

log( $importance )

Unlike the factory's debugging methods, these are intended to hold and return messages for the user. Whatever you send to log is pushed onto the log...

report()

...ready to be read back out again when you call report. In scalar it returns the latest item, in list the whole lot in ascending date order.

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. If called in scalar mode, returns only the first value.

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');

Under mod_perl2 all this has to do is set the content type, which it does by calling:

mime_type( $type )

Simple mutator that will get or set the mime-type for this response, or can be subclassed to make some other decision altogether. Defaults to:

default_mime_type()

Returns the mime type that will be used if no other is specified. The default default is text/html: set a default_mime_type parameter or subclass the method to do something more complicated.

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). The cookie is baked immediately (unlike mp1 versions of CDF).

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.