In this screencast we build a simple blog engine using Perl Dancer. If you are on MS Windows start by downloading Padre on Strawberry Perl that already has everything we need. On other system you'll have to install Dancer, File::Slurp and the Template::Toolkit. (Please note, the last one is actually identified as Template, by the CPAN clients.)

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 view/ 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 view/index.tt file replacing all of its content with a 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 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 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.

Well, sort of.