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

NAME

Class::DBI::Factory - factory interface to a set of Class::DBI classes, with optional mod_perl application skeleton

SYNOPSIS

  # in a simple script:
  
  my $factory = Class::DBI::Factory->new;
  $factory->set_db({ 
    db_type => 'mysql',
    db_name => 'items',
    db_username => 'me',
    db_password => 'password',
  });
  
  $factory->use_classes(qw(My::Item));
  
  my @columns = $factory->columns('item');
  my $item = $factory->retrieve('item', 1);
  my $iterator = $factory->search('item', year => 1980);

    
  # in an apache host configuration:
  
  PerlSetEnv _CDF_SITE_CONFIG '/home/my_site/conf/cdf.conf'
  <Location "/directory">
    SetHandler perl-script
    PerlHandler Class::DBI::Factory::Handler
  </Location>
  
  # and on a template somewhere:
  
  <p>
  [% FOREACH album IN factory.search('album', 
    'year', input.year,
    'artist', artist,
  ) %][% album.title %]<br>[% END %]
  </p>

INTRODUCTION

If you use Class::DBI::Factory by itself, it's a quick, clean way to work with a set of Class::DBI data classes, providing a simple way to access their data and class methods and removing some of cdbi's limitations: with the factory it's very easy to use the same data classes for different databases at the same time.

If you use Class::DBI::Factory with the set of helper classes that come with it, it's a full framework for MVC-friendly mod_perl-based web applications. The supplied Handler base class makes the factory's simple, consistent interface available on TT2 (or other) templates and hides all the complexity of working under mod_perl. In this context the database-abstraction that the factory offers allows you to use the same application on several sites on the same server, which is where all this began many years ago.

CDF is a simple alternative to application frameworks like Maypole and Catalyst. It doesn't automate the creation of CDBI classes - I prefer to write them - but it handles pretty much everything else and is very easy to extend and override.

EXAMPLES

A minimal script for getting at your data with no config files or other clutter:

  use Class::DBI::Factory;
  my $factory = Class::DBI::Factory->new;
  $factory->set_db({ 
    db_type => 'mysql',
    db_name => 'qanda',
    db_username => 'me',
    db_password => 'password',
  });
  
  $factory->use_classes(qw(My::Answer My::Question));
  my $question = $factory->retrieve('question', 1);

The same thing but with a configuration file:

  # in limited-access config file './answers.conf'
  
  db_type = 'mysql',
  db_name = 'qanda',
  db_username = 'me',
  db_password = 'password',
  class = My::Answer
  class = My::Question
  
  # in your script

  use Class::DBI::Factory;
  my $factory = Class::DBI::Factory->new('./items.conf');
  my $question = $factory->retrieve('question', 1);

A very simple mod_perl application:

  # in your virtualhost configuration

  PerlSetEnv _CDF_CONFIG '/home/conf/cdf.conf'
  PerlSetEnv _CDF_SITE_CONFIG '/home/answers/conf/site.conf'
  
  <Location "/answers">
    SetHandler perl-script
    PerlHandler Class::DBI::Factory::Handler
  </Location>

  # in /home/answers/conf/cdf.conf

  db_type = 'mysql',
  db_name = 'qanda',
  db_username = 'me',
  db_password = 'password',
  class = My::Question
  class = My::Answer
  master_template = 'master.tt2'
  template_path = '/home/answers/templates'
  default_view = 'welcome'
  
  # in /home/answers/templates/master.tt2

  [% PROCESS "views/${view}.tt2" %]

  # in /home/answers/templates/views/welcome.tt2

  [% IF input.question %]
    [% answers = factory.search('answer', 'question', question ) %]
    ...
  [% ELSE %]
    [% questions = factory.search('answer', 'question', question ) %]
    [% FOREACH q IN questions %]
      ...
    [% END %]
  [% END %]

It does get a little more complicated, but CDF comes with a set of five helpers that should make the rest of your job easier too:

Class::DBI::Factory::Config

Wraps around Andy Wardley's AppConfig to provide a simple, friendly configuration mechanism for CDF and the rest of your application. This is very likely to be loaded during even the simplest use of CDF, but you can supply your own configuration mechanism instead.

Class::DBI::Factory::Handler

A fairly comprehensive base class for mod_perl handlers, providing standard ways of retrieving and displaying one or many cdbi objects. It's biased towards the Template Toolkit but you can easily replace that with your preferred templating system. See the Class::DBI::Factory::Handler docs for much much more.

