A while ago I mentioned a voting website I created and a home-grown REST-ful library I created that I use in the site's code. I'm thinking that the library might be useful to others, but I'd like some feedback, both on it's probable utility and on the code in the library itself.

(Update: REST = Representational State Transfer; a good place to start reading up on this, as always, is wikipedia.)

So, here goes! First, the library - REST.pm

package REST; use strict; use CGI; our $DEBUG = 0; sub new { return bless {CGI => CGI->new}, shift }; sub cgi { shift()->{CGI} } sub debug { my ($self, $urls, @problems) = @_; my $cgi = $self->cgi; print $cgi->header, $cgi->start_html, "REST::debug executing: @problems<hr>", "<dl>", (map { "<dt><tt>$_</tt></dt><dd>$urls->{$_}</dd>" } keys % +$urls), "</dl>", "Path info: <tt>", $cgi->path_info, "</tt><br>", "Request method: <tt>", $cgi->request_method, "</tt><br>", $cgi->end_html; } sub run { my ($self, %urls) = @_; my $pathInfo = $self->cgi->path_info; my $method = $self->cgi->request_method; my ($package, @pathParams); foreach my $path ( keys %urls ) { $package = $urls{$path}; if ( $pathInfo =~ $path ) { @pathParams = ($1, $2, $3, $4, $5, $6, $7, $8, $9); last; } undef $package; } if ( $package ) { my $dispatcher = $package->new($self->cgi); if ( $dispatcher->can($method) ) { $dispatcher->$method(@pathParams); return; } } if ( $DEBUG ) { $self->debug(\%urls, $@); } else { print $self->cgi->header, $self->cgi->start_html, $self->cgi->end_ +html; } } 1;

As far as how you'd use REST, the overall idea is to set up a hash of "paths" to class/package names, create a REST instance, and then invoke run on that instance. These paths are actually regular expressions, allowing you to pass paramters as part of the path. E.g., the URL http://somewhere.com/index.pl/candidate/4/vote/yes would be mapped via a path like q{/candidate/(\d+)/vote/yes}. The regular expression would match 4 and then pass that value along as a parameter to the handling function.

#!/usr/bin/perl -w # (index.pl) use strict; use REST; use HTML::Template; my %urls = ( qr{^/?$} => 'Welcome', qr{^/hello$} => 'NiceToMeetYou', qr{^/bye/(\w+)$} => 'Goodbye', ); REST->new->run(%urls);

Each class would need to implement GET or POST, or whatever other verbs as appropriate for your HTTP clients. In essence, REST.pm is a dispatcher. Your "application code" would be in classes like the following (which could either be in index.pl or use'd as appropriate):

package Renderer; # a class w/common functionality in all "application" classes sub new { my ($class, $cgi) = @_; return bless { CGI => $cgi }, $class; } sub cgi { return shift->{CGI} } sub render { my ($self, $content) = @_; my $templateText = q{ <html> <head><title>Welcome</title></head> <body><TMPL_VAR NAME='CONTENT'></body> </html> }; my $template = HTML::Template->new_scalar_ref(\$templateText); $template->param(CONTENT => $content); print $self->cgi->header(-type => 'text/html'), $template->output; } package Welcome; use base 'Renderer'; sub GET { my $self = shift; $self->render(q{ <p>Welcome. My name is Perl. What's your name?</p> <form method="POST" action="index.pl/hello"> <input type="text" name="name"> <input type="submit"> </form> }); } package NiceToMeetYou; use base 'Renderer'; sub POST { my $self = shift; my $name = $self->cgi->param('name'); # yes, it would be scrubbed in + production code $self->render(qq{ <p>Nice to meet you $name. <a href="../index.pl/bye/$name">Leaving already?</a></p> }); } package Goodbye; use base 'Renderer'; sub GET { my ($self, $name) = @_; $self->render("Goodbye, $name. It was nice visiting."); }

There are a couple ideas and possible improvemetns I have in mind. One thing I don't like is that even though this "simulates" REST-fulness, it doesn't really dispatch differently based on different MIME-types. And I don't like how index.pl (or whatever your filename is) appears in the URL in the browser; I've tried playing with mod_rewrite, but have never been able to get it just right.

So, any suggestions? Comments? If this turns out to be useful for others, I'd consider putting it on CPAN. Let me know what you all think.


In reply to RFC: REST.pm by t'mo

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.