Using Perl as a framework for your web site

Summary:

Use Perl as a simple and powerful framework for your web site; this article will demonstrate how easy it is to get going and then customise a Plack application for your own needs.

Perl has been through a revolution over the last few years - 'Modern Perl', based on experienced and effective Perl 5 programmers knowledge, is about using the languages idioms and taking advantage of the best of CPAN.

We will be demonstrating how to create a web site using Plack, a 'Moden Perl' tool. Most Perl frameworks work with (or are migrating to) Plack, therefor it will be easy to integrate one of those later should you want to.

Plack is Perl superglue for web frameworks and web servers. Plack sits between your code (whether you use a web framework or not) and the web server (Apache, Starman, etc). This means that you (and your framework) do not need to worry about specifics of a web server, and vice-versa.

Let's get you setup... we are going to use cpanm (from App::cpanminus) to download and install modules into your local::lib (so you do not need root access)

Initial setup:

# archive of any existing cpan configuration
mv ~/.cpan ~/.cpan_original
 
# Then one of the following:

# if you can run wget
wget -O - http://cpanmin.us/ | perl - local::lib App::cpanminus && echo 'eval $(perl -I$HOME/perl5/lib/perl5 -Mlocal::lib)' >> ~/.bashrc && . ~/.bashrc
 
# OR if you can run curl
curl -L http://cpanmin.us/ | perl - local::lib App::cpanminus && echo 'eval $(perl -I$HOME/perl5/lib/perl5 -Mlocal::lib)' >> ~/.bashrc && . ~/.bashrc
 
# otherwise, download the contents of http://cpanmin.us to a file called cpanmin.us, make it executable and then run: 
./cpanmin.us local::lib App::cpanminus && echo 'eval $(perl -I$HOME/perl5/lib/perl5 -Mlocal::lib)' >> ~/.bashrc && . ~/.bashrc

Setting up Plack

    cpanm Plack
    # Please also run this as we will us it in the demonstration later
    cpanm Plack::Middleware::TemplateToolkit

The perl5 folder in your home directory will now have all the modules you need. The next step is to create a .psgi configuration file which will allow us to return a web page.

1.psgi

    # Tell Perl where our lib is (ALWAYS use this)
    use lib "$ENV{HOME}/perl5/lib/perl5";

    # ensure we declare everything correctly (ALWAYS use this)
    use strict;

    # Give us diagnostic warnings where possible (ALWAYS use this)
    use warnings;

    # Allow us to build our application
    use Plack::Builder;

    # A basic app
    my $default_app = sub {
        my $env = shift;
        return [
            200,    # HTTP Status code
            [ 'Content-Type' => 'text/html' ],    # HTTP Headers,
            ["All is good"]                       # Content
        ];
    };

    # Return the builder
    return builder {
        $default_app;
    }

Save this to a file, called 1.psgi, then use the plackup command to start your web server from the command line as follows:

    plackup 1.psgi

You will see:

    HTTP::Server::PSGI: Accepting connections at http://SERVER_IP:5000/

Using your web browser, go to http://SERVER_IP:5000/, if you are developing on your desktop computer then http://localhost:5000/ will work. You should now see a page with 'All is good'. In fact if you go to any page this is what you will see, e.g. http://localhost:5000/any_page.html because we are always returning this, irrespective of the request.

You will notice that on the command line you can see the access logs for the web server, this is because Plack defaults to development mode and turns on a few extra middleware layers for you, specifically AccessLog, StackTrace and Lint.

To see StackTrace in operation comment out line 27 of 1.psgi by adding a hash (#) in front of it:

    # ["All is good"]                       # Content

Restart your plackup command (type Ctrl+C to stop the process, then run plackup 1.psgi to start it). Now in your web browser go to http://localhost:5000/ again and you will see a StackTrace of the error. Note the main error message at the top of the page "response needs to be 3 element array, or 2 element in streaming". You can then follow each step of the trace, click on the 'Show function arguments' and 'Show lexical variables' links under any section of the trace to help debug the issue.

Remove the # and restart, so we have a working .psgi file again.

Development

There are several command line arguments to the plackup command, running perldoc plackup command will show you the documentation. The most used is '-r' or '--reload' this tells plackup to monitor you .psgi file (if you have a 'lib' directory along side your .psgi file it will also be monitored).

    plackup -r 1.psgi

Extending your application

Plack already has many useful applications which you may want to integrate with your web portal, here for example we are using Plack::App::Directory to get a directory listing and to serve it's content as static files. We will use Plack::App::URLMap to choose which URL we want to 'mount' this application on.

2.psgi

    use lib "$ENV{HOME}/perl5/lib/perl5";

    use strict;
    use warnings;
    use Plack::Builder;

    # 'mount' applications on specific URLs
    use Plack::App::URLMap;

    # Get directory listings and serve files
    use Plack::App::Directory;

    my $default_app = sub {
        my $env = shift;
        return [ 200, [ 'Content-Type' => 'text/html' ], ["All is good"] ];
    };

    # Get the Directory app, configured with a root directory
    my $dir_app = Plack::App::Directory->new( { root => "/tmp/" } )->to_app;

    # Create a mapper object
    my $mapper = Plack::App::URLMap->new();

    # mount our apps on urls
    $mapper->mount('/'    => $default_app);
    $mapper->mount('/tmp' => $dir_app);

    # extract the new overall app from the mapper
    my $app = $mapper->to_app();

    # Return the builder
    return builder {
        $app;
    }

If you visit http://localhost/tmp/ in your browser you will now see a directory listing of '/tmp/' - also not http://localhost/anything_else.html still hits the default_app which we built.

More Middleware and Apps

There are many Plack::Apps and Plack::Middleware modules available to help with common tasks. We are going to look at Plack::Middleware::TemplateToolkit, which parses files through the templating engine Template-Toolkit (TT). Images and other static content should not go through TT, so we are going to configure Plack::Middleware::Static to directly serve files with specific extensions. On top of this we want to have a nice looking page when there is a 404 (file not found), for this we will use Plack::Middleware::ErrorDocument. All the code we need to add is as follows:

    
    # A link to your htdocs root folder
    my $root = '/path/to/htdocs/';
    
    # Create a new template toolkit application (which we will default to)
    my $default_app = Plack::Middleware::TemplateToolkit->new(
        INCLUDE_PATH => $root,    # Required
    )->to_app();
    
    return builder {

        # Page to show when requested file is missing
        # this will not be processes with TT
        enable "Plack::Middleware::ErrorDocument",
            404 => "$root/page_not_found.html";

        # These files can be served directly
        enable "Plack::Middleware::Static",
            path => qr{[gif|png|jpg|swf|ico|mov|mp3|pdf|js|css]$},
            root => $root;

        # Our application
        $default_app;
    }
    

At this stage it is probably worth investigating one of the many web frameworks which offer PSGI support, so can be run with Plack. These frameworks offer far structure and support for doing more complex tasks. Have a look at Catalyst, Mojolicious or Dancer. The Perl.org web frameworks white paper discusses just a few of the advantages of using a framework.

Further resources

Perl has a vast number of modules available for use on CPAN. The only real problem being to work out which are best to use. A good place to start is Task::Kensho which recommends current best practices.

Perl Whitepapers