Slide 1

Slide 1 text

mojolicious next-generation real-time web framework intro to

Slide 2

Slide 2 text

Full framework not a micro-framework client and server

Slide 3

Slide 3 text

No Dependencies* Full framework *IO::Socket::SSL, IO::Socket::INET6 not a micro-framework client and server

Slide 4

Slide 4 text

No Dependencies* Full framework *IO::Socket::SSL, IO::Socket::INET6 not a micro-framework client and server Bleeding edge websockets, non-blocking events, concurrent requests, IPv6

Slide 5

Slide 5 text

No Dependencies* Full framework Pretty not a micro-framework client and server *IO::Socket::SSL, IO::Socket::INET6 API, presentation, docs, installation Bleeding edge websockets, non-blocking events, concurrent requests, IPv6

Slide 6

Slide 6 text

No Dependencies* Full framework *IO::Socket::SSL, IO::Socket::INET6 not a micro-framework client and server Bleeding edge websockets, non-blocking events, concurrent requests, IPv6 Pretty API, presentation, docs, installation

Slide 7

Slide 7 text

client

Slide 8

Slide 8 text

client Mojo::UserAgent

Slide 9

Slide 9 text

my $ua = Mojo::UserAgent->new; print $ua->get('http://mojolicio.us') ->res->body; DOM Parsing / CSS Selectors

Slide 10

Slide 10 text

my $ua = Mojo::UserAgent->new; print $ua->get('http://mojolicio.us') ->res->dom; DOM Parsing / CSS Selectors

Slide 11

Slide 11 text

my $ua = Mojo::UserAgent->new; print $ua->get('http://mojolicio.us') ->res->dom-> nd('#introduction ul li: rst-child'); DOM Parsing / CSS Selectors

Slide 12

Slide 12 text

