Sapphire, Steel and Perl

ajt on 2003-05-11T18:07:30

Spent some idle time today watching the rest of my Sapphire and Steel DVD. Okay it's only old 4:3 ratio, and the original video isn't the richest of sources, but it was still rather fun.

On a more productive front I've been working on a Apache::Registry/CGI-BIN based RSS client. I'm trying to make a small and simple client, but is more robust than a simple hack. By using the rather excellent Cache::FileCache module, CGI and my XML::RSS::Tools, I've been able to knock up a simple enough client in less than 200 lines of code.

If you have never used DeWitt Clinton's very useful Cache::Cache then you are missing a very useful set of modules. I first spotted it when reading one of Randal's excellent WebTechniques Perl columns - "Getting One-Click Processing".


Hrmmm

belg4mit on 2003-05-11T18:50:46

You mean like this? http://pthbb.org/software/pnews/

I'm overdue for pushing out another release,
I've added a fair bit of functionality,
maybe in a few weeks once finals are done

Re:Hrmmm

ajt on 2003-05-11T20:21:05

Not that sophisticated. My code is simple Apache::Registry application that takes the URI of a RSS Feed and the name of a XSL stylesheet, and it returns a nicely formatted HTML page. There is no real user interface, short of a HTML form you build yourself—which I have done.

In case anyone is interested here is the Perl code for my ultra-light RSS application. It will go into the next release of XML::RSS::Tools as a new example soon.

#!d:/bin/perl/bin/perl

use strict;
use warnings;
use CGI;
use CGI::Carp;
use XML::RSS::Tools;
use Cache::FileCache;

    $|++;
    my $q = CGI->new;

    my %config= (
        xsl_path => "G:/apache/cgi-bin/",
        css1     => "/style/style.css",
        css2     => "/style/news-style.css",
        title    => "i r e d a l e consulting | News Feeds |",
        language => "en-GB",
        namespace=> "xhtml",
        cache_depth    => 1,
        auto_purge     => '+2h',
        default_expire => '+1h',
        cache_root     => 'd:/tmp/FileCache/news');

    my $input = process_params($q);
    top_html($q, $input, \%config);
    tad ("Usage: news2html.pl?site=uri;style=xsl[;cache=off | ;debug=on]\n", $q)
        unless $input->{uri} && $input->{xsl};
    my ($cache, $cache_key, $data) = manage_cache($q, $input, \%config);
    $data = process_rss_feed($q, $input, \%config) unless $data;
    print $data;
    end_fragment($q, $input);
    $cache->set($cache_key, $data) if $cache;
    %ENV = ();
    exit;

#
#    Process Input Paramaters
#
sub process_params {
    my $q = shift;
    my %p;
    $p{uri}  = $q->param("site") if $q->param("site");
    $p{xsl}  = $q->param("style") if $q->param("style");
    $p{debug}= $q->param("debug") if $q->param("debug");
    $p{cache_status} = lc($q->param("cache")) || "on";
    return \%p;
}

#
#    Output HTTP and HTML headers
#
sub top_html {
    my $q     = shift;
    my $input = shift;
    my $config= shift;

    $input->{uri} = "No Site URI" unless $input->{uri};
    $input->{xsl} = "No XSL stylesheet" unless $input->{xsl};
    print $q->header(-type      => "text/html",
                     -expires   => $config->{default_expire}),
          $q->start_html(-title => $config->{title} . " " . $input->{uri} . " and " . $input->{xsl},
                         -lang  => $config->{language},
                         -style => {-src => $config->{css1},
                                     -verbatim => '@import url(' . $config->{css2} . ');'}
    );
    print $q->comment("\nInput Conditions:\nURI: ", $input->{uri},
                                  "\nxsl sheet: ", $input->{xsl},
                                  "\nCache:     ", $input->{cache_status}, "\n") if $input->{debug};
};

#
#    Deal with adding, removing or retrieving data from cache
#
sub manage_cache {
    my $q      = shift;
    my $input  = shift;
    my $config = shift;
    my $cache_key = $input->{uri} . $input->{xsl};

    my $cached_file;
    my $c_handle = Cache::FileCache->new({namespace             => $config->{namespace},
                                           default_expires_in  => $config->{default_expire},
                                         auto_purge_interval => $config->{auto_purge},
                                         cache_depth         => $config->{cache_depth},
                                            cache_root          => $config->{cache_root},
                                         auto_purge_on_set   => 1 } ) || tad ("Unable to create Cache object", $q);

    if ($input->{cache_status} eq "off") {
        $c_handle->remove($cache_key);
    } else {
        $cached_file = $c_handle->get($cache_key);
        $cached_file .= "\n<!-- Cached Fragment -->\n" if $cached_file && $input->{debug};
    }
    return $c_handle, $cache_key, $cached_file;
}

#
#    Get and process RSS Feed
#
sub process_rss_feed{
    my $q      = shift;
    my $input  = shift;
    my $config = shift;

    my $rss = XML::RSS::Tools->new;
    $rss->set_version(0);
    $rss->set_http_client("lite");

    if (! $rss->xsl_file($config->{xsl_path} . $input->{xsl})) {tad ($rss->as_string('error'), $q)};

    print $q->comment("\nHTTP Client: " . $rss->get_http_client . "\n") if $input->{debug};
    if (! $rss->rss_uri($input->{uri})) {tad ($rss->as_string('error'), $q)};

    if ($rss->transform) {
        return $rss->as_string;
    } else {
        tad ($rss->as_string('error'), $q);
    }
}

#
#    Handle the end of the page
#
sub end_fragment {
    my $q     = shift;
    my $input = shift;
    my $ref   = $q->referer() || "";
    my $self  = $q->self_url;

    print $q->hr, $q->start_div({-id => "navigation"});
    print $q->a({-href  => $ref,
                 -title => "Click to Go Back"}, "Go Back"), " " if $ref;
    print $q->a({-href  => "view-source: " . $input->{uri},
                 -title => "Click to See Source RSS Feed (Opens a New Window)",
                 -target=> "_blank"}, "View RSS"), " ",
          $q->a({-href  => "view-source:$self",
                   -title => "View HTML Page Source"}, "View HTML"), " ";

    $self  .= ";cache=off" unless $self =~ /cache=off/;
    print $q->a({-href  => "$self",
                 -title => "Reload RSS Feed From Source"}, "Refresh Feed"),
          $q->end_div, $q->end_html;
}

#
#    Gracefully die
#
sub tad {
    my $error = shift || "Unkown Error";
    my $q     = shift;

    warn $error;
    print $q->hr, $q->h1("News 2 HTML Error:"), $q->h2($error), $q->hr, $q->end_html;
    %ENV = ();
    exit;
}