There is also a rather experimental Class::DBI::Factory::Handler2 that should provide mod_perl 2 compatibility with little or no rewriting. It's in a fragile state at the moment because of fluctuations in the mod_perl and libapreq2 APIs, but later this year it should allow seamless migration from mod_perl 1 to 2 behind the Factory::Handler interface.

Class::DBI::Factory::Exception

Pervasive but fairly simple exception-handling routines for CDF-based applications based on Apache return codes. CDF::Handler uses try/catch for everything, and most of the other classes here will throw a CDF::Exception on error. See fail(), below.

Class::DBI::Factory::List

A fairly comprehensive builder and paginater of lists. It's iterator-based, so should be able to paginate most normal CDBI query results. You can also supply search criteria during list construction. See list() and list_from(), below.

Class::DBI::Factory::Ghost

'Ghost' objects are cdbi prototypes: each one is associated with a data class but doesn't belong to it. The ghost object will act like the cdbi object in most simple ways: column values can be set and relationships created, but without any effect on the database. I find this useful before the creation of an object (when populating forms) and after its deletion (for displaying confirmation page and deciding what next), but some people frown on such laziness. See ghost_object and ghost_from, below.

Class::DBI::Factory::Mailer

This is a simple interface to Email::Send: it can send messages raw or use the factory's Template object to format them, and it will use the factory's configuration settings to decide how email should be sent. See email_message below.

None of these modules is loaded unless you make a call that requires it, and all of them are easily replaced with your own subclass or alternative module. They all have the same sort of endless POD as this one :)

PERSISTENCE AND CONCURRENCY

The factory object by itself is a fairly lean and simple thing. Once it has loaded your data classes it should add very little overhead. It is designed to load its helpers only when you do something that requires them, and until you do it should be only a little bit slower or bulkier than working with Class::DBI directly.

Constructing a factory object can take a little while, though: it has to load all your data classes, establish database connections, and if you're using TT it has to create its Template object. It's possible to do all that in the course of a CGI request, but really not advisable. That's why the factory comes with all its associated mod_perl machinery. The factory object is designed to stay in memory, holding its database connection, template object and data stores in readiness for the next request.

CDF::Handler objects are lightweight and short-lived: a new one is constructed to deal with every incoming request. They can be be quick and simple because it's the factory that does all the work. The normal sequence goes like this:

1. Apache directs an incoming request to mod_perl
2. mod_perl causese the creation of a new Handler object to deal with the request.
3. Handler object uses an existing Factory object for session-management, database access, configuration, template-processing machinery and the other resources needed to deal with the request.
4. Handler object returns output to Apache and is destroyed.
5. Factory waits in memory for next request.

This is all made more useful by the fact that each factory is stored with a distinct id. You can keep several factories active in memory at once, each with a different configuration object and therefore its own cache of database handles, connection parameters, template paths and so on.

Because the data classes' normal database-access methods have been replaced with factory calls, this means that you can use the same set of data classes in as many sites as you like. Each time they ask for database access, the factory object will give them the right database for the current site.

The factories don't really 'sleep', of course. They're held in a class-data hash in Class::DBI::Factory. When a handler (or data class, or other script) calls CDF->instance to get its factory object, the instance method will consult its input and environment, work out a factory id and return the factory it's holding against that hash key. If no such factory exists, it will be created, and then left available for future requests.

In most cases, the 'id' associated with the factory will be the name of a website. If you want, you can specify an $ENV{_SITE_TITLE} in Apache's virtualhost definition:

    PerlSetEnv _SITE_TITLE = 'mysite.com'
    

But CDF will fall back on the $ENV{SITE_NAME} that Apache defines for each virtualhost and which will contain the name given to its ServerName directive.

The same mechanism can be adapted any other situation where you want to keep one or more factory objects handy without having to pass them round all the time. The factory id can be specified directly:

  my $factory = Class::DBI::Factory->instance('cd_collection');

or based on any environment variable you specify, such as:

  $Class::DBI::Factory::factory_id_from = 'USER';

which will keep one factory per user. Note that changing $factory_id_from has universal effect within the current namespace.

If no id can be retrieved from anywhere to single out a particular factory, then CDF will return the same singleton factory object on every call to instance(). This is often a useful shortcut, but if you don't like it you can always call instance(blah) and get an entirely new factory object instead.

