[Catalyst] Random thoughts on helper class generation

neil.lunn neil at mylunn.id.au
Fri Jan 24 23:58:53 GMT 2014


Hi all,

Was just thinking through setting up various project minting files and 
got to looking at the default Catalyst app layout from the helper. 
Specifically I wondered how much the defaults were just being cargo 
culted, and specifically addressing my dislike for overuse of inline 
__PACKAGE__ calls when we have Moose and BUILD available. So random 
musings below:

Here's a sensible base class for the App context:

package Catalyst::BaseClass;
use Moose;
use namespace::autoclean;

use Catalyst::Runtime 5.80;
use Catalyst qw/ PluginLoader /;  # Should be all we need

extends 'Catalyst';

our $VERSION = '0.01';

sub hook_config {
   my $self = shift;
   my $class = ref($self);

   # Set some basic defaults
   return {
     name => $class,
     enable_catalyst_header => 1,  # Send X-Catalyst header
   };
}

sub hook_logger {
   # Do nothing by default. Setup will take care of it.
}

sub BUILD {
   my $self = shift;
   my $class = ref($self);

   # Setup Config
   $self->config(
     %{ $self->hook_config }
   );

   # Place a hook to hang a logger on before setup is called
   $self->hook_logger();

   $class->setup();

}

As you can see by the "hook" methods, this was "take 2"  where I was 
abstracting my personal "cause" from the general base class. By placing 
the hooks in you can abstract in your application, as in:

package YourApp::Web;
use Moose;
use namespace::autoclean;

extends 'Catalyst::BaseClass';

our $VERSION = '0.01';

our $load_class = \&Plack::Util::load_class;

around 'hook_config' => sub {
   my $orig = shift;
   my $self = shift;
   my $class = ref($self);
   my $basename = $class;
   $basename =~ s/::.+//g;

   use Hash::Merge qw/merge/;

   # Setup Config
   my $configclass = $load_class->( "${basename}::Config" );
   return merge( $self->$orig, $configclass->config || {} );

};

override 'hook_logger' => sub {
   my $self = shift;

   # Optional logger from class
   my $logger = $self->config->{Logger};
   if ( defined $logger ) {
     die "Config Logger requires a Class key"
       unless $logger->{Class};

     my $logclass = $load_class->( $logger->{Class} );
     $self->log( $logclass->new( @{ $logger->{Config} || [] } ) );

     $self->log->debug( qq/Initialized logger: "$logclass"/ );

   }

};

Now, realistically even the *second* and extended implementation is 
still notably generic and *for me* this is even enough to place as a 
*base class* to every application as this is how I will lay things out. 
Plack::Util seems to be a fair assumption to be loaded as the end result 
is a PSGI app, and Plack::Runner is going to pull this in. For the nosy, 
the Logger class in this case is a mere wrapper around Log::Log4perl in 
this case, and would only get the logger instance if it had already been 
initialized. You can (and I do) set up Plack middleware to do the same 
thing, making the same logger available to other PSGI parts that might 
be used in your application, all without needing to wrap context to get 
at the logger, or explicitly call Log::Log4pperl::get_logger as we might 
just want to change that to a different logger at some stage.

So general thoughts are:
1. Have a config class that is external to Catalyst logic. You can use 
it elsewhere without hassle.
2. Have a hook to hang that config on and get it early; because
3. Hang a logger on a hook before 'setup' is called so you can get the 
startup logging on debug
4. Pull in the plugins from Config so there isn't a need to keep 
modifying that code in the context class for every app

Also minimising the selection of Plugins. I do try to keep this to 
session and auth stuff for convenience, and again have these as just 
thin layers over Plack Middleware. Other things can be delegated to role 
applicator stuff, which I haven't typed in here.

Anyone else have thoughts? Alternate favourite methods for layout?




---
This email is free from viruses and malware because avast! Antivirus protection is active.
http://www.avast.com




More information about the Catalyst mailing list