intro to dancer - oscon 2011
DESCRIPTION
Many developers who’ve left Perl behind still think that Matt’s Script Archive is state of the art web development techniques for Perl. This talk is intended to showcase some of the most exciting new developments and frameworks for Perl since Catalyst 5.8 was released a few years ago. With a short learning curve and a lot of power under the hood, Dancer deserves a broad audience of developers, including those who may have written off Perl as a relevant language for serious web development.TRANSCRIPT
Introduction to Dancer!A Sinatra-inspired web framework for Perl!!!Mark [email protected]!http://byte-me.org!http://github.com/mrallen1!http://bitbucket.org/mrallen1!https://metacpan.org/author/MALLEN!
The Old Days!
The Old Days
cgi-lib.pl (1994-1998)!!
The Old Days
CGI.pm (1995-2011)!!
The Old Days
mod_perl (1996-2011)!!
The Old Days
Matt’s Script Archive (1995-2011)!
!Use instead:!
http://nms-cgi.sourceforge.net/!(Thanks London.pm)!
!
The Old Days
use CGI.pm;!!my $q = CGI->new();!my ($a, $b, $c) = $q->param();!!if ($a !~ /\d+/) {!!print $q->header(-status => ‘400 Bad request’);!!print “<h1>Value of ‘a’ ($a) is not an integer</h1>”;!!exit 0;!
}!# yada, yada!
The Old Days
.htaccess Hack!!!RewriteEngine On!RewriteBase /abc!!RewriteRule ^(.*)/(.*)/(.*)$ !abc.pl?a=$1;b=$2;c=$3!!
The Old Days
.htaccess Hack!!# Use Web 2.0 style URLs, but keep CGI interface!RewriteEngine On!RewriteBase /abc!!RewriteRule ^(.*)/(.*)/(.*)$ !abc.pl?a=$1;b=$2;c=$3!!
The Old Days
.htaccess Hack!!# Use Web 2.0 style URLs, but keep CGI interface!RewriteEngine On!RewriteBase /abc!!RewriteRule ^(.*)/(.*)/(.*)$ !abc.pl?a=$1;b=$2;c=$3!!
The Old Days
.htaccess Hack!!# Use Web 2.0 style URLs, but keep CGI interface!RewriteEngine On!RewriteBase /abc!!RewriteRule ^(.*)/(.*)/(.*)$ !abc.pl?a=$1;b=$2;c=$3!!
The Old Days
h"p://flic.kr/p/5TWTR4
The Old Days
h"p://flic.kr/p/5TWTR4
Move over bacon, now there’s something leaner.!
!
Move over bacon
Perl 5 Renaissance:!• Moose!• DBIx::Class!• Plack!• Tons of great new stuff in
core perl 5.10+!!!
Move over bacon
Do you find Catalyst too !heavyweight or intimidating?!
!
Move over bacon
!Me too!!
!!
Move over bacon
Enter Dancer!!
Move over bacon
Other Sinatra-inspired frameworks:!• Mojo (Perl – TMTOWTDI)!• Flask (Python)!• Fitzgerald (PHP)!• Express (Node.js)!!
Move over bacon
Dancer is:!• Easy to learn!• Expressive!• Efficient!
Dancer is ...!!
Easy to learn!
Easy to learn
Basic Dancer concepts:!• One (or more) HTTP verb(s)!• A route!• A handler!
Easy to learn
use Dancer;!!get '/' => sub {! return 'Hello World!\n’;!};!!start;!
Easy to learn
use Dancer;!!get '/' => sub {! return 'Hello World!\n’;!};!!start;!
Easy to learn
use Dancer;!!get '/' => sub {! return 'Hello World!\n’;!};!!start;!
Easy to learn
use Dancer;!!get '/' => sub {! return 'Hello World!\n’;!};!!start;!
Easy to learn
use Dancer;!!get '/' => sub {! return 'Hello World!\n’;!};!!start;!
Easy to learn
Easy to learn
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'),! };!};!
Easy to learn
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'),! };!};!
Easy to learn
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'),! };!};!
Easy to learn
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'),! };!};!
Easy to learn
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'),! };!};!
Easy to learn
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'),! };!};!
Easy to learn
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'),! };!};!
Easy to learn
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'),! };!};!
Easy to learn
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'),! };!};!
Easy to learn
show_entries.tt!!<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>!!
Easy to learn
show_entries.tt!!<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>!!
Easy to learn
show_entries.tt!!<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>!!
Easy to learn
show_entries.tt!!<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>!!
Easy to learn
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'),! };!};!
Easy to learn
show_entries.tt!!<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>!!
Easy to learn
show_entries.tt!!<ul class=entries> <!– CSS class -->! <% 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>!!
Easy to learn
Directory structure !
dancr/!├── dancr.pl!├── public!│ └── css!│ └── style.css!└── views! ├── layouts! │ └── main.tt! ├── login.tt! └── show_entries.tt!
Easy to learn
Directory structure !
dancr/!├── dancr.pl!├── public!│ └── css!│ └── style.css!└── views! ├── layouts! │ └── main.tt! ├── login.tt! └── show_entries.tt!
Easy to learn
Directory structure !
dancr/!├── dancr.pl!├── public!│ └── css!│ └── style.css!└── views! ├── layouts! │ └── main.tt! ├── login.tt! └── show_entries.tt!
Easy to learn
Directory structure !
dancr/!├── dancr.pl!├── public!│ └── css!│ └── style.css!└── views! ├── layouts! │ └── main.tt! ├── login.tt! └── show_entries.tt!
Dancer is...!!
Expressive!
Expressive
post '/add' => sub {! if ( not session('logged_in') ) {! return 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 '/’;!};!
Expressive
post '/add' => sub {! if ( not session('logged_in') ) {! return 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 '/’;!};!
Expressive
post '/add' => sub {! if ( not session('logged_in') ) {! return 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 '/’;!};!
Expressive
post '/add' => sub {! if ( not session('logged_in') ) {! return 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 '/’;!};!
Expressive
post '/add' => sub {! if ( not session('logged_in') ) {! return 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 '/’;!};!
Expressive
post '/add' => sub {! if ( not session('logged_in') ) {! return 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 '/’;!};!
Expressive
post '/add' => sub {! if ( not session('logged_in') ) {! return 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 '/’;!};!
Expressive
Little Bobby Tables!
h"p://xkcd.com/327/
Expressive
post '/add' => sub {! if ( not session('logged_in') ) {! return send_error("Not logged in", 401);! }! # http://bobby-tables.com! 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 '/’;!};!
Expressive
Expressive
Configuration !• Session Engine!• Template Engine!• Logging !• Plugins!
Expressive
Configuration Examples!set 'session' => 'Simple'; set 'template' =>'template_toolkit'; set 'logger' => 'console'; set 'log' => 'debug'; set 'show_errors' => 1; !
Expressive
Configuration Examples!set 'session' => 'Simple'; set 'template' =>'template_toolkit'; set 'logger' => 'console'; set 'log' => 'debug'; set 'show_errors' => 1; !
Expressive
Configuration Use any non-reserved key/value pair.set 'username' => 'admin'; set 'password' => 'password';!!
Expressive
Routes with regex!!get qr{\A\/(?<code>[A-Za-z0-9]+)\Z} => sub {! my $decode = decode_base36( uc captures->{'code'}! );! ...;!};!
Expressive
Routes with regex!!get qr{\A\/(?<code>[A-Za-z0-9]+)\Z} => sub {! my $decode = decode_base36( uc captures->{'code'}! );! ...;!};!
Expressive
Routes with regex!!use v5.10; get qr{\A\/(?<code>[A-Za-z0-9]+)\Z} => sub {! my $decode = decode_base36( uc captures->{'code'}! );! ...;!};!
Expressive
Routes with regex!!use v5.10; get qr{\A\/(?<code>[A-Za-z0-9]+)\Z} => sub {! my $decode = decode_base36( uc captures->{'code'}! );! ...;!};!
Expressive
Routes with named params!!get '/:code/stats' => sub {! my $decode = decode_base36( uc params->{'code'} );! ...;!};!
Expressive
Routes with named params!!get '/:code/stats' => sub {! my $decode = decode_base36( uc params->{'code'} );! ...;!};!
Expressive
Routes with named params!!get qr{^/(.+)/stats$} => sub { params(‘code’) = $1; # input sanity goes here ...; };!
Expressive
use CGI.pm;!!my $q = CGI->new();!my ($a, $b, $c) = $q->param();!!if ($a !~ /\d+/) {!!print $q->header(-status => ‘400 Bad request’);!!print “<h1>Value of ‘a’ ($a) is not an integer</h1>”;!!exit 0;!
}!# yada, yada!
Expressive
!!!!use v5.10; use Dancer; !get qr{/(?<a>\d+)/(?<b>.+)/(?<c>.+)} => sub {! # Route will only match if a is an integer! # No more sanitation/validation necessary ...; };!!
Dancer is...!
Efficient!
Efficient
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 '/’;! }! }! template 'login.tt', {! 'err' => $err,! };!};!
Efficient
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 '/’;! }! }! template 'login.tt', {! 'err' => $err,! };!};!
Efficient
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 '/’;! }! }! template 'login.tt', {! 'err' => $err,! };!};!
Efficient
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 '/’;! }! }! template 'login.tt', {! 'err' => $err,! };!};!
Efficient
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 '/’;! }! }! template 'login.tt', {! 'err' => $err,! };!};!
Efficient
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 '/’;! }! }! template 'login.tt', {! 'err' => $err,! };!};!
Efficient
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 '/’;! }! }! template 'login.tt', {! 'err' => $err,! };!};!
Efficient
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 '/’;! }! }! template 'login.tt', {! 'err' => $err,! };!};!
Efficient
Hook Before / After!• De/Serialization!• Rendering!• Template!• Errors!
Efficient
Forward!!get '/demo/foo/:bar' => sub {! my $bar = param->{'bar'};! # magically use demo DB! ...;! return forward "/foo/$bar";!};! !
Efficient
Prefix!!prefix '/user'; # begins scope!!get '/view/:id' => sub { ...; };!post '/add' => sub { ...; };!any ['get', 'post'] => '/edit/:id' => sub { ...; };!!prefix undef; # ends scope!!get '/view/:id' => sub { ...; };!
Efficient
Dancer Plugins add keywords!• Auto-create RSS feeds!• Integrate Twitter for authN!• DBIx::Class!• DWIM CRUD apps!• ...and more!
Dancer is...!!
Awesome!!
Awesome
Source code:!hg clone https://bitbucket.org/mrallen1/dancr!!
See also:!https://metacpan.org/module/Dancer!!
Install:!curl -L http://cpanmin.us | perl - Dancer YAML Template !
!!
Awesome
Thank You!!!