*some noise about that new Dancer web framework*
"Meh. Catalyst is good. Me likes Catalyst."
*noise doesn't die down*
"Maybe I should try, just for the sake of knowing what it's like."
Go on hacking Dancer::Template::Mason
"Oh geez, is that a Dancer bug? Let's submit a patch!"
"So there's this guy who keep sending patches. What should we do to get rid of him?"
"Make him part of the core team?"
"Brilliant! That's a plan that can't possibly backfire!"
Provide the basic frame to build websites.
Intuitive, minimalist, expressive syntax, few dependencies, fast
Rather than all-encompassing, rigid, heavy
Small != Trivial (It's going to be awesomely perfect for 95% of web apps)
Treasure trove of plugins + templates
Serializing, sessions, cookies, all dealt with
Hooks
REST/Ajax-friendly
Development-friendly (standalone server, debugging facilities)
Project with many templating systems
Project touching many teams and must have rigid MVC structure
Lots of action chaining
Many apps must run under the same interpreter (Dancer 2 fixes that)
Project is godawfully complex with lots of eldritch magic under the hood
GET /welcome -> send welcome web page
PUT /user/(username) -> set user info
...
Template support baked-in. (View)
Model/Controller can be mashed together. Or not.
Insanely flexible
one-stop solution to everything (yes, everything)
# Can I Have the First Dance? * Markdown: cool for slides ```html # Can I Have the First Dance? * Markdown: cool for slides ```html Present-ception! ``` * Markdown-to-html web app! ``` * Markdown-to-html web app!
#!/usr/bin/env perl use 5.10.0; use strict; use warnings;
#!/usr/bin/env perl use 5.10.0; use strict; use warnings; use Text::Markdown 'markdown'; my $webpage = markdown( join '', <> );
#!/usr/bin/env perl use 5.10.0; use strict; use warnings; use Text::Markdown 'markdown'; use Dancer ':syntax'; my $webpage = markdown( join '', <> ); get '/' => sub { $webpage }; dance;
$ perl micro_chorus.pl prez.mkd >> Dancer 1.3115 server 17163 listening on http://0.0.0.0:3000 == Entering the development dance floor ...
$ sudo apt-get libdancer-perl or $ cpan Dancer or $ cpanm Dancer (using cpanminus - https://metacpan.org/release/App::cpanminus) or $ git clone http://github.com/perldancer/Dancer $ cd Dancer $ perl Makefile.PL && make test && make install
$ dancer -a my_app + my_app + my_app/bin + my_app/bin/app.pl + my_app/config.yml + my_app/environments + my_app/environments/development.yml + my_app/environments/production.yml + my_app/views + my_app/views/index.tt + my_app/views/layouts + my_app/views/layouts/main.tt + my_app/MANIFEST.SKIP + my_app/lib + my_app/lib/ + my_app/lib/mww.pm + my_app/public + my_app/public/css + my_app/public/css/style.css + my_app/public/css/error.css + my_app/public/images + my_app/public/500.html + my_app/public/404.html + my_app/public/dispatch.fcgi + my_app/public/dispatch.cgi + my_app/public/javascripts + my_app/public/javascripts/jquery.js + my_app/t + my_app/t/002_index_route.t + my_app/t/001_base.t + my_app/Makefile.PL
#!/usr/bin/env perl use Dancer; use Foo; dance;
+ my_app/config.yml + my_app/environments/development.yml + my_app/environments/production.yml
appname: "Foo" layout: "main" charset: "UTF-8" template: "simple" # template: "template_toolkit" # engines: # template_toolkit: # start_tag: '[%' # end_tag: '%]'
logger: "console" log: "core" warnings: 1 show_errors: 1 auto_reload: 0
+ my_app/views + my_app/views/index.tt + my_app/views/layouts + my_app/views/layouts/main.tt
Typical template system: Template Toolkit
package Foo; use Dancer ':syntax'; our $VERSION = '0.1'; get '/' => sub { template 'index'; }; true;
+ my_app/public/css/style.css + my_app/public/css/error.css + my_app/public/images + my_app/public/500.html + my_app/public/404.html + my_app/public/dispatch.fcgi + my_app/public/dispatch.cgi + my_app/public/javascripts + my_app/public/javascripts/jquery.js
Comes with batteries included
Ready for CGI and FCGI
+ my_app/t/001_base.t + my_app/t/002_index_route.t
+ my_app/MANIFEST.SKIP + my_app/Makefile.PL
$ ./bin/app.pl [3481] core @0.000016> loading Dancer::Handler::Standalone handler [3481] core @0.000333> loading handler 'Dancer::Handler::Standalone' >> Dancer 1.3071 server 3481 listening on http://0.0.0.0:3000 == Entering the development dance floor ...
$ ./bin/app.pl
$ plackup --server Twiggy --port 9090 --host 127.0.0.1 ./bin/app.pl $ plackup --server Starman --port 9090 --host 127.0.0.1 ./bin/app.pl
$ plackup -s FCGI --listen /tmp/app.fcgi ./bin/app.pl
AddHandler cgi-script .cgi ScriptAlias / /path/to/app/public/dispatch.cgi/
package MyApp; use Dancer ':syntax'; our $VERSION = '0.1'; get '/' => sub { template 'index'; }; true;
get '/' => sub { return "hello there!"; };
Verbs: get, post, del, put, post
get '/about' => sub { return 'this is an app'; }; post '/man' => sub { "I've been known to ring twice"; }; any [ qw/ get post put del / ] => '/rome' => sub { 'You reached Rome.' };
get '/login/:name' => sub { 'Hi ' . param( 'name' ); };
matches /login/alice
but not /login/foo/bar
get '/path/*/*' => sub { my ( $from, $to ) = splat; return "going from $from to $to"; }
matches /path/here/there
but not /path/here/there/anywhere
or /path/finder
get '/tags/**' => sub { my ( $tags ) = splat; return 'tags selected: ' . join ',', @$tag; }
matches /tags/dancer
and /tags/dancer/web/stuff
put qr#^/plate/(cake|pie)/slice/(\d+)$# => sub { my( $food, $slices ) = splat; return "yum, delicious $food " x $slices; };
matches /plate/cake/slice/1
but not /plate/icecream/slice/3
prefix '/foo'; get '/bar' => sub { ... };
get '/user/:name' => sub { # yanick is "special" pass if param('name') eq 'yanick'; ... }; get '/user/yanick' => sub { ... };
post '/movie/:title' => sub { halt("disk is full") if disk_usage() > 0.90; ... };
send_error "wut?", 404;
get '/puppy' => sub { my $file = $puppy_pic[rand @puppy_pic]; send_file $file; };
post '/upload' => sub { my $up = upload('foo'); my $content = $up->content; ... };
get '/this' => sub { forward '/that'; };
get '/search/:term' => sub { redirect 'http://google.ca/?s=' . param('term'); };
get '/about' => sub { return q{ <html> <body> <h1>This is my app</h1> </body> </html> }; };
get '/about' => sub { template 'about' => { app_name => 'Milky Way War' }; };
<%args> $game_name </%args> <html> <body> <h1><% $game_name %></h1> </body> </html>
<%args> $game_name </%args> <h1><% $game_name %></h1>
config.yml
template: mason engines: mason: default_escape_flags: ['h']
set serializer => 'JSON'; get '/webpage' => sub { return "totally normal webpage"; }; get '/ajaxy' => sub { my $struct = { name => 'Sammy', seals_broken => [ 3, 6, 66 ], }; return $struct; }; # PUT "{ name: 'Dean' }" put '/ajaxy' => sub { my $name = request->body->{name}; return "Hi $name"; };
set serializer => 'Mutable'; get '/ajaxy' => sub { my $struct = { name => 'Sammy', seals_broken => [ 3, 6, 66 ], }; return $struct; }; # PUT "{ name: 'Dean' }" put '/ajaxy' => sub { my $name = request->body->{name}; return "Hi $name"; };
use Dancer::Plugin::REST; prepare_serializer_for_format; resource '/ajaxy' => get => sub { ... }, create => sub { ... }, delete => sub { ... }, update => sub { ... };
resource '/user' => get => sub { my $user = find_user( param('id') ) or returns status_not_found( "all sad :-(" ); return status_ok({ name => $user->name, age => $user->age, }); };
Dancer::Plugin::*
config file:
plugins: 'Cache::CHI': driver: Memory global: 1 expires_in: 3600
in the code:
use Dancer::Plugin::Cache::CHI get '/expensive/action' => sub { # lots of processing-intensive stuff cache_page template 'expensive'; }
use Dancer::Plugin::MobileDevice; get '/' => sub { set layout => 'mobile' if is_mobile_device; ... };
hook before => sub { set layout => 'mobile' if is_mobile_device; };
use Test::More tests => 3; use strict; use warnings; use mww; use Dancer::Test; route_exists [GET => '/']; response_status_is ['GET' => '/'], 200; response_content_like [ 'GET' => '/timestamp' ], qr/stardate \d{16}/;
Perl Web Server Gateway Interface Specification
Inspired by Ruby's Rack and Python's WSGI.
# syntax: perl
my $app = sub {
my $env = shift;
return [
'200',
[ 'Content-Type' => 'text/plain' ],
[ "Hello World" ], # or IO::Handle-like object
];
};
PSGI toolkit and middleware.