MANAGED CLASSES

Each factory gathers up a set of what I've been calling 'data classes': that's your standard Class::DBI sub(sub)class with columns and relationships and whatnot. The set of classes is defined either by including a number of 'class' parameters in the configuration file(s) for each site, or by calling

  $factory->use_classes( list of names );

directly. Or both. The factory will ask each class for its moniker, along with a few other bits of optional but useful information, then it will drop in two methods that force the class to use the factory for database access. The result is as if you had added this in your base data class:

  sub _factory { Class::DBI::Factory->instance; }
  sub db_Main { shift->_factory->dbh; }

and that should be all that's required to get your application hooked up to the factory. For convenience, I usually add these methods too:

  sub config { shift->_factory->config; }
  sub debug { shift->_factory->debug(@_); }
  sub send_message { shift->_factory->send_message(@_); }

...but I don't want to trample on anyone's columns more than I already have, so I'll leave that up to you.

Note that Class::DBI will follow relationships in the usual way, require()ing the foreign class and hooking it up. You still have to declare all those classes in the factory configuration if you want the factory to provide access to them, or them to have access to it.

WARNING: If you miss out a class in the configuration but mention it in a has_many relationship, then that class will not be using the factory for its data access. This won't work, of course, and may mean that the barriers between your sites break down and cause bad strangeness.

FACTORY INTERFACE

Having bundled up a set of classes, the main purpose of the factory is to pass information between you and them. This is handled in a fairly intuitive and simple way: any mormal cdbi command of the form

  My::Class->foo(bar);

can be written

  $factory->foo($moniker, bar);
  

Provided that

  My::Class->moniker == $moniker;

and foo() is in the permitted set of methods, it should just work. If $moniker doesn't give us a valid class, we fail silently with only a debugging message. If foo() isn't allowed, we fail noisily.

CONFIGURATION

CDF is commonly used with at least two configuration files: a global server config and a local site config, but you can supply any number of configuration files to be read at construction time:

  my $factory = Class::DBI::Factory->new(
        '/global/config.file',
        '/local/config.file',
    ...
  );
  

You can also make the factory read one or two configuration files by filling these two environment variables:

  $ENV{_CDF_CONFIG} = '/global/config.file';
  $ENV{_CDF_SITE_CONFIG} = '/local/config.file';
  my $factory = Class::DBI::Factory->new;

This can also be done with PerlSetEnv, of course. There is no functional difference between configuration files, except in that they are read in a certain order and later declarations may supercede earlier.

The work here is actually done by Class::DBI::Factory::Config. Here's an example of a more practical configuration than the minimal ones above:

  db_type = 'SQLite'
  db_name = '/home/cdfdemo/data/cdfdemo.db'

  site_url = 'www.myrecords.com'
  site_title = 'My Record Collection'
  template_dir = '/home/myrecords/templates'
  template_suffix = 'html'

  class = 'My::Album'
  class = 'My::Artist'  
  class = 'My::Track'   
  class = 'My::Genre'   

  debug_level = 4
  mailer = 'Qmail'
  master_template = 'holder.html'
  default_view = 'front'

The config object is available to templates and data classes too, so there is no limit to the sort of information you might want to include there:

  form_box_width = '360px'
  html_email_allowed = 0
  show_document_count = 1
  ...

There's a sample application included with this distribution, using a configuration much like this one. It isn't big or clever, but it shows the basic principles at work and you might even be able to use it as a starting point. It uses SQLite and TT2, and should be very easy to set up provided you have a mod_perl-enabled Apache around. It's in ./cdfdemo and comes with a dim but enthusiastic installer and some very basic documentation.

CONSTRUCTION METHODS

In which a factory is built according to the instructions in the one or more configuration files described above.

new( @config_files )

This is now just a synonym for instance(). To maintain the old interface, it doesn't expect a factory id and relies on instance() to detect one. Unless you take steps to make sure that a site id can be discovered, this will mean that new() is a singleton constructor.

WARNING: If you want to use two factories with distinct configurations in the same script, you will need to construct them by calling instance(foo, ...) and instance(bar, ...). Calling new() twice with different config files won't work because the second factory will overwrite the first in the internal store.

instance( $tag, @config_files )