my $ua = Mojo::UserAgent->new; print $ua->get('http://mojolicio.us') ->res->dom-> nd('#introduction ul li: rst-child');
  • An amazing real-time web framework supporting a simpli ed single le mode through Mojolicious::Lite.
    ...
  • Very clean, portable and Object Oriented pure Perl API without any hidden magic and no requirements besides Perl 5.10.1 (although 5.12+ ... DOM Parsing / CSS Selectors

Slide 13

Slide 13 text

my $ua = Mojo::UserAgent->new; print $ua->get('http://mojolicio.us') ->res->dom-> nd('#introduction ul li: rst-child'); DOM Parsing / CSS Selectors
  • An amazing real-time web framework supporting a simpli ed single le mode through Mojolicious::Lite.
    ...
  • Very clean, portable and Object Oriented pure Perl API without any hidden magic and no requirements besides Perl 5.10.1 (although 5.12+ ...

Slide 14

Slide 14 text

my $ua = Mojo::UserAgent->new; print $ua->get('search.twitter.com/search.json?q=mojolicious') ->res->json; { "completed_in":0.04, "max_id":146706880847294465, "max_id_str":"146706880847294465", "page":1, "query":"mojocasts", "refresh_url":"?since_id=146706880847294465&q=mojocasts", "results":[ { "text":"If you're thinking about #Perl on the web, you should check out Mojolicious: http://t.co/Wisop9X0" "created_at":"Tue, 13 Dec 2011 21:43:40 +0000", "from_user":"reyjrar", ... JSON Parsing / JSON Pointers

Slide 15

Slide 15 text

my $ua = Mojo::UserAgent->new; print $ua->get('search.twitter.com/search.json?q=mojolicious') ->res->json->{results}->[0]->{text}; { "completed_in":0.04, "max_id":146706880847294465, "max_id_str":"146706880847294465", "page":1, "query":"mojocasts", "refresh_url":"?since_id=146706880847294465&q=mojocasts", "results":[ { "text":"If you're thinking about #Perl on the web, you should check out Mojolicious: http://t.co/Wisop9X0" "created_at":"Tue, 13 Dec 2011 21:43:40 +0000", "from_user":"reyjrar", ... JSON Parsing / JSON Pointers

Slide 16

Slide 16 text

my $ua = Mojo::UserAgent->new; print $ua->get('search.twitter.com/search.json?q=mojolicious') ->res->json->{results}->[0]->{text}; JSON Parsing / JSON Pointers { "completed_in":0.04, "max_id":146706880847294465, "max_id_str":"146706880847294465", "page":1, "query":"mojocasts", "refresh_url":"?since_id=146706880847294465&q=mojocasts", "results":[ { "text":"If you're thinking about #Perl on the web, you should check out Mojolicious: http://t.co/Wisop9X0" "created_at":"Tue, 13 Dec 2011 21:43:40 +0000", "from_user":"reyjrar", ...

Slide 17

Slide 17 text

my $ua = Mojo::UserAgent->new; print $ua->get('search.twitter.com/search.json?q=mojolicious') ->res->json(‘/results/0/text’); JSON Parsing / JSON Pointers { "completed_in":0.04, "max_id":146706880847294465, "max_id_str":"146706880847294465", "page":1, "query":"mojocasts", "refresh_url":"?since_id=146706880847294465&q=mojocasts", "results":[ { "text":"If you're thinking about #Perl on the web, you should check out Mojolicious: http://t.co/Wisop9X0" "created_at":"Tue, 13 Dec 2011 21:43:40 +0000", "from_user":"reyjrar", ...

Slide 18

Slide 18 text

Easy debugging my $ua = Mojo::UserAgent->new; print $ua->get('search.twitter.com/search.json?q=mojolicious') ->res->json(‘/results/0/text’);

Slide 19

Slide 19 text

Easy debugging }; get '/' => sub { shift->render('home/index'); }; get '/blogs' => sub { shift->redirect_to('/blogs/tag/personal'); }; get '/blogs/tag/(*tags)' => sub { my $self = shift; # Specified tags to search for: /tag/one/tag/two/tag/three my @tags = grep $_ ne 'tag' => split '/' => $self->param('tags'); my @blogs = $self->db->resultset('Blog')->by_tags(@tags) or return $self->redirect_to('/blogs'); $self->render('blogs/index', blogs => [@blogs],); }; get '/blogs/(:name)' => sub { my $self = shift; my $param = $self->stash('name'); my $blog = $self->db->resultset('Blog')->by_id_or_name($param) or return $self->redirect_to('/blogs'); $self->render('blogs/show', blog => $blog); }; get '/photos' => sub { my $self = shift; $self->render( 'photos/index', sets => [$self->db->resultset('Photoset')->search], photo_count => $self->db->resultset('Photo')->count ); }; get '/photos/:id' => (photoset => '') => sub { my $self = shift; my $id = $self->param('id'); my $set = $self->db->resultset('Photoset')->by_id_or_name($id) or $self->redirect_to("/photos"), return; $self->render(template => 'photos/show_set', set => $set); }; get '/photos/:id' => sub { my $self = shift; my $id = $self->param('id'); my $photo = $self->db->resultset('Photo')->find($id) or $self->redirect_to("/photos"), return; $self->render(template => 'photos/show', photo => $photo); }; app->start; __DATA__ @@ layouts/default.html.ep var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-29402272-1']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })();
% if ($self->req->url ne '/') {
% }
<%= content %>
# Requested id is a photoset? app->routes->add_condition( photoset => sub { my ($r, $c, $captures, $pattern) = @_; my $id = $captures->{id}; return 1 if $id !~ /^\d+$/ or $id =~ /^\d+$/ and length $id == 17; } ); helper db => sub { return Schema->connect('dbi:SQLite:' . ($ENV{TEST_DB} || 'test.db')); }; get '/' => sub { shift->render('home/index'); }; get '/blogs' => sub { shift->redirect_to('/blogs/tag/personal'); }; get '/blogs/tag/(*tags)' => sub { my $self = shift; # Specified tags to search for: /tag/one/tag/two/tag/three my @tags = grep $_ ne 'tag' => split '/' => $self->param('tags'); my @blogs = $self->db->resultset('Blog')->by_tags(@tags) or return $self->redirect_to('/blogs'); $self->render('blogs/index', blogs => [@blogs],); }; get '/blogs/(:name)' => sub { my $self = shift; my $param = $self->stash('name'); my $blog = $self->db->resultset('Blog')->by_id_or_name($param) or return $self->redirect_to('/blogs'); $self->render('blogs/show', blog => $blog); }; get '/photos' => sub { my $self = shift; $self->render( 'photos/index', sets => [$self->db->resultset('Photoset')->search], photo_count => $self->db->resultset('Photo')->count ); };#!/usr/bin/env perl use Mojolicious::Lite; #!/usr/bin/env perl use Mojolicious::Lite; use lib 'lib'; use Schema; # Requested id is a photoset? app->routes->add_condition( photoset => sub { my ($r, $c, $captures, $pattern) = @_; my $id = $captures->{id}; return 1 if $id !~ /^\d+$/ or $id =~ /^\d+$/ and length $id == 17; } ); my $ua = Mojo::UserAgent->new; my $res = $ua->get('search.twitter.com/search.json?q=mojolicious') ->res->json(‘/results/0/text’); get '/' => sub { shift->render('home/index'); }; get '/blogs' => sub { shift->redirect_to('/blogs/tag/personal'); }; get '/blogs/tag/(*tags)' => sub { my $self = shift; # Specified tags to search for: /tag/one/tag/two/tag/three my @tags = grep $_ ne 'tag' => split '/' => $self->param('tags'); my @blogs = $self->db->resultset('Blog')->by_tags(@tags) or return $self->redirect_to('/blogs'); $self->render('blogs/index', blogs => [@blogs],); }; get '/blogs/(:name)' => sub { my $self = shift; my $param = $self->stash('name'); my $blog = $self->db->resultset('Blog')->by_id_or_name($param) or return $self->redirect_to('/blogs'); $self->render('blogs/show', blog => $blog); }; get '/photos' => sub { my $self = shift; get '/blogs/tag/(*tags)' => sub { my $self = shift; # Specified tags to search for: /tag/one/tag/two/tag/three my @tags = grep $_ ne 'tag' => split '/' => $self->param('tags'); my @blogs = $self->db->resultset('Blog')->by_tags(@tags) or return $self->redirect_to('/blogs'); $self->render('blogs/index', blogs => [@blogs],); }; get '/blogs/(:name)' => sub { my $self = shift; my $param = $self->stash('name'); my $blog = $self->db->resultset('Blog')->by_id_or_name($param) or return $self->redirect_to('/blogs'); $self->render('blogs/show', blog => $blog); }; get '/photos' => sub { my $self = shift; $self->render( 'photos/index', sets => [$self->db->resultset('Photoset')->search], photo_count => $self->db->resultset('Photo')->count ); }; get '/photos/:id' => (photoset => '') => sub { my $self = shift; my $id = $self->param('id'); my $set = $self->db->resultset('Photoset')->by_id_or_name($id) or $self->redirect_to("/photos"), return; $self->render(template => 'photos/show_set', set => $set); }; get '/photos/:id' => sub { my $self = shift; my $id = $self->param('id'); my $photo = $self->db->resultset('Photo')->find($id) or $self->redirect_to("/photos"), return; $self->render(template => 'photos/show', photo => $photo); }; app->start; __DATA__ @@ layouts/default.html.ep var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-29402272-1']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })();
% if ($self->req->url ne '/') {
% }
<%= content %>
  • Mojolicious
  • Mojocasts
  • Mojo Example
  • Github
  • my $ua = Mojo::UserAgent->new; my $res = $ua->get('search.twitter.com/search.json?q=mojolicious') ->res->json(‘/results/0/text’); use Schema; # Requested id is a photoset? app->routes->add_condition( photoset => sub { my ($r, $c, $captures, $pattern) = @_; my $id = $captures->{id}; return 1 if $id !~ /^\d+$/ or $id =~ /^\d+$/ and length $id == 17; } ); helper db => sub { return Schema->connect('dbi:SQLite:' . ($ENV{TEST_DB} || 'test.db')); }; get '/' => sub { shift->render('home/index'); }; get '/blogs' => sub { shift->redirect_to('/blogs/tag/personal'); }; get '/blogs/tag/(*tags)' => sub { my $self = shift; # Specified tags to search for: /tag/one/tag/two/tag/three my @tags = grep $_ ne 'tag' => split '/' => $self->param('tags'); my @blogs = $self->db->resultset('Blog')->by_tags(@tags) or return $self->redirect_to('/blogs'); $self->render('blogs/index', blogs => [@blogs],); }; get '/blogs/(:name)' => sub { my $self = shift; my $param = $self->stash('name'); my $blog = $self->db->resultset('Blog')->by_id_or_name($param) or return $self->redirect_to('/blogs'); $self->render('blogs/show', blog => $blog); }; get '/photos' => sub { my $self = shift; $self->render( 'photos/index', sets => [$self->db->resultset('Photoset')->search], photo_count => $self->db->resultset('Photo')->count ); };#!/usr/bin/env perl use Mojolicious::Lite; #!/usr/bin/env perl use Mojolicious::Lite; use lib 'lib'; use Schema; # Requested id is a photoset? app->routes->add_condition( photoset => sub { my ($r, $c, $captures, $pattern) = @_; my $id = $captures->{id}; return 1 if $id !~ /^\d+$/ or $id =~ /^\d+$/ and length $id == 17; } ); helper db => sub { return Schema->connect('dbi:SQLite:' . ($ENV{TEST_DB} || 'test.db')); }; get '/' => sub { shift->render('home/index'); }; get '/blogs' => sub { shift->redirect_to('/blogs/tag/personal'); }; get '/blogs/tag/(*tags)' => sub { my $self = shift; # Specified tags to search for: /tag/one/tag/two/tag/three my @tags = grep $_ ne 'tag' => split '/' => $self->param('tags'); my @blogs = $self->db->resultset('Blog')->by_tags(@tags) or return $self->redirect_to('/blogs'); $self->render('blogs/index', blogs => [@blogs],); }; get '/blogs/(:name)' => sub { my $self = shift; my $param = $self->stash('name'); my $blog = $self->db->resultset('Blog')->by_id_or_name($param) or return $self->redirect_to('/blogs'); $self->render('blogs/show', blog => $blog); }; get '/photos' => sub { my $self = shift;

Slide 20

Slide 20 text

Easy debugging }; get '/' => sub { shift->render('home/index'); }; get '/blogs' => sub { shift->redirect_to('/blogs/tag/personal'); }; get '/blogs/tag/(*tags)' => sub { my $self = shift; # Specified tags to search for: /tag/one/tag/two/tag/three my @tags = grep $_ ne 'tag' => split '/' => $self->param('tags'); my @blogs = $self->db->resultset('Blog')->by_tags(@tags) or return $self->redirect_to('/blogs'); $self->render('blogs/index', blogs => [@blogs],); }; get '/blogs/(:name)' => sub { my $self = shift; my $param = $self->stash('name'); my $blog = $self->db->resultset('Blog')->by_id_or_name($param) or return $self->redirect_to('/blogs'); $self->render('blogs/show', blog => $blog); }; get '/photos' => sub { my $self = shift; $self->render( 'photos/index', sets => [$self->db->resultset('Photoset')->search], photo_count => $self->db->resultset('Photo')->count ); }; get '/photos/:id' => (photoset => '') => sub { my $self = shift; my $id = $self->param('id'); my $set = $self->db->resultset('Photoset')->by_id_or_name($id) or $self->redirect_to("/photos"), return; $self->render(template => 'photos/show_set', set => $set); }; get '/photos/:id' => sub { my $self = shift; my $id = $self->param('id'); my $photo = $self->db->resultset('Photo')->find($id) or $self->redirect_to("/photos"), return; $self->render(template => 'photos/show', photo => $photo); }; app->start; __DATA__ @@ layouts/default.html.ep var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-29402272-1']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })();
% if ($self->req->url ne '/') {
% }
<%= content %>
# Requested id is a photoset? app->routes->add_condition( photoset => sub { my ($r, $c, $captures, $pattern) = @_; my $id = $captures->{id}; return 1 if $id !~ /^\d+$/ or $id =~ /^\d+$/ and length $id == 17; } ); helper db => sub { return Schema->connect('dbi:SQLite:' . ($ENV{TEST_DB} || 'test.db')); }; get '/' => sub { shift->render('home/index'); }; get '/blogs' => sub { shift->redirect_to('/blogs/tag/personal'); }; get '/blogs/tag/(*tags)' => sub { my $self = shift; # Specified tags to search for: /tag/one/tag/two/tag/three my @tags = grep $_ ne 'tag' => split '/' => $self->param('tags'); my @blogs = $self->db->resultset('Blog')->by_tags(@tags) or return $self->redirect_to('/blogs'); $self->render('blogs/index', blogs => [@blogs],); }; get '/blogs/(:name)' => sub { my $self = shift; my $param = $self->stash('name'); my $blog = $self->db->resultset('Blog')->by_id_or_name($param) or return $self->redirect_to('/blogs'); $self->render('blogs/show', blog => $blog); }; get '/photos' => sub { my $self = shift; $self->render( 'photos/index', sets => [$self->db->resultset('Photoset')->search], photo_count => $self->db->resultset('Photo')->count ); };#!/usr/bin/env perl use Mojolicious::Lite; #!/usr/bin/env perl use Mojolicious::Lite; use lib 'lib'; use Schema; # Requested id is a photoset? app->routes->add_condition( photoset => sub { my ($r, $c, $captures, $pattern) = @_; my $id = $captures->{id}; return 1 if $id !~ /^\d+$/ or $id =~ /^\d+$/ and length $id == 17; } ); my $ua = Mojo::UserAgent->new; my $res = $ua->get('search.twitter.com/search.json?q=mojolicious') ->res->json(‘/results/0/text’); get '/' => sub { shift->render('home/index'); }; get '/blogs' => sub { shift->redirect_to('/blogs/tag/personal'); }; get '/blogs/tag/(*tags)' => sub { my $self = shift; # Specified tags to search for: /tag/one/tag/two/tag/three my @tags = grep $_ ne 'tag' => split '/' => $self->param('tags'); my @blogs = $self->db->resultset('Blog')->by_tags(@tags) or return $self->redirect_to('/blogs'); $self->render('blogs/index', blogs => [@blogs],); }; get '/blogs/(:name)' => sub { my $self = shift; my $param = $self->stash('name'); my $blog = $self->db->resultset('Blog')->by_id_or_name($param) or return $self->redirect_to('/blogs'); $self->render('blogs/show', blog => $blog); }; get '/photos' => sub { my $self = shift; get '/blogs/tag/(*tags)' => sub { my $self = shift; # Specified tags to search for: /tag/one/tag/two/tag/three my @tags = grep $_ ne 'tag' => split '/' => $self->param('tags'); my @blogs = $self->db->resultset('Blog')->by_tags(@tags) or return $self->redirect_to('/blogs'); $self->render('blogs/index', blogs => [@blogs],); }; get '/blogs/(:name)' => sub { my $self = shift; my $param = $self->stash('name'); my $blog = $self->db->resultset('Blog')->by_id_or_name($param) or return $self->redirect_to('/blogs'); $self->render('blogs/show', blog => $blog); }; get '/photos' => sub { my $self = shift; $self->render( 'photos/index', sets => [$self->db->resultset('Photoset')->search], photo_count => $self->db->resultset('Photo')->count ); }; get '/photos/:id' => (photoset => '') => sub { my $self = shift; my $id = $self->param('id'); my $set = $self->db->resultset('Photoset')->by_id_or_name($id) or $self->redirect_to("/photos"), return; $self->render(template => 'photos/show_set', set => $set); }; get '/photos/:id' => sub { my $self = shift; my $id = $self->param('id'); my $photo = $self->db->resultset('Photo')->find($id) or $self->redirect_to("/photos"), return; $self->render(template => 'photos/show', photo => $photo); }; app->start; __DATA__ @@ layouts/default.html.ep var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-29402272-1']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })();
% if ($self->req->url ne '/') {
% }
<%= content %>
  • Mojolicious
  • Mojocasts
  • Mojo Example
  • Github
  • use Schema; # Requested id is a photoset? app->routes->add_condition( photoset => sub { my ($r, $c, $captures, $pattern) = @_; my $id = $captures->{id}; return 1 if $id !~ /^\d+$/ or $id =~ /^\d+$/ and length $id == 17; } ); helper db => sub { return Schema->connect('dbi:SQLite:' . ($ENV{TEST_DB} || 'test.db')); }; get '/' => sub { shift->render('home/index'); }; get '/blogs' => sub { shift->redirect_to('/blogs/tag/personal'); }; get '/blogs/tag/(*tags)' => sub { my $self = shift; # Specified tags to search for: /tag/one/tag/two/tag/three my @tags = grep $_ ne 'tag' => split '/' => $self->param('tags'); my @blogs = $self->db->resultset('Blog')->by_tags(@tags) or return $self->redirect_to('/blogs'); $self->render('blogs/index', blogs => [@blogs],); }; get '/blogs/(:name)' => sub { my $self = shift; my $param = $self->stash('name'); my $blog = $self->db->resultset('Blog')->by_id_or_name($param) or return $self->redirect_to('/blogs'); $self->render('blogs/show', blog => $blog); }; get '/photos' => sub { my $self = shift; $self->render( 'photos/index', sets => [$self->db->resultset('Photoset')->search], photo_count => $self->db->resultset('Photo')->count ); };#!/usr/bin/env perl use Mojolicious::Lite; #!/usr/bin/env perl use Mojolicious::Lite; use lib 'lib'; use Schema; # Requested id is a photoset? app->routes->add_condition( photoset => sub { my ($r, $c, $captures, $pattern) = @_; my $id = $captures->{id}; return 1 if $id !~ /^\d+$/ or $id =~ /^\d+$/ and length $id == 17; } ); helper db => sub { return Schema->connect('dbi:SQLite:' . ($ENV{TEST_DB} || 'test.db')); }; get '/' => sub { shift->render('home/index'); }; get '/blogs' => sub { shift->redirect_to('/blogs/tag/personal'); }; get '/blogs/tag/(*tags)' => sub { my $self = shift; # Specified tags to search for: /tag/one/tag/two/tag/three my @tags = grep $_ ne 'tag' => split '/' => $self->param('tags'); my @blogs = $self->db->resultset('Blog')->by_tags(@tags) or return $self->redirect_to('/blogs'); $self->render('blogs/index', blogs => [@blogs],); }; get '/blogs/(:name)' => sub { my $self = shift; my $param = $self->stash('name'); my $blog = $self->db->resultset('Blog')->by_id_or_name($param) or return $self->redirect_to('/blogs'); $self->render('blogs/show', blog => $blog); }; get '/photos' => sub { my $self = shift; $ perl useragent_script.pl

Slide 21

Slide 21 text

Easy debugging $ MOJO_USERAGENT_DEBUG=1 perl useragent_script.pl -- Blocking request (http://mojolicio.us) -- Connect (http:mojolicio.us:80) -- Client >>> Server (http://mojolicio.us) GET / HTTP/1.1 User-Agent: Mojolicious (Perl) Connection: keep-alive Content-Length: 0 Host: mojolicio.us -- Client <<< Server (http://mojolicio.us) HTTP/1.1 200 OK Connection: keep-alive Content-Type: text/html;charset=UTF-8 X-Powered-By: Mojolicious (Perl) Date: Tue, 29 May 2012 06:11:18 GMT Content-Length: 10101 Server: Mojolicious (Perl) { "completed_in":0.04, "max_id":146706880847294465, "max_id_str":"146706880847294465", "page":1, "query":"mojocasts", "refresh_url":"?since_id=146706880847294465&q=mojocasts", "results":[ { "text":"If you're thinking about #Perl on the web, ... Request Response

Slide 22

Slide 22 text

Moar websocket requests non-blocking concurrent requests ipv6 requests SSL authentication events collections

Slide 23

Slide 23 text

server

Slide 24

Slide 24 text

Routes get$‘/’

Slide 25

Slide 25 text

Routes get$‘/’

Slide 26

Slide 26 text

Routes get$‘/:placeholder’ get$‘/’

Slide 27

Slide 27 text

Routes get$‘/:placeholder’ get$‘/’ Stores$“hello”$in$placeholder

Slide 28

Slide 28 text

Routes get$‘/:placeholder’ get$‘/’ get$‘/#relaxed’

Slide 29

Slide 29 text

Routes get$‘/:placeholder’ Stores$“url.with.dots”$in$relaxed get$‘/’ get$‘/#relaxed’

Slide 30

Slide 30 text

Routes get$‘/:placeholder’ get$‘/’ get$‘/*wildcard’ get$‘/#relaxed’

Slide 31

Slide 31 text

Routes get$‘/:placeholder’ Stores$“captures/slashes”$in$wildcard get$‘/’ get$‘/*wildcard’ get$‘/#relaxed’

Slide 32

Slide 32 text

Routes get$‘/:normal’ get$‘/’ get$‘/*wildcard’ get$‘/#relaxed’

Slide 33

Slide 33 text

server Mojolicious::Lite

Slide 34

Slide 34 text

server Mojolicious::Lite One le

Slide 35

Slide 35 text

server Mojolicious::Lite One le Getting started

Slide 36

Slide 36 text

server Mojolicious::Lite One le Getting started Prototype apps

Slide 37

Slide 37 text

server Mojolicious::Lite One le Getting started All the functionality of Mojolicious Prototype apps

Slide 38

Slide 38 text

Mojolicious::Lite $$mojo$generate$lite_app$

Slide 39

Slide 39 text

Mojolicious::Lite $$mojo$generate$lite_app$hello

Slide 40

Slide 40 text

Mojolicious::Lite use$Mojolicious::Lite;

Slide 41

Slide 41 text

Mojolicious::Lite get$‘/’ use$Mojolicious::Lite; $$$$$$$$=>$sub${ $$my$$self$=$shift; $$$selfJ>render(text$=>$‘Hello!’); };

Slide 42

Slide 42 text

Mojolicious::Lite get$‘/’$=>$sub${ $$my$$self$=$shift; $$$selfJ>render(text$=>$‘Hello!’); }; appJ>start; use$Mojolicious::Lite;

Slide 43

Slide 43 text

Mojolicious::Lite $$morbo$hello$ [Wed%May%30%12:58:51%2012]%[info]%Listening%at%"http://*:3000". Server%available%at%http://127.0.0.1:3000.

Slide 44

Slide 44 text

Mojolicious::Lite $$morbo$hello$ [Wed%May%30%12:58:51%2012]%[info]%Listening%at%"http://*:3000". Server%available%at%http://127.0.0.1:3000. http://127.0.0.1:3000

Slide 45

Slide 45 text

Mojolicious::Lite get$‘/’$=>$sub${ $$my$$self$=$shift; $$$selfJ>render(text$=>$‘Hello!’); }; appJ>start; use$Mojolicious::Lite;

Slide 46

Slide 46 text

Mojolicious::Lite get$‘/’$=>$sub${ $$my$$self$=$shift; $$$selfJ>render(‘mytemplate’); }; appJ>start; use$Mojolicious::Lite;

Slide 47

Slide 47 text

Mojolicious::Lite get$‘/’$=>$sub${ $$my$$self$=$shift; $$$selfJ>render(‘mytemplate’); }; appJ>start; use$Mojolicious::Lite; __DATA__ @@$mytemplate.html.ep Hello!

Slide 48

Slide 48 text

Mojolicious::Lite get$‘/:myplaceholder’$=>$sub${ $$my$$self$=$shift; $$$selfJ>render(‘mytemplate’); }; appJ>start; use$Mojolicious::Lite; __DATA__ @@$mytemplate.html.ep Hello!

Slide 49

Slide 49 text

Mojolicious::Lite get$‘/:myplaceholder’$=>$sub${ $$my$$self$=$shift; $$$selfJ>render(‘mytemplate’); }; appJ>start; use$Mojolicious::Lite; __DATA__ @@$mytemplate.html.ep Hello$<%=$myplaceholder%>!

Slide 50

Slide 50 text

__DATA__ @@$mytemplate.html.ep Hello$<%=$myplaceholder%>! Mojolicious::Lite get$‘/:myplaceholder’$=>$sub${ $$my$$self$=$shift; $$$selfJ>render(‘mytemplate’); }; appJ>start; use$Mojolicious::Lite; YAPC YAPC

Slide 51

Slide 51 text

__DATA__ @@$mytemplate.html.ep Hello$<%=$myplaceholder%>! Mojolicious::Lite get$‘/:myplaceholder’$=>$sub${ $$my$$self$=$shift; $$$selfJ>render(‘mytemplate’); }; appJ>start; use$Mojolicious::Lite; whatever YAPC whatever

Slide 52

Slide 52 text

Mojolicious::Lite get$‘/:myplaceholder’$=>$sub${ $$my$$self$=$shift; $$$selfJ>render(‘mytemplate’); }; appJ>start; use$Mojolicious::Lite; __DATA__ @@$mytemplate.html.ep Hello$<%=$$myplaceholder$%>! YAPC YAPC

Slide 53

Slide 53 text

Mojolicious::Lite __DATA__ @@$mytemplate.html.ep Hello$<%=$myplaceholder%>! get$‘/:myplaceholder’$=>$sub${ $$my$$self$=$shift; $$$selfJ>render(‘mytemplate’); }; appJ>start; use$Mojolicious::Lite; YAPC ?year=2012 YAPC

Slide 54

Slide 54 text

Mojolicious::Lite get$‘/:myplaceholder’$=>$sub${ $$my$$self$=$shift; $$$selfJ>render(‘mytemplate’); }; appJ>start; use$Mojolicious::Lite; __DATA__ @@$mytemplate.html.ep Hello$<%=$myplaceholder%> YAPC YAPC ?year=2012 2012! <%=param$‘year’%>!

Slide 55

Slide 55 text

Mojolicious::Lite get$‘/:myplaceholder’$=>$sub${ $$my$$self$=$shift; $$$selfJ>render(‘mytemplate’); }; appJ>start; use$Mojolicious::Lite; __DATA__ @@$mytemplate.html.ep Hello$<%=$myplaceholder%> <%=param$‘year’%>!

Slide 56

Slide 56 text

EP Templates %$for$(@$foo)${ $$$$... %$} %=$$avariable;

Slide 57

Slide 57 text

EP Templates %$for$(@$foo)${ $$$$... %$} %=$$avariable; <%$for$(@$foo)${$%> $$$... <%$}$%> <%=$$avariable$%>

Slide 58

Slide 58 text

EP Templates %$for$(@$foo)${ $$$$... %$} %=$$avariable;
<%$for$(@$foo)${$%> $$$... <%$}$%>
  • <%=$$avariable$%>
  • Slide 59

    Slide 59 text

    EP Templates ...

    Slide 60

    Slide 60 text

    EP Templates ...
      %$for$(@$foo)${ %$}

    Slide 61

    Slide 61 text

    EP Templates ...
      %$for$(@$foo)${ $$
    • <%=$$_$%>
    • %$}

    Slide 62

    Slide 62 text

    Tag Helpers %=$submit_button$‘Go!’

    Slide 63

    Slide 63 text

    Tag Helpers %=$submit_button$‘Go!’ $$de $$en $$... %=$select_field$language$=>$[qw/$de$en$/];

    Slide 64

    Slide 64 text

    Tag Helpers
  • <%=$$_$%>
  • %=$submit_button$‘Go!’ $$de $$en $$... %=$select_field$language$=>$[qw/$de$en$/];

    Slide 65

    Slide 65 text

    Tag Helpers %=$t$li$=>$$_
  • <%=$$_$%>
  • %=$submit_button$‘Go!’ $$de $$en $$... %=$select_field$language$=>$[qw/$de$en$/];

    Slide 66

    Slide 66 text

    Tag Helpers %= t ul => id => blogs => begin % for my $blog (@$blogs) { %= t li => id => $blog->url_title, class => blog => begin %= t a => name => 'id' . $blog->id %= t a => name => $blog->url_title %= t h2 => begin %= link_to $blog->title => ‘/blogs/’ . $blog->url_title), class => 'more' % end % end % } % end

    Slide 67

    Slide 67 text

    Tag Helpers %= t ul => id => blogs => begin % for my $blog (@$blogs) { %= t li => id => $blog->url_title, class => blog => begin %= t h2 => begin %= link_to $blog->title => ‘/blogs/’ . $blog->url_title), class => 'more' % end % end % } % end id %>” /> url_title %>” />

    Slide 68

    Slide 68 text

    Helpers stash config content content_for extends flash include layout memorize session param title url_for url_with javascript link_to form_for stylesheet hidden_field radio_button password_field check_box ...

    Slide 69

    Slide 69 text

    Helpers helper$dumper$=>$sub${ $$return$Data::DumperJ>new([@_])J>Dump }; %=$dumper$$foo

    Slide 70

    Slide 70 text

    Helpers $selfJ>dumper($foo); helper$dumper$=>$sub${ $$return$Data::DumperJ>new([@_])J>Dump }; %=$dumper$$foo

    Slide 71

    Slide 71 text

    Helpers $selfJ>dumper($foo); get$‘/’$=>$sub${ $$my$$self$=$shift; $$$selfJ>render_text($$$$$$$$$$$$$$$$$$$); }; helper$dumper$=>$sub${ $$return$Data::DumperJ>new([@_])J>Dump }; %=$dumper$$foo

    Slide 72

    Slide 72 text

    get$‘/’$=>$sub${ $$my$$self$=$shift; $$$selfJ>render_text($$$$$$$$$$$$$$$$$$$); }; $$$$$$$$$$$$$$$$$$$$$$selfJ>dumper($foo) Helpers $selfJ>dumper($foo); helper$dumper$=>$sub${ $$return$Data::DumperJ>new([@_])J>Dump }; %=$dumper$$foo $selfJ>dumper($foo);

    Slide 73

    Slide 73 text

    Plugins

    Slide 74

    Slide 74 text

    Plugins $$cpanm$Mojolicious::Plugin::ZombieApocalypse ~

    Slide 75

    Slide 75 text

    $$cpanm$Mojolicious::Plugin:: ~ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$ZombieApocalypse ZombieApocalypse Plugins plugin$‘ZombieApocalypse’;

    Slide 76

    Slide 76 text

    get$‘/’$=>$sub${ $$my$$self$=$shift; $$$selfJ>render(text$=>$‘Hello!’); }; Plugins appJ>start; use$Mojolicious::Lite; plugin$‘ZombieApocalypse’;

    Slide 77

    Slide 77 text

    server Mojolicious

    Slide 78

    Slide 78 text

    server Mojolicious Spread among several les

    Slide 79

    Slide 79 text

    server Mojolicious Spread among several les Teams

    Slide 80

    Slide 80 text

    server Mojolicious Spread among several les Teams Larger apps

    Slide 81

    Slide 81 text

    Full App %%[exist]%/Users/glen/my_app/script %%[write]%/Users/glen/my_app/script/my_app %%[chmod]%my_app/script/my_app%744 %%[exist]%/Users/glen/my_app/lib %%[write]%/Users/glen/my_app/lib/MyApp.pm %%[exist]%/Users/glen/my_app/lib/MyApp %%[write]%/Users/glen/my_app/lib/MyApp/Example.pm %%[exist]%/Users/glen/my_app/t %%[write]%/Users/glen/my_app/t/basic.t %%[exist]%/Users/glen/my_app/log %%[exist]%/Users/glen/my_app/public %%[write]%/Users/glen/my_app/public/index.html %%[exist]%/Users/glen/my_app/templates/layouts %%[write]%/Users/glen/my_app/templates/layouts/default.html.ep %%[exist]%/Users/glen/my_app/templates/example %%[write]%/Users/glen/my_app/templates/example/welcome.html.ep $$mojo$generate$app$MyApp

    Slide 82

    Slide 82 text

    Full App Executables $$mojo$generate$app$MyApp App%Code Tests Logs Public Templates %%[exist]%/Users/glen/my_app/script %%[write]%/Users/glen/my_app/script/my_app %%[chmod]%my_app/script/my_app%744 %%[exist]%/Users/glen/my_app/lib %%[write]%/Users/glen/my_app/lib/MyApp.pm %%[exist]%/Users/glen/my_app/lib/MyApp %%[write]%/Users/glen/my_app/lib/MyApp/Example.pm %%[exist]%/Users/glen/my_app/t %%[write]%/Users/glen/my_app/t/basic.t %%[exist]%/Users/glen/my_app/log %%[exist]%/Users/glen/my_app/public %%[write]%/Users/glen/my_app/public/index.html %%[exist]%/Users/glen/my_app/templates/layouts %%[write]%/Users/glen/my_app/templates/layouts/default.html.ep %%[exist]%/Users/glen/my_app/templates/example %%[write]%/Users/glen/my_app/templates/example/welcome.html.ep

    Slide 83

    Slide 83 text

    Mojolicious Mojolicious::Lite

    Slide 84

    Slide 84 text

    Command Line ojo )

    Slide 85

    Slide 85 text

    Command Line perl -Mojo -E ‘a(“/” => {text => “Hello!”})->start’ daemon get$‘/’$=>$sub${ $$my$$self$=$shift; $$$selfJ>render(text$=>$“Hello!”); }; appJ>start; use$Mojolicious::Lite;

    Slide 86

    Slide 86 text

    No content

    Slide 87

    Slide 87 text

    websockets event-driven user agent embedded apps hot code reloading non-blocking parallel requests concurrent requests restful routes IPv6 PSGI support JSON parsing DOM parsing CSS3 selectors Multipart support SSL certi cate auth Tag Helpers

    Slide 88

    Slide 88 text

    websockets event-driven user agent embedded apps hot code reloading non-blocking parallel requests concurrent requests restful routes IPv6 PSGI support JSON parsing DOM parsing CSS3 selectors Multipart support SSL certi cate auth Tag Helpers Code < 10k lines

    Slide 89

    Slide 89 text

    Code ~ 17k lines websockets event-driven user agent embedded apps hot code reloading non-blocking parallel requests concurrent requests restful routes IPv6 PSGI support JSON parsing DOM parsing CSS3 selectors Multipart support SSL certi cate auth < 10k lines Tests ~ 9k unit tests Tag Helpers

    Slide 90

    Slide 90 text

    ~%cpanm%Mojolicious Code < 10k lines Tests ~ 9k unit tests cpanm Mojolicious websockets event-driven user agent embedded apps hot code reloading non-blocking parallel requests concurrent requests restful routes IPv6 PSGI support JSON parsing DOM parsing CSS3 selectors Multipart support SSL certi cate auth Code < 10k lines ~ 17k lines Tests ~ 9k unit tests Tag Helpers

    Slide 91

    Slide 91 text

    Code ~ 17k lines < 10k lines Tests ~ 9k unit tests ~%cpanm%Mojolicious TT>%Working%on%Mojolicious Fetching%http://www.cpan.org/authors/id/S/SR/SRI/MojoliciousT2.97.tar.gz% Configuring%MojoliciousT2.97%...%OK Building%and%testing%MojoliciousT2.97%... %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%OK Successfully%installed%MojoliciousT2.97 1%distribution%installed %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%OK ~

    Slide 92

    Slide 92 text

    Code ~ 17k lines < 10k lines Tests ~ 9k unit tests TT>%Working%on%Mojolicious Fetching%http://www.cpan.org/authors/id/S/SR/SRI/MojoliciousT2.97.tar.gz% Configuring%MojoliciousT2.97%...%OK Building%and%testing%MojoliciousT2.97%...%OK Successfully%installed%MojoliciousT2.97 1%distribution%installed ~ %%HARNESS_OPTIONS=j9 %%cpanm%Mojolicious 00:00:50;22 ~

    Slide 93

    Slide 93 text

    Code ~ 17k lines < 10k lines Tests ~ 9k unit tests ~%HARNESS_OPTIONS=j9%cpanm%Mojolicious TT>%Working%on%Mojolicious Fetching%http://www.cpan.org/authors/id/S/SR/SRI/MojoliciousT2.97.tar.gz% Configuring%MojoliciousT2.97%...%OK Building%and%testing%MojoliciousT2.97%...%OK Successfully%installed%MojoliciousT2.97 1%distribution%installed 00:00:15;41 00:00:50;22

    Slide 94

    Slide 94 text

    server Deployment

    Slide 95

    Slide 95 text

    server Deployment PSGI CGI Daemon

    Slide 96

    Slide 96 text

    Daemon

    Slide 97

    Slide 97 text

    Daemon Morbo Development Server

    Slide 98

    Slide 98 text

    Morbo Development Server Hypnotoad Hot-code reloading production server Daemon

    Slide 99

    Slide 99 text

    $$hypnotoad hello Hot-code reloading production server Hypnotoad

    Slide 100

    Slide 100 text

    $$hypnotoad$hello Run$&$Reload: Hot-code reloading production server Hypnotoad

    Slide 101

    Slide 101 text

    $$hypnotoad$hello Run$&$Reload: Stop: $$hypnotoad$Js$hello Hot-code reloading production server Hypnotoad

    Slide 102

    Slide 102 text

    No content

    Slide 103

    Slide 103 text

    No content

    Slide 104

    Slide 104 text

    server Deployment

    Slide 105

    Slide 105 text

    No content

    Slide 106

    Slide 106 text

    $$cpanm$Mojolicious::Command::deploy::heroku $

    Slide 107

    Slide 107 text

    $$cpanm$Mojolicious::Command::deploy::heroku $ ✔

    Slide 108

    Slide 108 text

    $$./hello$deploy$heroku$JJcreate $ $$mojo$generate$lite_app$hello

    Slide 109

    Slide 109 text

    $$./hello$deploy$heroku $ $$mojo$generate$lite_app$hello $Jn$hellomojo

    Slide 110

    Slide 110 text

    1

    Slide 111

    Slide 111 text

    1 2 $$cpanm$Mojolicious::Command::deploy::heroku

    Slide 112

    Slide 112 text

    1 2 $$cpanm$Mojolicious::Command::deploy::heroku mojo$generate$lite_app$hello 3

    Slide 113

    Slide 113 text

    1 2 $$cpanm$Mojolicious::Command::deploy::heroku hello$deploy$heroku 4 mojo$generate$lite_app$hello 3

    Slide 114

    Slide 114 text

    what’s next?

    Slide 115

    Slide 115 text

    mojocasts.com

    Slide 116

    Slide 116 text

    mojocasts.com

    Slide 117

    Slide 117 text

    mojocasts.com

    Slide 118

    Slide 118 text

    mojocasts.com aggregation of links that are not collected anywhere else. it’s good a good place to know what the latest links are in the mojolicious community.

    Slide 119

    Slide 119 text

    Mojolicious Guides

    Slide 120

    Slide 120 text

    Mojo Example http://git.io/mojoexample lite app

    Slide 121

    Slide 121 text

    Mojo Example lite app full app http://git.io/mojoexample

    Slide 122

    Slide 122 text

    Mojo Example Live on Heroku http://git.io/mojoexample

    Slide 123

    Slide 123 text

    irc://irc.perl.org/#mojo mailing list [email protected] irc

    Slide 124

    Slide 124 text

    irc://irc.perl.org/#mojo mailing list [email protected] irc :D

    Slide 125

    Slide 125 text

    http://mojocasts.com/yapc resources

    Slide 126

    Slide 126 text

    @tempire http://tempi.re Glen Hinkle

    Slide 127

    Slide 127 text

    mojolicious next-generation real-time web framework intro to @tempire http://tempi.re/ Glen Hinkle http://mojocasts.com/yapc resources