Building a blog engine using Perl Dancer

In this screencast we build a simple blog engine using Dancer.

If you are on MS Windows start by downloading Padre on Strawberry Perl which already has everything we need. On other systems you'll have to install Dancer, File::Slurp and Template::Toolkit.

The application we build is going to be called Dwimmer. We need to use the command line to build the skeleton of our application by typing in

dancer -a Dwimmer

This will create a directory called Dwimmer and all the files necessary to build a Dancer application.

The bin/app.pl file is just a simple launcher for a web server on port 3000

The real code is in lib/Dwimmer.pm

The templates are in the views/ directory.

This is the full code:

package Dwimmer;
use Dancer ':syntax';

use File::Slurp qw(read_file write_file);

our $VERSION = '0.01';

get '/' => sub {
    my $filename = config->{dwimmer}{json};
    my $json = -e $filename ? read_file $filename : '{}';
    my $data = from_json $json;
    template 'index', {data => $data};
};


get '/page' => sub {
    template 'page';
};

post '/page' =>  sub {
    my $filename = config->{dwimmer}{json};
    my $json = -e $filename ? read_file $filename : '{}';
    my $data = from_json $json;
    my $now   = time;
    $data->{$now} = {
        title => params->{title},
        text  => params->{text},
    };

    write_file $filename, to_json($data);
    redirect '/';
};

true;

The module File::Slurp provides two functions for reading and writing files in a simple way.

Dancer is route based so you for each page out there you need to define a route. You can even differentiate between the HTTP METHOD types such as GET and POST though they are written in lower case in Dancer.

We changed the config.yml file in the root of the application. Commented out the

# template: "simple"

and enabled

template: "template_toolkit"
engines:
  template_toolkit:
    encoding:  'utf8'

Later we also added the following

dwimmer:
  json: c:\work\dwimmer.json

to set where the json "database" is located. (Our home made NoSQL database.)

We edited the file views/index.tt file replacing all of its content with simple HTML linking to the page.

Hi
<a href="/page">add a new entry</a>

clicking on that link would generate a 404-error so we also created a route to serve the request:

get '/page' => sub {
  template 'page';
};

The page template is a simple form:

<form method="POST">
<input name="title" size="80"/><br />
<textarea name="text" rows="20" cols="80">
</textarea><br />
<input type="submit" value="Post" />
</form>

Note we set the submission method to be POST! Hence when you submit this form you get a 404 error. We have to implement a separate route to handle this post request:

post '/page' =>  sub {
  my $filename = config->{dwimmer}{json};
  my $json = -e $filename ? read_file $filename : '{}';
  my $data = from_json $json;
  my $now   = time;
  $data->{$now} = {
      title => params->{title},
      text  => params->{text},
  };

  write_file $filename, to_json($data);
  redirect '/';
};

In this code the config method returns the all the configuration options where we already saved the name of the json file. -e $filename checks if the file exists. If it does, we read it in to the $json variable; if not then we assign to it a JSON string representing an empty hash.

my $json = -e $filename ? read_file $filename : '{}';

from_json is a built-in Dancer function turning a json string into a Perl data structure.

We then add a new entry to our "database" with the key being the timestamp and having a hash containing the title and text as received from the user.

write_file $filename, to_json($data);

This will write back the whole data structure after turning it into a json file.

The last line redirects the user to the main page.

This code will save the new blog submission to the json file but won't display yet.

For that we changed the main route copying the data reading code to it. It then passes the data to the template call.

get '/' => sub {
  my $filename = config->{dwimmer}{json};
  my $json = -e $filename ? read_file $filename : '{}';
  my $data = from_json $json;
  template 'index', {data => $data};
};

The index.tt template itself had to be changed as well to include this:

<% FOR e IN data.keys.sort %>
  <hr />
  <h2><% data.$e.title %></h2>
  <% data.$e.text %>
<% END %>

Which will display all the results from the data hash.

That's it. Dwimmer is ready.

Well, sort of.

AUTHOR

Gabor Szabo, <szabgab@gmail.com>