Returns the factory corresponding to the supplied or discovered site id. If no such factory exists, it is created and stored.

If no id is supplied then site_id is called, which by default will look for $ENV{_SITE_TITLE}. If that doesn't work, we will attempt to use Apache's $ENV{SITE_NAME}. If no site id is available from any source then a singleton factory object will be returned to all requests.

If you supply a list of configuration files to instance(), they will be used when a factory object is constructed. They will have no effect on an existing factory object.

  my $factory = Class::DBI::Factory->new(); 
  my $factory = Class::DBI::Factory->instance(); 
  # will use environment variables for site id and configuration file
    
  my $factory = Class::DBI::Factory->instance( $site_id );

  my $factory = Class::DBI::Factory->instance(
    $site_id,
    $global_config_file, 
    $per_site_package_file,
    $per_site_config_file,
    $per_user_config_file,
    $per_day_config_file...
  );

factory_id_from()

A handy mutator that gets or sets the name of the environment variable that we will use as the key when storing and retrieving a factory object. It defaults to the arbitrary _SITE_ID, as explained above.

Note that this has global effect. If you want to do odd things with a particular factory's id, you have to supply it directly to the construction and retrieval methods. The easiest way to do that is call to instance( $site_id ) for the initial construction step.

config_class()

Should return the Full::Class::Name that will be used to handle factory configuration. Defaults to Class::DBI::Factory::Config.

config()

Returns the configuration object which the factory is using, with which any settings can be retrieved or set.

If you're using Class::DBI::Factory::Config, then the config object is just a thinly-wrapped AppConfig object.

Note that no configuration is read or built until you call config(). The first call to this method will cause the factory to load Class::DBI::Factory::Config and thus AppConfig and read whatever configuration files have been specified.

id()

Returns the site tag by which this factory would be retrieved. This ought to be the same as site_id, which looks in the host configuration, unless something has gone horribly wrong.

use_classes()

For quick and dirty work you can skip any or all of these configuration mechanisms and just pass in a list of Full::Class::Names. You can also set configuration parameters, so this will work:

  my $factory = Class::DBI::Factory->new;
  $factory->use_classes( qw(My::Movie My::Actor My::Role) );
  $factory->config->set( worst_effects_ever => 'All out monsters attack!' );
  

This has to be done first, before any factory method is called that depends on the data classes being loaded, which is nearly all of them.

init()

Initialises the factory: loads data classes, reads configuration files, and so on. When constructed the factory is vacant: this method populates it with classes. init() is called for you before any factory operation that needs it, so you very rarely need to invoke it directly. There are a few situations, though: if you want to access a data class directly before you have done anything else with the factory, for example. This is rare outside a test script.

init() just calls _load_classes(), but this may change in future. Use init().

_load_classes()

Each class that has been specified in the configuration (the list is retrieved by calling _class_names, if you felt like changing it in devious ways) is required here, in the usual eval-and-check way, and its moniker stored as a retrieval key.

Normally this is done only once, and before anything else happens, but if you call _load_classes(1) (or with any other true value), you force it to require everything again. This doesn't unload the already required classes, so you can't, currently, use this to change the list of managed classes.

This is mostly accomplished by calls to the following methods:

pre_require()

This method is called once before the loading of classes begins (unlike post_require, which is called for each class). It can act on the configuration data to affect the list of classes called, or make whatever other preparations you require. The default does nothing.

load_class()

This method handles the details of incorporating each individual class into the application. It requires the module, checks that the require has worked, and among other things makes calls to assimilate_class and then post_require for each class:

assimilate_class()

This method is called to store information about the class in the factory object. The default version here hopes that each class will have at least some of the following methods:

moniker: a tag by which to refer to the class, eg 'cd'
class_title: a proper name by which the class can be described, eg 'CD'
class_plural: plural form of the title
class_description: a blurb about the class

Only the moniker is actually required and the standard cdbi moniker mechanism will normally provide that, so you can safely ignore all this unless it seems useful.

post_require

This is called for each class after it has been loaded and assimilated, and is supplied with the moniker and full class name.

Here it is used to place factory() and db_Main() methods in each data class. That work is actually done by:

create_factory_accessor

Which you can override or ignore. If your cdbi subclass already has a db_Main method that you want to keep, just override post_require().

permitted_methods()

