Dancer Web Framework

Mark Allen
<mrallen1 -at- yahoo.com>

Website - Bitbucket

Frozen Perl 2011

February 5, 2011

Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 Unported License.

What this talk is not

This talk is not

It's just a straight forward introduction to what Dancer is and how it works.

What is Dancer?

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.

What else is out there?

The micro web app framework concept is found on a bunch of other programming platforms, including:

Quote of the day: "Aww, cute! It's like a puppy that makes websites!"

Hello World

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;

A more interesting example

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.

Screenshot

Prerequisites

Besides Dancer, this application uses the following (non-core) Perl modules:

Basic filesystem layout

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

Database model

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
  );

First route handler

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($sqlor 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'),
    };
  };

show_entries Template

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>

CRUDdy HTTP Verbs

There are 8 defined HTTP verbs in RFC 2616: OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT

Dancer supports (among others):

Adding a new post

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($sqlor die $db->errstr;
    $sth->execute(params->{'title'}, params->{'text'}) or die $sth->errstr;

    set_flash('New entry posted!');
    redirect '/';
  };

Showing entries on the front page

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 %>

Configuration options

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.

More configuration

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';

Handling more than one verb in your route

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 '/';
      }
  }

Cont'd

Send the output to the template.


  any ['get''post'] => '/login' => sub {
    my $err;
    if ( request->is_post ) {
      # yada yada
      ...;
    } 

    template 'login.tt', {
      'err' => $err,
    };
  };

Logout

The logout route handler.


  get '/logout' => sub {
    session->destroy;
    set_flash('You are logged out.');
    redirect '/';
  };

Serving static files

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.)

Using a layout template

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

Advanced routes

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'});
    ...;
  };

Modifying template variables before rendering

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');
  };

Some Dancer plugins

There are quite a few of these!

  • Dancer::Plugin::Authorize

    Authentication, authorization and RBAC

  • Dancer::Plugin::DBIC

    ORMy goodness

  • Dancer::Plugin::Feed

    Generate RSS/Atom feeds from an arrayref of items.

  • Dancer::Plugin::SimpleCRUD

    DWIM HTML form generator based on database tables (beta)

  • Microblog in 100 lines of code

    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.

    Questions?

    Thank you!

    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.