<mrallen1 -at- yahoo.com>

This talk is not
Dancer vs. $FRAMEWORK
A tutorial on web application design
A tutorial about SQL or other database technologies
It's just a straight forward introduction to what Dancer is and how it works.
Dancer is a very lightweight web framework.
The basic idea is you specify URL paths called 'routes' and write handlers for routes when requests match those routes.
Dancer is very highly inspired by Sinatra written in Ruby.
The micro web app framework concept is found on a bunch of other programming platforms, including:
Mojo (Perl)
Flask (Python)
Bottle (Python)
Fitzgerald (PHP)
Quote of the day: "Aww, cute! It's like a puppy that makes websites!"
This is a full Dancer program. It handles the '/' URL and outputs 'Hello World!\n' which is not a very interesting example.
use Dancer;
get '/' => sub {
return 'Hello World!\n';
};
start;
I was jealous of the awesome docs and examples that came with Flask, so I set out to port the 'Flaskr' demo application to Dancer so I could better understand how Dancer works.
So I ported "Dancr", a microblog. It's not too trivial, but not too hard either. Besides, the 'Hello World' of web app frameworks is Yet Another Blog.
Besides Dancer, this application uses the following (non-core) Perl modules:
Template (Template Toolkit isn't required but it's a Good Idea to use.)
This is my simple demo app; Dancer ships with a helper script to create a more robust layout automatically.
myapp/
|
+--------> myapp.pl (implements your application)
|
+--------> public/ (serves static web content)
|
+--------> views/
|
+---------------> show_stuff.tt
|
+---------------> layouts/
|
+------------------------> main.tt
Here's the simple schema for this application.
create table if not exists entries (
id integer primary key autoincrement,
title string not null,
text string not null
);
Note 'template' keyword and its parameter list.
get '/' => sub {
my $db = connect_db();
my $sql = 'select id, title, text from entries order by id desc';
my $sth = $db->prepare($sql) or die $db->errstr;
$sth->execute or die $sth->errstr;
template 'show_entries.tt', {
'msg' => get_flash(),
'add_entry_url' => uri_for('/add'),
'entries' => $sth->fetchall_hashref('id'),
};
};
Note that in Dancer, Toolkit Template directives use <% %> instead
of the default [% %] (but you can reset this if you want)
<ul class=entries>
<% IF entries.size %>
<% FOREACH id IN entries.keys.nsort %>
<li><h2><% entries.$id.title %></h2><% entries.$id.text %>
<% END %>
<% ELSE %>
<li><em>Unbelievable. No entries here so far</em>
<% END %>
</ul>
There are 8 defined HTTP verbs in RFC 2616: OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
Dancer supports (among others):
PUT (Create)
GET (Retrieve)
POST (Update)
DELETE (Delete)
Note the use of 'session' and 'send_error'
post '/add' => sub {
if ( not session('logged_in') ) {
send_error("Not logged in", 401);
}
my $db = connect_db();
my $sql = 'insert into entries (title, text) values (?, ?)';
my $sth = $db->prepare($sql) or die $db->errstr;
$sth->execute(params->{'title'}, params->{'text'}) or die $sth->errstr;
set_flash('New entry posted!');
redirect '/';
};
Here's the template for the front page.
<% IF session.logged_in %>
<form action="<% add_entry_url %>" method=post class=add-entry>
<dl>
<dt>Title:
<dd><input type=text size=30 name=title>
<dt>Text:
<dd><textarea name=text rows=5 cols=40></textarea>
<dd><input type=submit value=Share>
</dl>
</form>
<% END %>
Out of the box Dancer gives you some defaults for templating, session management and error handling, but most of these behaviors are only suitable for the most trivial of web applications.
Dancer supports two basic styles of setting application behavior. The production way to handle this is by using one or more YAML files. The quick 'n' dirty demo style is to embed configuration parameters at the top of your Dancer script.
The 'set' keyword configures Dancer behavior.
set 'session' => 'Simple';
set 'template' => 'template_toolkit';
set 'logger' => 'console';
set 'log' => 'debug';
set 'show_errors' => 1;
Settings can be any arbitrary non-reserved word key value pair.
set 'username' => 'admin';
set 'password' => 'password';
Done with the 'any' verb, and further specifying verbs in an anonymous array.
any ['get', 'post'] => '/login' => sub {
my $err;
if ( request->is_post ) {
if ( params->{'username'} ne setting('username') ) {
$err = "Invalid username";
}
elsif ( params->{'password'} ne setting('password') ) {
$err = "Invalid password";
}
else {
session 'logged_in' => true;
set_flash('You are logged in.');
redirect '/';
}
}
Send the output to the template.
any ['get', 'post'] => '/login' => sub {
my $err;
if ( request->is_post ) {
# yada yada
...;
}
template 'login.tt', {
'err' => $err,
};
};
The logout route handler.
get '/logout' => sub {
session->destroy;
set_flash('You are logged out.');
redirect '/';
};
Dancer's obviously handy for dynamic content, what about static files?
get '/' => sub {
send_file 'index.html';
};
Static files go into the "public/" directory but omit "public/" from the path
in your app. (Like for a CSS file, for example.)
You can define a layout template to use by defining one in your application like this:
layout 'main';
This will give all of your views in specific handlers a consistent look and
feel. Dancer will render views into a scalar called content which will
be inserted into your layout template as appropriate. These templates
should go into the "views/layouts/" directory and end with .tt
What's really cool is that you can use regex to match and capture URL elements...
get qr{\A\/(?<code>[A-Za-z0-9]+)\Z} => sub {
# named capture requires at least 5.10
my $decode = decode_base36(uc captures->{'code'});
...;
};
get '/:code/stats' => sub {
my $decode = decode_base36(uc params->{'code'});
...;
};
It's possible to manipulate the values which go into templates using the
before_template command. It looks like this:
before_template sub {
my $tokens = shift;
$tokens->{'css_url'} = request->base . 'css/style.css';
$tokens->{'login_url'} = uri_for('/login');
$tokens->{'logout_url'} = uri_for('/logout');
};
There are quite a few of these!
Authentication, authorization and RBAC
ORMy goodness
Generate RSS/Atom feeds from an arrayref of items.
DWIM HTML form generator based on database tables (beta)
Put it all together you end up with a flexible and fast way to build websites.
You can see the complete source code and tutorial document on my bitbucket repo here. (Feel free to clone, etc.)
See also the Dancer website. There is also a Dancer Advent calendar which has several other tutorials and examples.
This slide deck is at http://bitbucket.org/mrallen1/dancer-slides/src
I used Pod::S5 to
create this deck; the rendered version is in the
dancermpw2011/index.html file.