This method defines a core set of method calls that the factory will accept and pass on to data classes: it's the Class::DBI interface, basically, along with the extensions provided by Class::DBI::mysql and a few synonyms to cover old changes (has_column == find_column, for example) or simplify template code.

It does this by returning a hash of

  factory_method_name => cdbi_object_method_name,

which is used as a dispatch table by AUTOLOAD. Subclass this method to replace the standard factory interface with a reduced or different set of allowed methods.

extra_methods()

This is a hook to allow subclasses to extend (or selectively override) the set of permitted method calls with a minimum of bother. It returns a reference to a hash that is appended to the hash returned by permitted_methods, with the same factory method => cdbi method structure.

It's common for a local subclass of Class::DBI to add custom operations to the normal cdbi set: a retrieve_latest here, a delete_older_than there. To access these methods through the factory, you need to add a local factory subclass next to the cdbi subclass, containing at least an extra_methods method.

  package My::Factory;
  use base qw( Class::DBI::Factory );

  sub extra_methods {
    return {
      latest => retrieve_latest,
      purge => delete_older_than,
      by_title => retrieve_by_title,
    }
  }

The default extra_methods method doesn't do anything, so it can be overridden freely.

classes()

returns an array reference containing the list of monikers. This is populated by the _load_classes method and includes only those classes which were successfully loaded.

_class_names()

returns an array reference containing the list of full class names.

class_name()

Returns the full class name for a given moniker.

has_class()

Returns true if the supplied value is a valid moniker.

relationships( $moniker, $type )

A handy gadget that looks into Class::DBI's meta_info to find the relationships entered into by the monikered class. The relationship type defaults to 'has_a', and we return a hash of method names => foreign class monikers.

  $factory->relationships( 'album' );

in the supplied demo application would return ('genre', 'artist').

relationship_exists( $moniker, $relationship )

Returns true if the class designated by $moniker has a has_many relationship of the supplied name.

moniker_from_class()

Given a full class name, returns the moniker. This is rarely used directly, since you would normally just call $class->moniker, but it is wrapped in an eval and therefore safe in the case of an uncertain value for $class.

moniker_from_relationship()

Given a moniker and a has_many relationship name, returns the moniker of the foreign class. This is useful if, say, you want to count the number of related objects in SQL rather than perl: You can turn the relationship name into a class name here and call count_where on that.

inflate_if_possible()

Accepts a column name => value pair and inflates the value, if possible, into a member of the class monikered by the column name.

translate_to_moniker( column name, moniker of calling class )

Some column names don't match the moniker of the objects they contain: perhaps because there is more than one column containing that type, or perhaps just for readability. get_moniker maps the column name onto the moniker.

The method defined here (which expects to be overridden), strips _id off the end of a column name, and maps 'parent' onto the moniker of the present class.

moniker_aliases( )

Returns a hashref of column_name => moniker that is used by translate_to_moniker to turn column names into monikers for inflation. This is only of relevance to $factory->inflate_if_possible, which is called from a few places (like Handler->param) but doesn't have any effect on the normal cdbi inflation mechanism.

The hashref is empty by default: this method expects to be subclassed.

ghost_class()

Sets and/or returns the Full::Class::Name that will be used to handle paginated lists. Checks the value of $Class::DBI::Factory::list_class then defaults to Class::DBI::Factory::List.

There are several methods here of this form, used to determine the class that should be used to perform some function or other. They include list_class, mailer_class, iterator_class and ghost_class. The logic is the same in each case;

    In the case of ghost_class, the class-wide default is held in $Class::DBI::Factory::ghost_class and can be set at any time. The hard default if no other value is discovered is 'Class::DBI::Factory::Ghost', which is the standard helper that comes with CDF. The other methods work in the same way but with predictably different variable names.

ghost_object( moniker, columns_hashref )

Creates and returns an object of the ghost class, which is just a data-holder able to mimic a cdbi object well enough to populate a template, but no more. A hashref of column=>value should be supplied and will be used to construct the ghost.

ghost_from( data_object )

Returns a ghost object based on the class and properties of the supplied real object. This is useful to keep a record of an object about to be deleted, for example.

(In which case the deleted object can be reconsituted with a call to $ghost-\make>. You will lose anything that was removed in a cascading delete, though. This is not nearly good enough to serve as an undo mechanism unless you extend the ghost to ghost all its relatives too).

title() plural() description()

