Asides And Oddities

This is a grab bag of things not yet seen elsewhere. Things mentioned here could get moved into other documentation or Wiki pages when it seems appropriate. If duplications are found, note where, and remove the duplication here.

Porting to Older Perl Versions

Perl 5.6.1

[TODO: peterdragon: not sure if this still applies, I think you need Perl 5.8.5 now]

I was able to get the basic Catalyst running on Perl 5.6.1, even though the stated prerequisite was 5.8.1. I was getting very strange "Initializer for response must be a hash reference" messages (something like that) and all requests would die.

I tracked that down to module Class::Struct. Perl 5.6.1 installed version 0.59 of Class::Struct. Once I replaced Class::Struct with version 0.63 from Perl 5.8.7 the nasty messages stopped and requests were able to be processed by Catalyst.

Example of changing log formatting

The documentation for Catalyst::Log mentions that its routines can be replaced. I just wanted to override the formatting. I did it inserting the following code into my application class module:

package Widget::Log;  # create new subclassed log class
use base 'Catalyst::Log';

sub _log {    # override the format and output method
 my $self    = shift;
 my $level   = shift;
 # my $time    = localtime(time);    # This is the overridden line
 my $time    = DateTimeMMDDpHHMMSS();
 my $message = join( "\n", @_ );
 printf( STDERR "[%s] [catalyst] [%s] %s\n", $time, $level, $message );
}

(I did the same, but sri thinks _log is *private* and should remain so - herakles)

#      Return local date/time in format MMDD.HHmmSS
{ # Persistent private variables for subroutine
my( $lasttime, $laststring );
sub DateTimeMMDDpHHMMSS {
  my( $time ) = shift  ||  time;
  unless( defined $lasttime  &&  $lasttime == $time ) {
    $lasttime = $time;
    my( $sec, $min, $hour, $mday, $mon, $year ) = localtime($lasttime);
    $laststring = sprintf( "%02d%02d.%02d%02d%02d",
                            $mon+1, $mday, $hour, $min, $sec );
  }
  $laststring;
}
}

package Widget;

To set the use of this log class, do this in your app class (as is mentioned in the docs):

__PACKAGE__->log( new Widget::Log );
__PACKAGE__->setup;

Moving the "Authentication, but not for static content" checking

Most of the authentication documentation and examples mention a 'gotcha'. You want to force automatic login checking, but not for static content requests. Most examples have something like this, put into the begin method of the highest-level application class:

# Don't force login on static content
return 1 if ($c->req->{path} =~ /^(static|login)$/);

However you can avoid trying to figure out the correct RE here - because the work has already been done!

Over in Cookbook they told you to create a controller to serve up static files, and create actions like:

# serve all files under /static as static files
sub default : Path('/static') {
      :  :  :  :  :  :    
}
# also handle requests for /favicon.ico
sub favicon : Path('/favicon.ico') {
      :  :  :  :  :  :    
}

You have already told Catalyst to send actions for static content to your App::C::Static (or whatever) class. You specified the correct RE's here, why repeat yourself?

Now all we need to do is prevent the begin method in the App class from getting control. Umm, oh!, only the begin method closest to the action method gets control. All we need do in add a dummy begin method to your App::C::Static class:

sub begin : Private {
}

Now the App class {{{begin}}} method never sees the static content request. No fuss, no muss, and no repeating yourself.

Multi-platform YAML Configurations

Not strictly Catalyst-related, but could be useful if you want to keep a single config with some sections specific to single platforms or locations. I have to move between Linux and Windows with the same source. Yet the locations of directories are different. How to do this without two or more separate (and coordinated) configurations?

Here is part of my YAML configuration:

--- #YAML:1.0
name: WonderWidget

session:
    expires: 691200
    storage: /var/opt/widget/cgi/sessions/widget_mmap_sessions
# Location of system programs
system:
    perl_bin_path: /usr/bin/perl

# Alternate configuration values. These are merged on top of the
#   'normal' configuration values above.
# This config is for running under Windows during testing
alternate_windows:
    session:
        storage: /var/opt/widget/cgi/sessions/widget_mmap_sessions
    system:
        perl_bin_path: /perl587/bin/perl.exe

I have sections used by plugins and sections used by my code. Some of these values need to be changed if I am running under Windows. For just those values I create an 'overlay' section, containing some keys and values duplicating the production section.

In my startup code I conditionally use Hash::Merge to copy an alternate section "on top of" the normal values:

# Fetch application configuration from file
my  $appl_config_path = __PACKAGE__->config->{home} . '/UpWell.yml';
my  $appl_config_ref  = YAML::LoadFile( $appl_config_path );

# If we need to apply our platform-specific config values overlay ...
if ( $^O eq 'MSWin32' ) {
    my  $alt_config = $appl_config_ref->{alternate_windows};
    if ( $alt_config ) {
        Hash::Merge::set_behavior('RIGHT_PRECEDENT');
        Hash::Merge::set_clone_behavior(0);
        $appl_config_ref = Hash::Merge::merge( $appl_config_ref, $alt_config );
    }
}

__PACKAGE__->config( $appl_config_ref );

One less thing to get wrong as I switch back and forth, and all in one file.

My tags:
 
Popular tags:
 
Powered by Catalyst
Powered by MojoMojo Hosted by Shadowcat - Managed by Nordaaker