Catalyst Plugins, Writing

From Dev411: The Code Wiki

This page provides some tips for writing Catalyst plugins.

Table of contents

Tests

An easy way to write tests for plugins is to create a Catalyst::Test TestApp. See Catalyst::Plugin::DefaultEnd, Catalyst::Plugin::I18N, etc. for examples. ##live.t where ## is 04 seems to be a common name for a test using TestApp so your distribution ends up with the following files:

 t/04live.t
 t/lib/TestApp.pm

To run the test use:

 prove -v t/04live.t

Prerequisites, Checking for

Sometimes your plugin will rely on the usage of other plugins. You can use $c->can to check for those plugins.

# From Catalyst::Plugin::UploadProgress

sub setup {
    my $c = shift;
            
    $c->NEXT::setup(@_);

    [...]
    
    unless ( $c->can('cache') && $c->can('session') ) {
        Catalyst::Exception->throw(
            message => '<Plugin Name> requires cache and session plugins.'
        );
    }
}

Error at Startup

You can thrown an exception during start up if there's an error by using an eval cage and then setting $c->log->debug if $c->debug is set. Here's an example:

# From Class::Model::CDBI

    eval { $self->loader( Class::DBI::Loader->new(%$self) ) };
    if ($@) { 
        Catalyst::Exception->throw( message => $@ );
    }
    else {
        $c->log->debug(
            'Loaded tables "' . join( ' ', $self->loader->tables ) . '"' )
          if $c->debug;
    }
# From Catalyst::Model::DBI

sub new {
    my ( $self, $c ) = @_;
    $self = $self->NEXT::new($c);
    $self->{namespace}               ||= ref $self;
    $self->{additional_base_classes} ||= ();
	eval { 
		$self->dbh( 
			DBI->connect( 
				$self->{dsn}, 
				$self->{user}, 
				$self->{password},
				$self->{options}
			)
		);
	};
    if ($@) { $c->log->debug( qq{Couldn't connect to the database "$@"} ) if $c->debug }
    else { $c->log->debug ( q{Connected to the database} ) if $c->debug; }
    return $self;
}

Context Namespace, Cleaner

Catalyst puts plugin methods in the $c context namespace like $c->foo, however sometimes it is cleaner to have a plugin method like $c->myplugin->foo. This can be accomplished by separating the plugin into two modules, a plugin and a plugin wrapper. Although plugins are not typically written this way, it can be done. The plugin wrapper could be:

package Catalyst::Plugin::MyPlugin;
use strict;
use base 'Class::Data::Inheritable';
use MyPlugin;

__PACKAGE__->mk_classdata('myplugin');

sub prepare {
  my $c = shift;
  $c = $c->NEXT::prepare( @_ );
  $c->mailer( MyPlugin->new( $c ) );
  return $c;
}

1;

Private Namespace Plugins

Catalyst automatically finds plugins in the Catalyst::Plugin:: namespace. You can use the following to add plugins in your own namespace.

# From #catalyst irc channel

miyagawa: If you want plugins for your own app, you could
  sub MyApp::setup_plugins {
    my $c = shift;
    $c->NEXT::setup_plugins(@_);
    # now push your own plugins on @ISA
  }

SVN Patches

If you want to create a patch for an existing Catalyst plugin, see the page on SVN Patches