each return the corresponding value defined in the data class, as in:

  Which of these [% factory.plural('track') %] 
  has not been covered by a boy band?
  

We only have these values if you add the corresponding class_title, class_plural and class_description methods to your data classes. I normally do this:

  package My::Album
  sub moniker { 'album' }
  sub class_title { 'Album' }
  sub class_plural { 'Albums' }
  sub class_description { 'An album (from Latin albus "white", "blank", relating to a blank book in which something can be inserted) is a packaged collection of related things.' }

USEFUL SERVICES

The rest of the factory's functions are designed to provide support to Class::DBI applications. The factory is an efficient place to store widely used components like database handles and template engines, pagers, searches and lists, and to keep useful tools like escape and unescape, so that's what we do:

dsn()

Returns the $data_string that CDF is using to create handles for this factory. Some modules - like Class::DBI::Loader - want to be given a dsn rather than a database handle: sending them $factory->dsn should just work.

If a db_dsn parameter is supplied, it is accepted intact. Otherwise we will look for db_type, db_name, db_host, db_server and db_port parameters to try and build a suitable data source string. You will probably also want to db_username and db_password settings unless you're using SQLite.

dbh()

Returns the database handle which is used by this factory.

Each factory normally has one handle, created according to its configuration instructions and then made available to all its data classes. The main point of this is to get around the one class -> one table assumptions of Class::DBI: each factory can provide a different database connection to the data using different data.

For this to be useful you must also override db_Main in your Class::DBI subclass, eg:

  sub db_Main { return shift->factory->dbh(@_); }

Should do it, except that you will probably have subclassed CDF, and should use the name of your subclass instead.

You can safely ignore all this unless your data clases need access to configuration information, template handler, unrelated other data classes or some other factory mechanism.

set_db()

Can be used to set database connection values if for some reason you don't want them in a config file. Expects to receive a hashref of parameters. The tests for CDF use this approach, if you want a look.

  $factory->set_db({
    db_type => '...',         # defaults to 'SQLite'
    db_host => '...',         # in which case no other parameters
    db_port => '...',         # are needed except a path/to/file
    db_name => '...',         # in db_name
    db_username => '...',
    db_password => '...',
  });
 

Defaults can be supplied by Class::DBI::Config::default_values, which is called early in the configuration process.

_dbc()

Taps into the terrible innards of Ima::DBI to retrieve a closure that returns a database handle of the right kind for use here, but instead of being incorprated as a method, the closure is stored in the factory object's hashref.

(All dbh really does is to execute the closure held in $self->{_dbc}.)

This depends on close tracking of internal features of Ima::DBI and Class::DBI, since there is no easy way to make use of the handle-creation defaults from the outside. It will no doubt have to change with each update to cdbi.

db_options()

Returns the hash of attributes that will be used to create database connections. Separated out here for subclassing. This too will change abruptly when new versions of CDBI come out.

db_rootclass()

Returns the full name of the root class for $dbh. It is very unlikely that this will not be DBIx::ContextualFetch, but I suppose you might have subclassed that.

I doubt very much that any good will come of changing this unless you do it immediately upon construction of the factory and before any database has been accessed access.

tt()

Like the database handle, each factory object can hold and make available a single Template object. This is almost always called by handlers during the return of a page, but you sometimes find that the data classes themselves need to make use of a template, eg. to publish a page or send an email. If you don't intend to use the Template Toolkit, you can override process or just ignore all this: the Toolkit is not loaded until tt is called.

Template paths can be supplied in two ways: as simple template_dir parameters, or by supplying a single template_root and several template_subdir parameters. The two can be combined: See Class::DBI::Factory::Config for details.

process()

  $self->process( $template_path, $output_hashref, $outcome_scalar_ref );

