Intro to Dancer - OSCON 2011

Post on 11-Nov-2014

2.130 views 1 download

Tags:

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 of Intro to Dancer - OSCON 2011

Introduction to Dancer!A Sinatra-inspired web framework for Perl!!!Mark Allen!mrallen1@yahoo.com!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!!!