Uses the local Template object to display the output data you provide in the template you specify and store the resulting text in the scalar (or request object) you supply (or to STDOUT if you don't). If you're using a templating system other than TT, this should be the only method you need to override.

Note that process returns Apache's OK on success and SERVER_ERROR on failure, and OK is zero. It means you can close a method handler with return $self-process(...)> but can't say $self-<gtprocess(...) or ... >

This is separated out here so that all data classes and handlers can use the same method for template-parsing. It should be easy to replace it with some other templating system, or amend it with whatever strange template hacks you like to apply before returning pages.

list()

returns a list object with the parameters you specify. These can include column values as well as display parameters:

  my $list = $factory->list('cd',
    year => 1969,
    artist => $artist_object,
    sort_by => 'title',
    sort_order => 'asc',
    step => 20,
  );

The default list module (Class::DBI::Factory::List) will build a query from the criteria you specify, turn it into an iterator and provide hooks that make it easy to display and paginate lists.

list_from()

For situations where the list method doesn't quite provide the right access, you can also create a list object from any iterator by calling:

  my $list = $factory->list_from($iterator);

Which will provide display and pagination support without requiring you to jump through so many hoops.

list_class()

Sets and/or returns the Full::Class::Name that will be used to handle paginated lists. Checks the value of $Class::DBI::Factory::list_class then defaults to Class::DBI::Factory::List.

iterator_from( class, listref )

Returns an iterator built around the list of supplied ids. A list of objects can also be used instead: it's not very efficient, but sometimes it's necessary.

iterator_class()

Sets and/or returns the Full::Class::Name that will be used to build iterators. Checks the value of $Class::DBI::Factory::iterator_class then defaults to Class::DBI::Factory::List.

fail( $parameters )

A general-purpose failure-handler. Throws a SERVER_ERROR exception with the supplied -text parameter. Parameters are passed on to the exception handler. If you don't like CDF::Exception, overriding this method will almost make it go away.

EMAIL

This is a bit tangential, but for web applications the factory is the obvious place to handle email-sending: like the template and database connections, we hold an email channel open and wait for instructions.

mailer_class()

Sets and/or returns the Full::Class::Name that will be used to handle email. Checks the value of $Class::DBI::Factory::mailer_class then defaults to Class::DBI::Factory::Mailer.

mailer()

Returns an object of the mailer class. These are usual very dumb creatures with only a send_message method and a few bits and pieces. We'll hang on to it and it should be used for all subsequent emailing duties.

send_message()

Sends an email message. See Class::DBI::Factory::Mailer for more about the required and possible parameters, but to begin with:

  $factory->send_message({
    to => 'someone@there',
    from => 'someone@here',
    subject => 'down with this kind of thing',
    message => 'Careful now',
  });

Instead of the message parameter, you can pass through a template path as template. The usual templating mechanism will be used to generate the message using the other values you have supplied will be passed to the template. Otherwise, the mailer will look for a message parameter and treat that as finished message text.

email_admin()

Sends a message to the standard admin address associated with this factory configuration. Otherwise exactly the same as send_message.

DEBUGGING

The factory provides a general-purpose logger that prints to STDERR. Each debugging message has an importance value and optionally a set of subject tags. The configuration of each factory defines a threshold, and a set of debugging topics that we're interested in. If the message importance is less than the threshold, and there is an overlap between the debugging topics and the message tags, the message will be printed.

If there are no debugging topics in the configuration, that part of the filter is omitted and all sufficiently urgent messages appear.

Note that including debugging lines always incurs some small cost, since this method is called and the threshold comparison performed each time, even if the message isn't printed. For more expensive debugging operations you might want to test the value of $factory->debug_level before doing the work.

  $self->factory->debug_level(1);
  $self->factory->debug(2, 
    "session id is $s", 
    "session key is $k",
    "these messages will not be logged",
  );
  $self->factory->debug(0, "but this will appear in the log");

debug( $importance, @messages )

Checks the threshold and prints the messages. Each message has prepended a [site_id] marker, but even so nothing will make much sense if requests overlap. For debugging processes you probably want to run apache in single-process mode.

debug_level()

Sets and gets the threshold for display of debugging messages. Defaults to the config file value (set by the debug_level parameter). Roughly:

debug_level = 1

prints a few important messages: usually ways in which this request or operation differs from the normal run of events

debug_level = 2

prints markers as well, to record the stages of a request or operation as it is handled. This can be a useful trace when trying to locate a failure.

debug_level = 3

adds more detail, including AUTOLOAD calls and other bulky but useful notes.

debug_level = 4

prints pretty much everything as it happens.

debug_level = 5

won't shut up.

If debug() is called as a class method, configuration information will not be available. In that case the global value

  $Class::DBI::Factory::class_debug_threshold

will be used. It defaults to zero. Changing it will have global effect within the current namespace (eg all factories within a given apache process).

debug_topic {

Debug messages optionally come with additional parameters that associate them with one or more topics. If you supply one or more debug_topic parameters either by passing them to this method or by including one or more debug_topic parameters in the site configuration, then only messages associated with those topics will be printed.

Useful possibilities include 'handler', 'input', 'templates', 'storage', 'factory', 'list', 'ghost' and 'session'.

If no topic is defined then all messages of sufficent importance are printed.

version()

Returns the global $Class::DBI::Factory::VERSION, so your subclass will probably want its own version method.

timestamp()

Returns the timestamp (ie creation time in epoch seconds) of this factory object. Sometimes useful for debugging, especially if you're using GTopLimit or some such killer-off.

The config object associated with the factory normally has its own timestamp: unlike this one, it is updated when the configuration is refreshed. Comparing them can be useful.

add_status_menu()

If CDF is loaded under mod_perl and Apache::Status is in your mod_perl configuration, then calling CDF-add_status_menu> will add a menu item to the main page. The obvious place to call it from is startup.pl.

The report it produces is useful in debugging multiple-site configurations, solving namespace clashes and tracing startup problems, none of which should happen if the module is working properly, but you know.

Remember that the server must be started in single-process mode for the reports to be of much use, and that factories are not created until they're needed (eg. on the first request, not on server startup), so you need to blip each site before you can see its factory in the report.

SUBCLASSING

In serious use, Class::DBI::Factory and all its helper modules expect to be subclassed and extended. The methods you will want to look at first are probably:

  CDF::Handler::build_page()
  CDF::Handler::factory_class()
  
  CDF::pre_require()
  CDF::post_require()
  CDF::extra_methods()
  CDF::list_class()
  CDF::mailer_class()
  CDF::iterator_class()
  
  CDF::Config::skeleton()
  CDF::Config::list_parameters()
  CDF::Config::hash_parameters()
  CDF::Config::default_values()

All of which have been separated out and surrounded with ancillary methods in order to facilitate selective replacement. See the method descriptions above, and in the helper modules, which will go on about all this in exhausting detail.

I'm trying to keep CDF minimal and to the point, with variable success. It's always tempting to implement something at this level, so that it's universally available, so the development cycle has mostly consisted of throwing stuff in and then pruning carefully. This version (0.9) is mostly the result of pruning, so you can imagine how bushy some of the others have been :)

For an example of how much can be done on this platform, have a look at www.spanner.org/delivery/

KNOWN ISSUES

This version of CDF is unlikely to work with any combination other than Class::DBI 0.96 and Ima::DBI 0.33.
CDF under mod_perl is not compatible with the unique-object cache introduced in Class::DBI v0.96, and cannot be made so since the cache is held as class data and assumes that an object of a class with a certain id is always the same object. The next version of CDBI will probably give me a way to work with this: there are plans to introduce a more structured object cache, and/or to make it possible to subclass some of its storage and retrieval mechanisms. Until then, the factory disables the cache immediately upon loading.
Class::DBI and Apache::DBI are not entirely compatible. This is because Ima::DBI has its own caching mechanism for database handles. It's not a serious problem unless you're using database transactions, in which case some necessary cleaning up doesn't happen, but it's easily avoided just by omitting Apache::DBI from your setup.
I haven't tried using CDF with the various setup_table methods provided by cdbi subclasses like Class::DBI::mysql. There's no reason why they shouldn't work, but no reason why they should, either.

BUGS

Are likely. Please use http://rt.cpan.org/ to report them, or write to wross@cpan.org to suggest more sweeping changes and new features. I use this all the time and am likely to respond quickly.

TODO

  • Ensure cross-database compatibility (I've only used this with mysql and sqlite). This is especially problematic for CDF::List, probably.

  • Improve Apache::Status reports, eg with optional logs and error reports.

  • Wiki and mailing list. you know you know.

REQUIRES

Class::DBI
AppConfig (unless you replace the configuration mechanism)
Apache::Request (if you use CDF::Handler)
Apache::Cookie (if you use CDF::Handler);
DBD::SQLite2 (but only for tests and demo)

SEE ALSO

Class::DBI Class::DBI::Factory::List Class::DBI::Factory::Config Class::DBI::Factory::Handler Class::DBI::Factory::Exception Class::DBI::Factory::Ghost

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.

2 POD Errors

The following errors were encountered while parsing the POD:

Around line 805:

=over should be: '=over' or '=over positive_number'

Around line 823:

You forgot a '=back' before '=head2'