Mojolicious.
Веб в коробке!

Анатолий Шарифулин
Sebastian Riedel
 автор Catalyst и Mojolicious
    http://twitter.com/kraih
Что такое
Mojolicious?
Что такое Mojolicious?
• Pure Perl веб-фреймворк
• Без зависимостей (с версии Perl 5.8.1)
• Объектно-ориентированное API (...
«Fresh code,
based upon years of
expirience developing
       Catalyst»
Pure Perl
веб-фреймворк
• Mojo::Base
• Mojo::ByteStream
• Mojo::Template, Mojo::JSON
• Mojo::Loader, Mojo::Log, Mojo::Path
• Mojo::URL, Mojo::Para...
• Mojo::Base
• Mojo::ByteStream
• Mojo::Template, Mojo::JSON
• Mojo::Loader, Mojo::Log, Mojo::Path
• Mojo::URL, Mojo::Para...
Mojo::Base
Minimal Base Class For Mojo Projects
package App;
use base ‘Mojo::Base’;

__PACKAGE__->attr(conf => sub {
	

 return do ‘conf/app.conf’;
});
__PACKAGE__->attr(...
package App;
use base ‘Mojo::Base’;

__PACKAGE__->attr(conf => sub {
	

 return do ‘conf/app.conf’;
});
__PACKAGE__->attr(...
package App;
use base ‘Mojo::Base’;

__PACKAGE__->attr(conf => sub {
	

 return do ‘conf/app.conf’;
});
__PACKAGE__->attr(...
package App;
use base ‘Mojo::Base’;

__PACKAGE__->attr(conf => sub {
	

 return do ‘conf/app.conf’;
});
__PACKAGE__->attr(...
• Mojo::Base
• Mojo::ByteStream
• Mojo::Template, Mojo::JSON
• Mojo::Loader, Mojo::Log, Mojo::Path
• Mojo::URL, Mojo::Para...
Mojo::ByteStream
     ByteStream
use Mojo::ByteStream;
my $s = Mojo::ByteStream->new('foo_bar');

$s->camelize; $s->decamelize;
$s->b64_encode; $s->b64_dec...
use Mojo::ByteStream 'b';
my $s = b('foo_bar');

$s->camelize; $s->decamelize;
$s->b64_encode; $s->b64_decode;
$s->encode(...
use Mojo::ByteStream;
my $s = Mojo::ByteStream->new('foo_bar');

$s->camelize; $s->decamelize;
$s->b64_encode; $s->b64_dec...
use Mojo::ByteStream;
my $s = Mojo::ByteStream->new('foo_bar');

$s->camelize; $s->decamelize;
$s->b64_encode; $s->b64_dec...
use Mojo::ByteStream;
my $s = Mojo::ByteStream->new('foo_bar');

$s->camelize; $s->decamelize;
$s->b64_encode; $s->b64_dec...
use Mojo::ByteStream;
my $s = Mojo::ByteStream->new('foo_bar');

$s->camelize; $s->decamelize;
$s->b64_encode; $s->b64_dec...
use Mojo::ByteStream;
my $s = Mojo::ByteStream->new('foo_bar');

$s->camelize; $s->decamelize;
$s->b64_encode; $s->b64_dec...
use Mojo::ByteStream;
my $s = Mojo::ByteStream->new('foo_bar');

$s->camelize; $s->decamelize;
$s->b64_encode; $s->b64_dec...
use Mojo::ByteStream;
my $s = Mojo::ByteStream->new('foo_bar');

$s->camelize; $s->decamelize;
$s->b64_encode; $s->b64_dec...
use Mojo::ByteStream;
my $s = Mojo::ByteStream->new('foo_bar');

$s->camelize; $s->decamelize;
$s->b64_encode; $s->b64_dec...
use Mojo::ByteStream;
my $s = Mojo::ByteStream->new('foo_bar');

$s->camelize; $s->decamelize;
$s->b64_encode; $s->b64_dec...
• Mojo::Base
• Mojo::ByteStream
• Mojo::Template, Mojo::JSON
• Mojo::Loader, Mojo::Log, Mojo::Path
• Mojo::URL, Mojo::Para...
Mojo::Template
   Perlish Templates!
% my $list = $self->stash(‘list’);

<ul>
% for (@$list) {
	

 <li>
	

 	

 <%= $_->{title} %>
	

 </li>
%}
</ul>

<p><%# c...
% my $list = $self->stash(‘list’);

<ul>
% for (@$list) {
	

 <li>
	

 	

 <%= $_->{title} %>
	

 </li>
%}
</ul>

<p><%# c...
% my $list = $self->stash(‘list’);

<ul>
% for (@$list) {
	

 <li>
	

 	

 <%= $_->{title} %>
	

 </li>
%}
</ul>

<p><%# c...
% my $count = 10 * 5;

<ul>
% for (@$list) {
	

 <li>
	

 	

 <%= $_->{title} %>
	

 </li>
%}
</ul>

<p><%# comment %></p>
% use Foo::Bar;

<ul>
% for (@$list) {
	

 <li>
	

 	

 <%= $_->{title} %>
	

 </li>
%}
</ul>

<p><%# comment %></p>
% my $list = $self->stash(‘list’);

<ul>
% for (@$list) {
	

 <li>
	

 	

 <%= $_->{title} %>
	

 </li>
%}
</ul>

<p><%# c...
% my $list = $self->stash(‘list’);

<ul>
% if (@$list) {
	

 <li>
	

 	

 <%= $_->{title} %>
	

 </li>
%}
</ul>

<p><%# co...
% my $list = $self->stash(‘list’);

<ul>
% for (@$list) {
	

 <li>
	

 	

 <%= $_->{title} %>
	

 </li>
%}
</ul>

<p><%# c...
% my $list = $self->stash(‘list’);

<ul>
% for (@$list) {
	

 <li>
	

 	

 <%= $_->{title} %>
	

 </li>
%}
</ul>

<p><%# c...
% my $list = $self->stash(‘list’);

<ul>
% for (@$list) {
	

 <li>
	

 	

 <%== $_->{title} %>
	

 </li>
%}
</ul>

<p><%# ...
% my $list = $self->stash(‘list’);

<ul>
% for (@$list) {
	

 <li>
	

 	

 <%= ucfirst $_->{title} %>
	

 </li>
%}
</ul>

<...
% my $list = $self->stash(‘list’);

<ul>
% for (@$list) {
	

 <li>
	

 	

 <%= $_->{title} %>
	

 </li>
%}
</ul>

<p><%# c...
% my $list = $self->stash(‘list’);

<ul>
% for (@$list) {
	

 <li>
	

 	

 <%= $_->{title} %>
	

 </li>
%}
</ul>

<p><%# c...
1   % my $list = $self->stash(‘list’);
 2
 3   <ul>
 4   % for (@$list) {
 5   	

 <li>
 6   	

 	

 <%= $_->{title} xx %>...
Сообщение об ошибке
  Bareword "xx" not allowed while "strict
      subs" in use at template line 6.
Простота PHP,
 мощь Perl
• Mojo::Base
• Mojo::ByteStream
• Mojo::Template, Mojo::JSON
• Mojo::Loader, Mojo::Log, Mojo::Path
• Mojo::URL, Mojo::Para...
Mojo::JSON
 Minimalistic JSON
my $json = Mojo::JSON->new;

my $string = $json->encode({foo => ‘bar’});

my $hash = $json->decode(‘{"foo":"bar"}’);
my $json = Mojo::JSON->new;

my $string = $json->encode({foo => ‘bar’});

my $hash = $json->decode(‘{"foo":"bar"}’);
• Mojo::Base
• Mojo::ByteStream
• Mojo::Template, Mojo::JSON
• Mojo::Loader, Mojo::Log, Mojo::Path
• Mojo::URL, Mojo::Para...
Mojo::Loader
Class loader: load, reload, search
• Mojo::Base
• Mojo::ByteStream
• Mojo::Template, Mojo::JSON
• Mojo::Loader, Mojo::Log, Mojo::Path
• Mojo::URL, Mojo::Para...
• Mojo::Base
• Mojo::ByteStream
• Mojo::Template, Mojo::JSON
• Mojo::Loader, Mojo::Log, Mojo::Path
• Mojo::URL, Mojo::Para...
• Mojo::Base
• Mojo::ByteStream
• Mojo::Template, Mojo::JSON
• Mojo::Loader, Mojo::Log, Mojo::Path
• Mojo::URL, Mojo::Para...
•   Mojo::Trasaction

•   Mojo::IOLoop

•   Mojo::Client, Mojo::Server

    •   Mojo::Server::CGI, Mojo::Server::FastCGI

...
•   Mojo::Trasaction

•   Mojo::IOLoop

•   Mojo::Client, Mojo::Server

    •   Mojo::Server::CGI, Mojo::Server::FastCGI

...
•   Mojo::Trasaction

•   Mojo::IOLoop

•   Mojo::Client, Mojo::Server

    •   Mojo::Server::CGI, Mojo::Server::FastCGI

...
Mojo::IOLoop
Minimalistic Reactor For TCP Clients And Servers
Mojo::IOLoop
Поддержка IO::KQueue, IO::Epoll,
IO::Socket::INET6 и IO::Socket::SSL
my $loop = Mojo::IOLoop->new;
$loop->listen(port => 3000, cb => sub {
   my ($self, $id) = @_;

     $self->read_cb ($id =...
my $loop = Mojo::IOLoop->new;
$loop->listen(port => 3000, cb => sub {
   my ($self, $id) = @_;

     $self->read_cb ($id =...
my $loop = Mojo::IOLoop->new;
$loop->listen(port => 3000, cb => sub {
   my ($self, $id) = @_;

     $self->read_cb ($id =...
my $loop = Mojo::IOLoop->new;
$loop->listen(port => 3000, cb => sub {
   my ($self, $id) = @_;

     $self->read_cb ($id =...
•   Mojo::Trasaction

•   Mojo::IOLoop

•   Mojo::Client, Mojo::Server

    •   Mojo::Server::CGI, Mojo::Server::FastCGI

...
Mojo::Client
Async IO HTTP 1.1 And WebSocket Client
my $client = Mojo::Client->new;

$client->get(
	

 ‘http://goo.gl’ => sub {
	

 	

 my ($self, $tx) = @_;
	

 	

 say $tx-...
my $client = Mojo::Client->new;

$self->client->get(
	

 ‘http://goo.gl’ => sub {
	

 	

 my ($self, $tx) = @_;
	

 	

 sa...
my $client = Mojo::Client->new;

$client->get(
	

 ‘http://goo.gl’ => sub {
	

 	

 my ($self, $tx) = @_;
	

 	

 say $tx-...
my $client = Mojo::Client->new;

$client->head(
	

 ‘http://goo.gl’ => sub {
	

 	

 my ($self, $tx) = @_;
	

 	

 say $tx...
my $client = Mojo::Client->new;

$client->post(
	

 ‘http://goo.gl’ => sub {
	

 	

 my ($self, $tx) = @_;
	

 	

 say $tx...
my $client = Mojo::Client->new;

$client->put(
	

 ‘http://goo.gl’ => sub {
	

 	

 my ($self, $tx) = @_;
	

 	

 say $tx-...
my $client = Mojo::Client->new;

$client->async->get(
	

 ‘http://goo.gl’ => sub {
	

 	

 my ($self, $tx) = @_;
	

 	

 s...
my $client = Mojo::Client->new;

$client->get(
	

 ‘http://goo.gl’ => sub {
	

 	

 my ($self, $tx) = @_;
	

 	

 say $tx-...
my $client = Mojo::Client->new;

$client->get(
	

 ‘http://goo.gl’ => sub {
	

 	

 my ($self, $tx) = @_;
	

 	

 say $tx-...
my $client = Mojo::Client->new;

$client->get(
	

 ‘http://goo.gl’ => sub {
	

 	

 my ($self, $tx) = @_;
	

 	

 say $tx-...
my $client = Mojo::Client->new;

$client->get(‘http://goo.gl’)->res->code;

$client->get(
  'http://search.twitter.com/tre...
my $client = Mojo::Client->new;

$client->get(‘http://goo.gl’)->res->code;

$client->get(
  'http://search.twitter.com/tre...
script/mojolicious
 get 'http://goo.gl'
•   Mojo::Trasaction

•   Mojo::IOLoop

•   Mojo::Client, Mojo::Server

    •   Mojo::Server::CGI, Mojo::Server::FastCGI

...
•   Mojo::Trasaction

•   Mojo::IOLoop

•   Mojo::Client, Mojo::Server

    •   Mojo::Server::CGI, Mojo::Server::FastCGI

...
•   Mojo::Trasaction

•   Mojo::IOLoop

•   Mojo::Client, Mojo::Server

    •   Mojo::Server::CGI, Mojo::Server::FastCGI

...
Всё, что нужно – есть
    Mojolicious – веб в коробке!
Без зависимостей
    с версии Perl 5.8.1
Mojo runtime
Perl 5.005
Perl 5.006002
Perl 5.008001
Perl 5.010
Объектно-
ориентированное
      API
package App;
use base 'Mojo';
sub handler {
    my ($self, $tx) = @_;
	

	

 warn $tx->req; warn $tx->req->url;
	

    $tx...
package App;
use base 'Mojo';
sub handler {
    my ($self, $tx) = @_;
	

	

 warn $tx->req; warn $tx->req->url;
	

    $tx...
package App;
use base 'Mojo';
sub handler {
    my ($self, $tx) = @_;
	

	

 warn $tx->req; warn $tx->req->url;
	

    $tx...
package App;
use base 'Mojo';
sub handler {
    my ($self, $tx) = @_;
	

	

 warn $tx->req; warn $tx->req->url;
	

    $tx...
package App;
use base 'Mojo';
sub handler {
    my ($self, $tx) = @_;
	

	

 warn $tx->req; warn $tx->req->url;
	

    $tx...
package App;
use base 'Mojo';
sub handler {
    my ($self, $tx) = @_;
	

	

 warn $tx->req; warn $tx->req->url;
	

    $tx...
use overload '""' => sub
  { shift->to_string },
     fallback => 1;
GET / HTTP/1.1
Connection: keep-alive
Accept: text/html, application/xhtml, ....
Accept-Charset: windows-1251, utf-8; ...
...
Поддержка HTTP 1.1
      клиент-сервер
Google Summer of Code 2009
          Pascal Gaudette
         MojoX::UserAgent
Поддержка WebSocket
      клиент-сервер
Протокол WebSocket
         Google, Inc
    16 декабря 2009 года
use Mojolicious::Lite;

websocket ‘/echo’ => sub {
	

 my $self = shift;
	

 $self->receive_message(sub {
	

 	

 my ($sel...
use Mojolicious::Lite;

websocket ‘/echo’ => sub {
	

 my $self = shift;
	

 $self->receive_message(sub {
	

 	

 my ($sel...
Mojolicious WebSocket Examples
       Интересный пример IRC-клиент
            Github.com @xantus
Google Summer of Code 2010
             Performance tuning
     the http/websocket implementation
Поддержка CGI,
 FastCGI, PSGI,
Daemon и Prefork
script/mojolicious
COMMAND [OPTIONS]
script/mojolicious cgi
script/mojolicious fastcgi
script/mojolicious psgi
script/mojo...
script/mojolicious
COMMAND [OPTIONS]
script/mojolicious cgi
script/mojolicious fastcgi
script/mojolicious psgi
script/mojo...
PATH_INFO='/foo/bar'
   script/mojolicious cgi
             Легко тестировать
Можно профилировать код, используя Devel::NY...
script/mojolicious
COMMAND [OPTIONS]
script/mojolicious cgi
script/mojolicious fastcgi
script/mojolicious psgi
script/mojo...
Mojo::Server::FCGI
script/mojolicious fcgi
script/mojolicious fcgi_prefork
script/mojolicious
COMMAND [OPTIONS]
script/mojolicious cgi
script/mojolicious fastcgi
script/mojolicious psgi
script/mojo...
script/mojolicious
COMMAND [OPTIONS]
script/mojolicious cgi
script/mojolicious fastcgi
script/mojolicious psgi
script/mojo...
script/mojolicious
      daemon --reload
Приложение перезагружается перед запросом,
          если код был изменен
Обработка сигнала USR1
   Для обновления кода приложения
script/mojolicious
COMMAND [OPTIONS]
script/mojolicious cgi
script/mojolicious fastcgi
script/mojolicious psgi
script/mojo...
Веб-фреймворки
Mojo
Base framework
package App;
use base 'Mojo';
sub handler {
    my ($self, $tx) = @_;
	

	

 warn $tx->req; warn $tx->req->url;
	

    $tx...
package App;
use base 'Mojo';
sub handler {
    my ($self, $tx) = @_;
	

	

 warn $tx->req; warn $tx->req->url;
	

    $tx...
Генерация
mojo-приложения
script/mojo generate
       app App
[write ] app/script/app
[chmod] app/script/app 744
[write ] app/lib/App.pm
[write ] ap...
script/app COMMAND
      [OPTIONS]
    script/app cgi
    script/app fastcgi
    script/app psgi
    script/app daemon
   ...
script/app COMMAND
      [OPTIONS]
    script/app get
    script/app test
MVC веб-фреймворки
Mojolicious
 use base 'Mojo';
•   Mojolicous::Controller

•   Mojolicious::Plugins

•   Mojolicious::Commands

•   MojoX::Types

•   MojoX::Session

•  ...
•   Mojolicous::Controller

•   Mojolicious::Plugins

•   Mojolicious::Commands

•   MojoX::Types

•   MojoX::Session

•  ...
Mojolicious::Controller
      Controller Base Class
•   render

•   render_text

•   render_data

•   render_json

•   render_static

•   render_not_found / render_exception
...
•   render(controller => 'foo', action => 'bar')

•   render_text

•   render_data

•   render_json

•   render_static

• ...
•   render(template => 'foo/bar')

•   render_text

•   render_data

•   render_json

•   render_static

•   render_not_fo...
•   render('foo#bar')

•   render_text

•   render_data

•   render_json

•   render_static

•   render_not_found / render...
•   render('foo#bar', format => 'html')

•   render_text

•   render_data

•   render_json

•   render_static

•   render_...
•   render

•   render_text

•   render_data

•   render_json

•   render_static

•   render_not_found / render_exception
...
•   render

•   render_text('РИТ++ 2010')

•   render_data

•   render_json

•   render_static

•   render_not_found / ren...
•   render

•   render(text => 'РИТ++ 2010')

•   render_data

•   render_json

•   render_static

•   render_not_found / ...
•   render

•   render_text

•   render_data

•   render_json

•   render_static

•   render_not_found / render_exception
...
•   render

•   render_text

•   render_data('binary data')

•   render_json

•   render_static

•   render_not_found / re...
•   render

•   render_text

•   render(data => 'binary data')

•   render_json

•   render_static

•   render_not_found /...
•   render

•   render_text

•   render_data

•   render_json

•   render_static

•   render_not_found / render_exception
...
•   render

•   render_text

•   render_data

•   render_json({foo => 'bar'})

•   render_static

•   render_not_found / r...
•   render

•   render_text

•   render_data

•   render(json => {foo => 'bar'})

•   render_static

•   render_not_found ...
•   render

•   render_text

•   render_data

•   render_json

•   render_static

•   render_not_found / render_exception
...
•   render

•   render_text

•   render_data

•   render_json

•   render_static('img/logo.png')

•   render_not_found / r...
•   render

•   render_text

•   render_data

•   render_json

•   render_static

•   render_not_found / render_exception
...
•   render

•   render_text

•   render_data

•   render_json

•   render_static

•   render_not_found / render_exception
...
•   render

•   render_text

•   render_data

•   render_json

•   render_static

•   render_not_found / render_exception
...
•   Mojolicous::Controller

•   Mojolicious::Plugins

•   Mojolicious::Commands

•   MojoX::Types

•   MojoX::Session

•  ...
Mojolicious::Plugins
        Plugins
•   AgentCondition

•   Charset

•   DefaultHelpers

•   EplRenderer

•   EpRenderer

•   HeaderCondition

•   I18n, JsonC...
•   AgentCondition

•   Charset

•   DefaultHelpers

•   EplRenderer

•   EpRenderer

•   HeaderCondition

•   I18n, JsonC...
•   dumper

•   param

•   stash

•   layout

•   include

•   content

•   extends

•   url_for
•   Data::Dumper (Maxdepth: 2, Indent: 1, Terse: 1)

•   param

•   stash

•   layout

•   include

•   content

•   exten...
•   dumper

•   param

•   stash

•   layout

•   include

•   content

•   extends

•   url_for
•   dumper

•   $self->req->param( ... )

•   stash

•   layout

•   include

•   content

•   extends

•   url_for
•   dumper

•   param

•   stash

•   layout

•   include

•   content

•   extends

•   url_for
•   dumper

•   param

•   $self->stash( ... )

•   layout

•   include

•   content

•   extends

•   url_for
•   dumper

•   param

•   stash

•   layout

•   include

•   content

•   extends

•   url_for
•   dumper

•   param

•   stash

•   layout

•   include

•   content

•   extends

•   url_for
•   dumper

•   param

•   stash

•   layout

•   include

•   content

•   extends

•   url_for
•   dumper

•   param

•   stash

•   layout

•   include

•   content

•   extends

•   url_for
•   dumper

•   param

•   stash

•   layout

•   include

•   content

•   extends

•   url_for
•   dumper

•   param

•   stash

•   layout

•   include

•   content

•   extends

•   $self->url_for
•   AgentCondition

•   Charset

•   DefaultHelpers

•   EplRenderer

•   EpRenderer

•   HeaderCondition

•   I18n, JsonC...
EplRenderer
  Embed Perl Lite
•   расширение шаблонов .epl

•   в начале каждого шаблона:
    % my $self = shift;

•   $self->stash('foo')
•   AgentCondition

•   Charset

•   DefaultHelpers

•   EplRenderer

•   EpRenderer

•   HeaderCondition

•   I18n, JsonC...
EpRenderer
  Embed Perl
•   расширение шаблонов .ep

•   кеширование шаблонов со stash-параметрами

•   вместо $self->stash('foo') – $foo

•   дос...
•   Mojolicous::Controller

•   Mojolicious::Plugins

•   Mojolicious::Commands

•   MojoX::Types

•   MojoX::Session

•  ...
•   Mojolicous::Controller

•   Mojolicious::Plugins

•   Mojolicious::Commands

•   MojoX::Types

•   MojoX::Session

•  ...
•   Mojolicous::Controller

•   Mojolicious::Plugins

•   Mojolicious::Commands

•   MojoX::Types

•   MojoX::Session

•  ...
•   Mojolicous::Controller

•   Mojolicious::Plugins

•   Mojolicious::Commands

•   MojoX::Types

•   MojoX::Session

•  ...
Генерация
mojolicious-приложения
script/mojolicious generate
         app App
    [write ] app/script/app
    [chmod] app/script/app 744
    [write ] app/l...
package App;
use base 'Mojolicious';

sub startup {
  my $self = shift;

     my $r = $self->routes;
     $r->route('/:con...
package App;
use base 'Mojolicious';

sub startup {
  my $self = shift;

     my $r = $self->routes;
     $r->route('/:con...
package App;
use base 'Mojolicious';

sub startup {
  my $self = shift;

     my $r = $self->routes;
     $r->route('/:con...
package App;
use base 'Mojolicious';

sub startup {
  my $self = shift;

     my $r = $self->routes;
     $r->route('/:con...
package App;
use base 'Mojolicious';

sub startup {
  my $self = shift;

     my $r = $self->routes;
     $r->route('/:con...
package App;
use base 'Mojolicious';

sub startup {
  my $self = shift;

     my $r = $self->routes;
     $r->route('/:id'...
package App;
use base 'Mojolicious';

sub startup {
  my $self = shift;

    my $r = $self->routes;
    for ($r->bridge->t...
package App;
use base 'Mojolicious';

sub startup {
  my $self = shift;

    my $r = $self->routes;
    for ($r->bridge->t...
package App;
use base 'Mojolicious';

sub startup {
  my $self = shift;

     my $r = $self->routes;
     $r->route('/:con...
script/mojolicious generate
         app App
    [write ] app/script/app
    [chmod] app/script/app 744
    [write ] app/l...
package App::Example;
use base 'Mojolicious::Controller';

sub welcome {
    my $self = shift;
	

 warn $self->stash(‘id’)...
package App::Example;
use base 'Mojolicious::Controller';

sub welcome {
    my $self = shift;
	

 warn $self->stash(‘id’)...
package App::Example;
use base 'Mojolicious::Controller';

sub welcome {
    my $self = shift;
	

 warn $self->stash(‘id’)...
script/mojolicious generate
         app App
[mkdir] app/public
[write ] app/templates/not_found.html.ep
[write ] app/temp...
script/mojolicious generate
         app App
[mkdir] app/public
[write ] app/templates/not_found.html.ep
[write ] app/temp...
script/mojolicious generate
         app App
[mkdir] app/public
[write ] app/templates/not_found.html.ep
[write ] app/temp...
% layout 'default';

<h2><%= $message %></h2>

<a href="<%== url_for %>">click here</a>
% layout 'default';

<h2><%= $message %></h2>

<a href="<%== url_for %>">click here</a>
% layout 'default';

<h2><%= $self->stash('message') %></h2>

<a href="<%== url_for %>">click here</a>
% layout 'default';

<h2><%= $message %></h2>

<a href="<%== url_for %>">click here</a>
% layout 'default';

<h2><%= stash 'message' %></h2>

<a href="<%== url_for %>">click here</a>
% layout 'default';

<h2><%= $message %></h2>

<a href="<%== url_for %>">click here</a>
script/mojolicious generate
         app App
[mkdir] app/public
[write ] app/templates/not_found.html.ep
[write ] app/temp...
<!doctype html>
<html>
   <head><title>Welcome</title></head>
   <body>
     <%== content %>
   </body>
</html>
<!doctype html>
<html>
   <head><title>Welcome</title></head>
   <body>
     <%== content %>
   </body>
</html>
script/mojolicious generate
         app App
[mkdir] app/public
[write ] app/templates/not_found.html.ep
[write ] app/temp...
script/mojolicious generate
         app App
[mkdir] app/public
[write ] app/templates/not_found.html.ep
[write ] app/temp...
Global symbol "$message2" requires explicit
package name at (eval 280) line 2.

1: % layout 'default';
2: <h2><%= $message...
example/
 welcome.html.ep
Автоматическая генерация имени шаблона
controller/
action.format.handler
 Автоматическая генерация имени шаблона
example/
 welcome.html.ep
Автоматическая генерация имени шаблона
example/
  welcome.xml.ep
Автоматическая генерация имени шаблона
example/
   welcome.rss.ep
Автоматическая генерация имени шаблона
example/
  welcome.json.ep
Автоматическая генерация имени шаблона
example/
 welcome.html.ep
Автоматическая генерация имени шаблона
example/
 welcome.html.epl
Автоматическая генерация имени шаблона
example/
  welcome.html.tt
Автоматическая генерация имени шаблона
example/
welcome.html.cttp2
Автоматическая генерация имени шаблона
script/app COMMAND
      [OPTIONS]
    script/app cgi
    script/app fastcgi
    script/app psgi
    script/app daemon
   ...
script/app COMMAND
      [OPTIONS]
    script/app get
    script/app test
    script/app routes
Mojolicious::Lite
   use base 'Mojolicious';
script/mojolicious generate
       lite_app lite.pl
       [write ] lite.pl
       [chmod] lite.pl 744
use Mojolicious::Lite;

get '/' => 'index';

get '/:groovy' => sub {
   my $self = shift;
   $self->render_text( $self->pa...
use Mojolicious::Lite;

get '/' => 'index';

get '/:groovy' => sub {
   my $self = shift;
   $self->render_text( $self->pa...
use Mojolicious::Lite;

get '/' => 'index';

get '/:groovy' => sub {
   my $self = shift;
   $self->render_text( $self->pa...
use Mojolicious::Lite;

post '/' => 'index';

get '/:groovy' => sub {
   my $self = shift;
   $self->render_text( $self->p...
use Mojolicious::Lite;

any '/' => 'index';

get '/:groovy' => sub {
   my $self = shift;
   $self->render_text( $self->pa...
use Mojolicious::Lite;

get '/' => (agent => qr/Firefox/) => 'index';

get '/:groovy' => sub {
   my $self = shift;
   $se...
use Mojolicious::Lite;

get '/' => 'index';

get '/:groovy' => {groovy => 42} => sub {
   my $self = shift;
   $self->rend...
use Mojolicious::Lite;

get '/' => 'index';

get '/:groovy' => [groovy => qr/d+/] => sub {
   my $self = shift;
   $self->...
use Mojolicious::Lite;
ladder sub {}; # auth
get '/' => 'index';

get '/:groovy' => sub {
   my $self = shift;
   $self->r...
use Mojolicious::Lite;
websocket '/echo' => sub { ... };
get '/' => 'index';

get '/:groovy' => sub {
   my $self = shift;...
use Mojolicious::Lite;

get '/' => 'index';

get '/:groovy' => sub {
   my $self = shift;
   $self->render_text( $self->pa...
use Mojolicious::Lite;

get '/' => 'index';

get '/:groovy' => sub {
   my $self = shift;
   $self->render_text( $self->pa...
__DATA__

@@ index.html.ep
% layout 'funky';
Yea baby!

@@ layouts/funky.html.ep
<!doctype html><html>
   <head><title>Fun...
__DATA__

@@ index.html.ep
% layout 'funky';
Yea baby!

@@ layouts/funky.html.ep
<!doctype html><html>
   <head><title>Fun...
lite.pl COMMAND
     [OPTIONS]
  lite.pl cgi
  lite.pl fastcgi
  lite.pl psgi
  lite.pl daemon
  lite.pl daemon_prefork
lite.pl COMMAND
     [OPTIONS]
  lite.pl test
  lite.pl get
  lite.pl routes
  lite.pl inflate
«Making hard things possible
   and everything fun!»
         Девиз Mojolicious
«Viva la revolution!»
    Девиз Mojolicious #2
«Duct tape for the HTML5
          web»
       Девиз Mojolicious #3
Mojolicious-модули на CPAN
• Mojolicious              • MojoX::Routes::AsGraph
• Mojo::Server::FCGI       • MojoX::Log::*
...
Mojolicious::Lite
       vs.
   Dancer
    Соревнование
Стабильная версия,
 совместимость?
Обратная
  совместимость
не гарантируется :)
  До первой стабильной версии
Mojolicious 0.999925
Последняя версия доступна на github.com
Стабильная версия,
 совместимость?
Документация
• Пока не очень, зато очень хороший фидбек :)
• Mojolicious::Lite и Mojolicious::Guides
• Mojolicious Handboo...
Полезная информация
• http://mojolicious.org
• irc://irc.perl.org/#mojo
• http://groups.google.com/group/mojolicious
• Git...
«Особая разновидность
современного программиста
 – программист, изучающий
        фреймворки»
«Каждый программист
 должен сделать 3 вещи:
фреймворк, шаблонизатор
 и событийную машину»
use Mojolicious
     or die;
Viva la revolution!
use Perl
  or die;
JFDI
any ‘/questions’ => sub {
	

 	

 shift->render(
	

 	

 	

 answer => ‘sharifulin’,
	

 	

 );
   };
Спасибо за внимание!
      Это 255й слайд
     Анатолий Шарифулин
анатолий шарифулин Mojolicious   финальная версия
анатолий шарифулин Mojolicious   финальная версия
анатолий шарифулин Mojolicious   финальная версия
анатолий шарифулин Mojolicious   финальная версия
Upcoming SlideShare
Loading in...5
×

анатолий шарифулин Mojolicious финальная версия

2,285

Published on

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
2,285
On Slideshare
0
From Embeds
0
Number of Embeds
5
Actions
Shares
0
Downloads
22
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

анатолий шарифулин Mojolicious финальная версия

  1. 1. Mojolicious. Веб в коробке! Анатолий Шарифулин
  2. 2. Sebastian Riedel автор Catalyst и Mojolicious http://twitter.com/kraih
  3. 3. Что такое Mojolicious?
  4. 4. Что такое Mojolicious? • Pure Perl веб-фреймворк • Без зависимостей (с версии Perl 5.8.1) • Объектно-ориентированное API (без скрытой магии) • Поддержка HTTP 1.1, WebSocket, IPv6, SSL, IDNA • Поддержка CGI, FastCGI, PSGI, Daemon и Prefork • Веб-фреймворки: Mojo, Mojolicious и Mojolicious::Lite
  5. 5. «Fresh code, based upon years of expirience developing Catalyst»
  6. 6. Pure Perl веб-фреймворк
  7. 7. • Mojo::Base • Mojo::ByteStream • Mojo::Template, Mojo::JSON • Mojo::Loader, Mojo::Log, Mojo::Path • Mojo::URL, Mojo::Parameters, Mojo::Content • Mojo::Message::Request, Mojo::Message::Response, Mojo::Headers, Mojo::Cookie, Mojo::Date
  8. 8. • Mojo::Base • Mojo::ByteStream • Mojo::Template, Mojo::JSON • Mojo::Loader, Mojo::Log, Mojo::Path • Mojo::URL, Mojo::Parameters, Mojo::Content • Mojo::Message::Request, Mojo::Message::Response, Mojo::Headers, Mojo::Cookie, Mojo::Date
  9. 9. Mojo::Base Minimal Base Class For Mojo Projects
  10. 10. package App; use base ‘Mojo::Base’; __PACKAGE__->attr(conf => sub { return do ‘conf/app.conf’; }); __PACKAGE__->attr(db => sub { my $self = shift; return Util->db($self->conf->{'db'}); }); sub dispatch { ... }
  11. 11. package App; use base ‘Mojo::Base’; __PACKAGE__->attr(conf => sub { return do ‘conf/app.conf’; }); __PACKAGE__->attr(db => sub { my $self = shift; return Util->db($self->conf->{'db'}); }); sub dispatch { ... }
  12. 12. package App; use base ‘Mojo::Base’; __PACKAGE__->attr(conf => sub { return do ‘conf/app.conf’; }); __PACKAGE__->attr(db => sub { my $self = shift; return Util->db($self->conf->{'db'}); }); sub dispatch { ... }
  13. 13. package App; use base ‘Mojo::Base’; __PACKAGE__->attr(conf => sub { return do ‘conf/app.conf’; }); __PACKAGE__->attr(db => sub { my $self = shift; return Util->db($self->conf->{'db'}); }); sub dispatch { ... }
  14. 14. • Mojo::Base • Mojo::ByteStream • Mojo::Template, Mojo::JSON • Mojo::Loader, Mojo::Log, Mojo::Path • Mojo::URL, Mojo::Parameters, Mojo::Content • Mojo::Message::Request, Mojo::Message::Response, Mojo::Headers, Mojo::Cookie, Mojo::Date
  15. 15. Mojo::ByteStream ByteStream
  16. 16. use Mojo::ByteStream; my $s = Mojo::ByteStream->new('foo_bar'); $s->camelize; $s->decamelize; $s->b64_encode; $s->b64_decode; $s->encode('utf8'); $s->decode('utf8'); $s->html_escape; $s->html_unescape; $s->qp_encode; $s->qp_decode; $s->quote; $stream->unquote; $s->url_escape; $s->url_unescape; $s->xml_escape; $s->punycode_encode; $s->punycode_decode;
  17. 17. use Mojo::ByteStream 'b'; my $s = b('foo_bar'); $s->camelize; $s->decamelize; $s->b64_encode; $s->b64_decode; $s->encode('utf8'); $s->decode('utf8'); $s->html_escape; $s->html_unescape; $s->qp_encode; $s->qp_decode; $s->quote; $stream->unquote; $s->url_escape; $s->url_unescape; $s->xml_escape; $s->punycode_encode; $s->punycode_decode;
  18. 18. use Mojo::ByteStream; my $s = Mojo::ByteStream->new('foo_bar'); $s->camelize; $s->decamelize; $s->b64_encode; $s->b64_decode; $s->encode('utf8'); $s->decode('utf8'); $s->html_escape; $s->html_unescape; $s->qp_encode; $s->qp_decode; $s->quote; $stream->unquote; $s->url_escape; $s->url_unescape; $s->xml_escape; $s->punycode_encode; $s->punycode_decode;
  19. 19. use Mojo::ByteStream; my $s = Mojo::ByteStream->new('foo_bar'); $s->camelize; $s->decamelize; $s->b64_encode; $s->b64_decode; $s->encode('utf8'); $s->decode('utf8'); $s->html_escape; $s->html_unescape; $s->qp_encode; $s->qp_decode; $s->quote; $stream->unquote; $s->url_escape; $s->url_unescape; $s->xml_escape; $s->punycode_encode; $s->punycode_decode;
  20. 20. use Mojo::ByteStream; my $s = Mojo::ByteStream->new('foo_bar'); $s->camelize; $s->decamelize; $s->b64_encode; $s->b64_decode; $s->encode('utf8'); $s->decode('utf8'); $s->html_escape; $s->html_unescape; $s->qp_encode; $s->qp_decode; $s->quote; $stream->unquote; $s->url_escape; $s->url_unescape; $s->xml_escape; $s->punycode_encode; $s->punycode_decode;
  21. 21. use Mojo::ByteStream; my $s = Mojo::ByteStream->new('foo_bar'); $s->camelize; $s->decamelize; $s->b64_encode; $s->b64_decode; $s->encode('utf8'); $s->decode('utf8'); $s->html_escape; $s->html_unescape; $s->qp_encode; $s->qp_decode; $s->quote; $stream->unquote; $s->url_escape; $s->url_unescape; $s->xml_escape; $s->punycode_encode; $s->punycode_decode;
  22. 22. use Mojo::ByteStream; my $s = Mojo::ByteStream->new('foo_bar'); $s->camelize; $s->decamelize; $s->b64_encode; $s->b64_decode; $s->encode('utf8'); $s->decode('utf8'); $s->html_escape; $s->html_unescape; $s->qp_encode; $s->qp_decode; $s->quote; $stream->unquote; $s->url_escape; $s->url_unescape; $s->xml_escape; $s->punycode_encode; $s->punycode_decode;
  23. 23. use Mojo::ByteStream; my $s = Mojo::ByteStream->new('foo_bar'); $s->camelize; $s->decamelize; $s->b64_encode; $s->b64_decode; $s->encode('utf8'); $s->decode('utf8'); $s->html_escape; $s->html_unescape; $s->qp_encode; $s->qp_decode; $s->quote; $stream->unquote; $s->url_escape; $s->url_unescape; $s->xml_escape; $s->punycode_encode; $s->punycode_decode;
  24. 24. use Mojo::ByteStream; my $s = Mojo::ByteStream->new('foo_bar'); $s->camelize; $s->decamelize; $s->b64_encode; $s->b64_decode; $s->encode('utf8'); $s->decode('utf8'); $s->html_escape; $s->html_unescape; $s->qp_encode; $s->qp_decode; $s->quote; $stream->unquote; $s->url_escape; $s->url_unescape; $s->xml_escape; $s->punycode_encode; $s->punycode_decode;
  25. 25. use Mojo::ByteStream; my $s = Mojo::ByteStream->new('foo_bar'); $s->camelize; $s->decamelize; $s->b64_encode; $s->b64_decode; $s->encode('utf8'); $s->decode('utf8'); $s->html_escape; $s->html_unescape; $s->qp_encode; $s->qp_decode; $s->quote; $stream->unquote; $s->url_escape; $s->url_unescape; $s->xml_escape; $s->punycode_encode; $s->punycode_decode;
  26. 26. use Mojo::ByteStream; my $s = Mojo::ByteStream->new('foo_bar'); $s->camelize; $s->decamelize; $s->b64_encode; $s->b64_decode; $s->encode('utf8'); $s->decode('utf8'); $s->html_escape; $s->html_unescape; $s->qp_encode; $s->qp_decode; $s->quote; $stream->unquote; $s->url_escape; $s->url_unescape; $s->xml_escape; $s->punycode_encode; $s->punycode_decode;
  27. 27. • Mojo::Base • Mojo::ByteStream • Mojo::Template, Mojo::JSON • Mojo::Loader, Mojo::Log, Mojo::Path • Mojo::URL, Mojo::Parameters, Mojo::Content • Mojo::Message::Request, Mojo::Message::Response, Mojo::Headers, Mojo::Cookie, Mojo::Date
  28. 28. Mojo::Template Perlish Templates!
  29. 29. % my $list = $self->stash(‘list’); <ul> % for (@$list) { <li> <%= $_->{title} %> </li> %} </ul> <p><%# comment %></p>
  30. 30. % my $list = $self->stash(‘list’); <ul> % for (@$list) { <li> <%= $_->{title} %> </li> %} </ul> <p><%# comment %></p>
  31. 31. % my $list = $self->stash(‘list’); <ul> % for (@$list) { <li> <%= $_->{title} %> </li> %} </ul> <p><%# comment %></p>
  32. 32. % my $count = 10 * 5; <ul> % for (@$list) { <li> <%= $_->{title} %> </li> %} </ul> <p><%# comment %></p>
  33. 33. % use Foo::Bar; <ul> % for (@$list) { <li> <%= $_->{title} %> </li> %} </ul> <p><%# comment %></p>
  34. 34. % my $list = $self->stash(‘list’); <ul> % for (@$list) { <li> <%= $_->{title} %> </li> %} </ul> <p><%# comment %></p>
  35. 35. % my $list = $self->stash(‘list’); <ul> % if (@$list) { <li> <%= $_->{title} %> </li> %} </ul> <p><%# comment %></p>
  36. 36. % my $list = $self->stash(‘list’); <ul> % for (@$list) { <li> <%= $_->{title} %> </li> %} </ul> <p><%# comment %></p>
  37. 37. % my $list = $self->stash(‘list’); <ul> % for (@$list) { <li> <%= $_->{title} %> </li> %} </ul> <p><%# comment %></p>
  38. 38. % my $list = $self->stash(‘list’); <ul> % for (@$list) { <li> <%== $_->{title} %> </li> %} </ul> <p><%# comment %></p>
  39. 39. % my $list = $self->stash(‘list’); <ul> % for (@$list) { <li> <%= ucfirst $_->{title} %> </li> %} </ul> <p><%# comment %></p>
  40. 40. % my $list = $self->stash(‘list’); <ul> % for (@$list) { <li> <%= $_->{title} %> </li> %} </ul> <p><%# comment %></p>
  41. 41. % my $list = $self->stash(‘list’); <ul> % for (@$list) { <li> <%= $_->{title} %> </li> %} </ul> <p><%# comment %></p>
  42. 42. 1 % my $list = $self->stash(‘list’); 2 3 <ul> 4 % for (@$list) { 5 <li> 6 <%= $_->{title} xx %> 7 </li> 8 %} 9 </ul> 10 11 <p><%# comment %></p>
  43. 43. Сообщение об ошибке Bareword "xx" not allowed while "strict subs" in use at template line 6.
  44. 44. Простота PHP, мощь Perl
  45. 45. • Mojo::Base • Mojo::ByteStream • Mojo::Template, Mojo::JSON • Mojo::Loader, Mojo::Log, Mojo::Path • Mojo::URL, Mojo::Parameters, Mojo::Content • Mojo::Message::Request, Mojo::Message::Response, Mojo::Headers, Mojo::Cookie, Mojo::Date
  46. 46. Mojo::JSON Minimalistic JSON
  47. 47. my $json = Mojo::JSON->new; my $string = $json->encode({foo => ‘bar’}); my $hash = $json->decode(‘{"foo":"bar"}’);
  48. 48. my $json = Mojo::JSON->new; my $string = $json->encode({foo => ‘bar’}); my $hash = $json->decode(‘{"foo":"bar"}’);
  49. 49. • Mojo::Base • Mojo::ByteStream • Mojo::Template, Mojo::JSON • Mojo::Loader, Mojo::Log, Mojo::Path • Mojo::URL, Mojo::Parameters, Mojo::Content • Mojo::Message::Request, Mojo::Message::Response, Mojo::Headers, Mojo::Cookie, Mojo::Date
  50. 50. Mojo::Loader Class loader: load, reload, search
  51. 51. • Mojo::Base • Mojo::ByteStream • Mojo::Template, Mojo::JSON • Mojo::Loader, Mojo::Log, Mojo::Path • Mojo::URL, Mojo::Parameters, Mojo::Content • Mojo::Message::Request, Mojo::Message::Response, Mojo::Headers, Mojo::Cookie, Mojo::Date
  52. 52. • Mojo::Base • Mojo::ByteStream • Mojo::Template, Mojo::JSON • Mojo::Loader, Mojo::Log, Mojo::Path • Mojo::URL, Mojo::Parameters, Mojo::Content • Mojo::Message::Request, Mojo::Message::Response, Mojo::Headers, Mojo::Cookie, Mojo::Date
  53. 53. • Mojo::Base • Mojo::ByteStream • Mojo::Template, Mojo::JSON • Mojo::Loader, Mojo::Log, Mojo::Path • Mojo::URL, Mojo::Parameters, Mojo::Content • Mojo::Message::Request, Mojo::Message::Response, Mojo::Headers, Mojo::Cookie, Mojo::Date
  54. 54. • Mojo::Trasaction • Mojo::IOLoop • Mojo::Client, Mojo::Server • Mojo::Server::CGI, Mojo::Server::FastCGI • Mojo::Server::PSGI • Mojo::Server::Daemon и ::Prefork • Mojo::Command • Mojo::Command::Generate и ~ Server
  55. 55. • Mojo::Trasaction • Mojo::IOLoop • Mojo::Client, Mojo::Server • Mojo::Server::CGI, Mojo::Server::FastCGI • Mojo::Server::PSGI • Mojo::Server::Daemon и ::Prefork • Mojo::Command • Mojo::Command::Generate и ~ Server
  56. 56. • Mojo::Trasaction • Mojo::IOLoop • Mojo::Client, Mojo::Server • Mojo::Server::CGI, Mojo::Server::FastCGI • Mojo::Server::PSGI • Mojo::Server::Daemon и ::Prefork • Mojo::Command • Mojo::Command::Generate и ~ Server
  57. 57. Mojo::IOLoop Minimalistic Reactor For TCP Clients And Servers
  58. 58. Mojo::IOLoop Поддержка IO::KQueue, IO::Epoll, IO::Socket::INET6 и IO::Socket::SSL
  59. 59. my $loop = Mojo::IOLoop->new; $loop->listen(port => 3000, cb => sub { my ($self, $id) = @_; $self->read_cb ($id => sub { ... }); $self->write_cb($id => sub { ... }); ); my $id = $loop->connect(port => 3000, ...); $loop->start; $loop->stop;
  60. 60. my $loop = Mojo::IOLoop->new; $loop->listen(port => 3000, cb => sub { my ($self, $id) = @_; $self->read_cb ($id => sub { ... }); $self->write_cb($id => sub { ... }); ); my $id = $loop->connect(port => 3000, ...); $loop->start; $loop->stop;
  61. 61. my $loop = Mojo::IOLoop->new; $loop->listen(port => 3000, cb => sub { my ($self, $id) = @_; $self->read_cb ($id => sub { ... }); $self->write_cb($id => sub { ... }); ); my $id = $loop->connect(port => 3000, ...); $loop->start; $loop->stop;
  62. 62. my $loop = Mojo::IOLoop->new; $loop->listen(port => 3000, cb => sub { my ($self, $id) = @_; $self->read_cb ($id => sub { ... }); $self->write_cb($id => sub { ... }); ); my $id = $loop->connect(port => 3000, ...); $loop->start; $loop->stop;
  63. 63. • Mojo::Trasaction • Mojo::IOLoop • Mojo::Client, Mojo::Server • Mojo::Server::CGI, Mojo::Server::FastCGI • Mojo::Server::PSGI • Mojo::Server::Daemon и ::Prefork • Mojo::Command • Mojo::Command::Generate и ~ Server
  64. 64. Mojo::Client Async IO HTTP 1.1 And WebSocket Client
  65. 65. my $client = Mojo::Client->new; $client->get( ‘http://goo.gl’ => sub { my ($self, $tx) = @_; say $tx->res; } )->process;
  66. 66. my $client = Mojo::Client->new; $self->client->get( ‘http://goo.gl’ => sub { my ($self, $tx) = @_; say $tx->res; } )->process;
  67. 67. my $client = Mojo::Client->new; $client->get( ‘http://goo.gl’ => sub { my ($self, $tx) = @_; say $tx->res; } )->process;
  68. 68. my $client = Mojo::Client->new; $client->head( ‘http://goo.gl’ => sub { my ($self, $tx) = @_; say $tx->res; } )->process;
  69. 69. my $client = Mojo::Client->new; $client->post( ‘http://goo.gl’ => sub { my ($self, $tx) = @_; say $tx->res; } )->process;
  70. 70. my $client = Mojo::Client->new; $client->put( ‘http://goo.gl’ => sub { my ($self, $tx) = @_; say $tx->res; } )->process;
  71. 71. my $client = Mojo::Client->new; $client->async->get( ‘http://goo.gl’ => sub { my ($self, $tx) = @_; say $tx->res; } )->process;
  72. 72. my $client = Mojo::Client->new; $client->get( ‘http://goo.gl’ => sub { my ($self, $tx) = @_; say $tx->res; } )->process;
  73. 73. my $client = Mojo::Client->new; $client->get( ‘http://goo.gl’ => sub { my ($self, $tx) = @_; say $tx->res; } )->process;
  74. 74. my $client = Mojo::Client->new; $client->get( ‘http://goo.gl’ => sub { my ($self, $tx) = @_; say $tx->res; } )->process;
  75. 75. my $client = Mojo::Client->new; $client->get(‘http://goo.gl’)->res->code; $client->get( 'http://search.twitter.com/trends.json' )->success->json->{trends}->[0]->{name}
  76. 76. my $client = Mojo::Client->new; $client->get(‘http://goo.gl’)->res->code; $client->get( 'http://search.twitter.com/trends.json' )->success->json->{trends}->[0]->{name}
  77. 77. script/mojolicious get 'http://goo.gl'
  78. 78. • Mojo::Trasaction • Mojo::IOLoop • Mojo::Client, Mojo::Server • Mojo::Server::CGI, Mojo::Server::FastCGI • Mojo::Server::PSGI • Mojo::Server::Daemon и ::Prefork • Mojo::Command • Mojo::Command::Generate и ~ Server
  79. 79. • Mojo::Trasaction • Mojo::IOLoop • Mojo::Client, Mojo::Server • Mojo::Server::CGI, Mojo::Server::FastCGI • Mojo::Server::PSGI • Mojo::Server::Daemon и ::Prefork • Mojo::Command • Mojo::Command::Generate и ~ Server
  80. 80. • Mojo::Trasaction • Mojo::IOLoop • Mojo::Client, Mojo::Server • Mojo::Server::CGI, Mojo::Server::FastCGI • Mojo::Server::PSGI • Mojo::Server::Daemon и ::Prefork • Mojo::Command • Mojo::Command::Generate и ~ Server
  81. 81. Всё, что нужно – есть Mojolicious – веб в коробке!
  82. 82. Без зависимостей с версии Perl 5.8.1
  83. 83. Mojo runtime
  84. 84. Perl 5.005
  85. 85. Perl 5.006002
  86. 86. Perl 5.008001
  87. 87. Perl 5.010
  88. 88. Объектно- ориентированное API
  89. 89. package App; use base 'Mojo'; sub handler { my ($self, $tx) = @_; warn $tx->req; warn $tx->req->url; $tx->res->headers ->content_type('text/plain'); $tx->res->body('РИТ++ 2010'); } 1;
  90. 90. package App; use base 'Mojo'; sub handler { my ($self, $tx) = @_; warn $tx->req; warn $tx->req->url; $tx->res->headers ->content_type('text/plain'); $tx->res->body('РИТ++ 2010'); } 1;
  91. 91. package App; use base 'Mojo'; sub handler { my ($self, $tx) = @_; warn $tx->req; warn $tx->req->url; $tx->res->headers ->content_type('text/plain'); $tx->res->body('РИТ++ 2010'); } 1;
  92. 92. package App; use base 'Mojo'; sub handler { my ($self, $tx) = @_; warn $tx->req; warn $tx->req->url; $tx->res->headers ->content_type('text/plain'); $tx->res->body('РИТ++ 2010'); } 1;
  93. 93. package App; use base 'Mojo'; sub handler { my ($self, $tx) = @_; warn $tx->req; warn $tx->req->url; $tx->res->headers ->content_type('text/plain'); $tx->res->body('РИТ++ 2010'); } 1;
  94. 94. package App; use base 'Mojo'; sub handler { my ($self, $tx) = @_; warn $tx->req; warn $tx->req->url; $tx->res->headers ->content_type('text/plain'); $tx->res->body('РИТ++ 2010'); } 1;
  95. 95. use overload '""' => sub { shift->to_string }, fallback => 1;
  96. 96. GET / HTTP/1.1 Connection: keep-alive Accept: text/html, application/xhtml, .... Accept-Charset: windows-1251, utf-8; ... Accept-Encoding: gzip,deflate Accept-Language: ru,en-us;q=0.7,en;q=0.3 Host: localhost:3000 User-Agent: Mozilla/5.0 (Macintosh; ... Content-Length: 0 Keep-Alive: 300
  97. 97. Поддержка HTTP 1.1 клиент-сервер
  98. 98. Google Summer of Code 2009 Pascal Gaudette MojoX::UserAgent
  99. 99. Поддержка WebSocket клиент-сервер
  100. 100. Протокол WebSocket Google, Inc 16 декабря 2009 года
  101. 101. use Mojolicious::Lite; websocket ‘/echo’ => sub { my $self = shift; $self->receive_message(sub { my ($self, $msg) = @_; $self->send_massage("echo: $msg"); }); }; app->start;
  102. 102. use Mojolicious::Lite; websocket ‘/echo’ => sub { my $self = shift; $self->receive_message(sub { my ($self, $msg) = @_; $self->send_massage("echo: $msg"); }); }; app->start;
  103. 103. Mojolicious WebSocket Examples Интересный пример IRC-клиент Github.com @xantus
  104. 104. Google Summer of Code 2010 Performance tuning the http/websocket implementation
  105. 105. Поддержка CGI, FastCGI, PSGI, Daemon и Prefork
  106. 106. script/mojolicious COMMAND [OPTIONS] script/mojolicious cgi script/mojolicious fastcgi script/mojolicious psgi script/mojolicious daemon script/mojolicious daemon_prefork
  107. 107. script/mojolicious COMMAND [OPTIONS] script/mojolicious cgi script/mojolicious fastcgi script/mojolicious psgi script/mojolicious daemon script/mojolicious daemon_prefork
  108. 108. PATH_INFO='/foo/bar' script/mojolicious cgi Легко тестировать Можно профилировать код, используя Devel::NYTProf
  109. 109. script/mojolicious COMMAND [OPTIONS] script/mojolicious cgi script/mojolicious fastcgi script/mojolicious psgi script/mojolicious daemon script/mojolicious daemon_prefork
  110. 110. Mojo::Server::FCGI script/mojolicious fcgi script/mojolicious fcgi_prefork
  111. 111. script/mojolicious COMMAND [OPTIONS] script/mojolicious cgi script/mojolicious fastcgi script/mojolicious psgi script/mojolicious daemon script/mojolicious daemon_prefork
  112. 112. script/mojolicious COMMAND [OPTIONS] script/mojolicious cgi script/mojolicious fastcgi script/mojolicious psgi script/mojolicious daemon script/mojolicious daemon_prefork
  113. 113. script/mojolicious daemon --reload Приложение перезагружается перед запросом, если код был изменен
  114. 114. Обработка сигнала USR1 Для обновления кода приложения
  115. 115. script/mojolicious COMMAND [OPTIONS] script/mojolicious cgi script/mojolicious fastcgi script/mojolicious psgi script/mojolicious daemon script/mojolicious daemon_prefork
  116. 116. Веб-фреймворки
  117. 117. Mojo Base framework
  118. 118. package App; use base 'Mojo'; sub handler { my ($self, $tx) = @_; warn $tx->req; warn $tx->req->url; $tx->res->headers ->content_type('text/plain'); $tx->res->body('РИТ++ 2010'); } 1;
  119. 119. package App; use base 'Mojo'; sub handler { my ($self, $tx) = @_; warn $tx->req; warn $tx->req->url; $tx->res->headers ->content_type('text/plain'); $tx->res->body('РИТ++ 2010'); } 1;
  120. 120. Генерация mojo-приложения
  121. 121. script/mojo generate app App [write ] app/script/app [chmod] app/script/app 744 [write ] app/lib/App.pm [write ] app/t/basic.t [mkdir ] app/log
  122. 122. script/app COMMAND [OPTIONS] script/app cgi script/app fastcgi script/app psgi script/app daemon script/app daemon_prefork
  123. 123. script/app COMMAND [OPTIONS] script/app get script/app test
  124. 124. MVC веб-фреймворки
  125. 125. Mojolicious use base 'Mojo';
  126. 126. • Mojolicous::Controller • Mojolicious::Plugins • Mojolicious::Commands • MojoX::Types • MojoX::Session • MojoX::Dispatcher, MojoX::Routes
  127. 127. • Mojolicous::Controller • Mojolicious::Plugins • Mojolicious::Commands • MojoX::Types • MojoX::Session • MojoX::Dispatcher, MojoX::Routes
  128. 128. Mojolicious::Controller Controller Base Class
  129. 129. • render • render_text • render_data • render_json • render_static • render_not_found / render_exception • send_message / receive_message • url_for / redirect_to
  130. 130. • render(controller => 'foo', action => 'bar') • render_text • render_data • render_json • render_static • render_not_found / render_exception • send_message / receive_message • url_for / redirect_to
  131. 131. • render(template => 'foo/bar') • render_text • render_data • render_json • render_static • render_not_found / render_exception • send_message / receive_message • url_for / redirect_to
  132. 132. • render('foo#bar') • render_text • render_data • render_json • render_static • render_not_found / render_exception • send_message / receive_message • url_for / redirect_to
  133. 133. • render('foo#bar', format => 'html') • render_text • render_data • render_json • render_static • render_not_found / render_exception • send_message / receive_message • url_for / redirect_to
  134. 134. • render • render_text • render_data • render_json • render_static • render_not_found / render_exception • send_message / receive_message • url_for / redirect_to
  135. 135. • render • render_text('РИТ++ 2010') • render_data • render_json • render_static • render_not_found / render_exception • send_message / receive_message • url_for / redirect_to
  136. 136. • render • render(text => 'РИТ++ 2010') • render_data • render_json • render_static • render_not_found / render_exception • send_message / receive_message • url_for / redirect_to
  137. 137. • render • render_text • render_data • render_json • render_static • render_not_found / render_exception • send_message / receive_message • url_for / redirect_to
  138. 138. • render • render_text • render_data('binary data') • render_json • render_static • render_not_found / render_exception • send_message / receive_message • url_for / redirect_to
  139. 139. • render • render_text • render(data => 'binary data') • render_json • render_static • render_not_found / render_exception • send_message / receive_message • url_for / redirect_to
  140. 140. • render • render_text • render_data • render_json • render_static • render_not_found / render_exception • send_message / receive_message • url_for / redirect_to
  141. 141. • render • render_text • render_data • render_json({foo => 'bar'}) • render_static • render_not_found / render_exception • send_message / receive_message • url_for / redirect_to
  142. 142. • render • render_text • render_data • render(json => {foo => 'bar'}) • render_static • render_not_found / render_exception • send_message / receive_message • url_for / redirect_to
  143. 143. • render • render_text • render_data • render_json • render_static • render_not_found / render_exception • send_message / receive_message • url_for / redirect_to
  144. 144. • render • render_text • render_data • render_json • render_static('img/logo.png') • render_not_found / render_exception • send_message / receive_message • url_for / redirect_to
  145. 145. • render • render_text • render_data • render_json • render_static • render_not_found / render_exception • send_message / receive_message • url_for / redirect_to
  146. 146. • render • render_text • render_data • render_json • render_static • render_not_found / render_exception • send_message / receive_message • url_for / redirect_to
  147. 147. • render • render_text • render_data • render_json • render_static • render_not_found / render_exception • send_message / receive_message • url_for / redirect_to
  148. 148. • Mojolicous::Controller • Mojolicious::Plugins • Mojolicious::Commands • MojoX::Types • MojoX::Session • MojoX::Dispatcher, MojoX::Routes
  149. 149. Mojolicious::Plugins Plugins
  150. 150. • AgentCondition • Charset • DefaultHelpers • EplRenderer • EpRenderer • HeaderCondition • I18n, JsonConfig, PoweredBy • PodRenderer, RequestTimer
  151. 151. • AgentCondition • Charset • DefaultHelpers • EplRenderer • EpRenderer • HeaderCondition • I18n, JsonConfig, PoweredBy • PodRenderer, RequestTimer
  152. 152. • dumper • param • stash • layout • include • content • extends • url_for
  153. 153. • Data::Dumper (Maxdepth: 2, Indent: 1, Terse: 1) • param • stash • layout • include • content • extends • url_for
  154. 154. • dumper • param • stash • layout • include • content • extends • url_for
  155. 155. • dumper • $self->req->param( ... ) • stash • layout • include • content • extends • url_for
  156. 156. • dumper • param • stash • layout • include • content • extends • url_for
  157. 157. • dumper • param • $self->stash( ... ) • layout • include • content • extends • url_for
  158. 158. • dumper • param • stash • layout • include • content • extends • url_for
  159. 159. • dumper • param • stash • layout • include • content • extends • url_for
  160. 160. • dumper • param • stash • layout • include • content • extends • url_for
  161. 161. • dumper • param • stash • layout • include • content • extends • url_for
  162. 162. • dumper • param • stash • layout • include • content • extends • url_for
  163. 163. • dumper • param • stash • layout • include • content • extends • $self->url_for
  164. 164. • AgentCondition • Charset • DefaultHelpers • EplRenderer • EpRenderer • HeaderCondition • I18n, JsonConfig, PoweredBy • PodRenderer, RequestTimer
  165. 165. EplRenderer Embed Perl Lite
  166. 166. • расширение шаблонов .epl • в начале каждого шаблона: % my $self = shift; • $self->stash('foo')
  167. 167. • AgentCondition • Charset • DefaultHelpers • EplRenderer • EpRenderer • HeaderCondition • I18n, JsonConfig, PoweredBy • PodRenderer, RequestTimer
  168. 168. EpRenderer Embed Perl
  169. 169. • расширение шаблонов .ep • кеширование шаблонов со stash-параметрами • вместо $self->stash('foo') – $foo • доступны все helpers: % layout 'default'; % warn dumper $list; • обработчик по умолчанию в mojolicious
  170. 170. • Mojolicous::Controller • Mojolicious::Plugins • Mojolicious::Commands • MojoX::Types • MojoX::Session • MojoX::Dispatcher, MojoX::Routes
  171. 171. • Mojolicous::Controller • Mojolicious::Plugins • Mojolicious::Commands • MojoX::Types • MojoX::Session • MojoX::Dispatcher, MojoX::Routes
  172. 172. • Mojolicous::Controller • Mojolicious::Plugins • Mojolicious::Commands • MojoX::Types • MojoX::Session • MojoX::Dispatcher, MojoX::Routes
  173. 173. • Mojolicous::Controller • Mojolicious::Plugins • Mojolicious::Commands • MojoX::Types • MojoX::Session • MojoX::Dispatcher, MojoX::Routes
  174. 174. Генерация mojolicious-приложения
  175. 175. script/mojolicious generate app App [write ] app/script/app [chmod] app/script/app 744 [write ] app/lib/App.pm [write ] app/lib/App/Example.pm [write ] app/t/basic.t [mkdir ] app/log
  176. 176. package App; use base 'Mojolicious'; sub startup { my $self = shift; my $r = $self->routes; $r->route('/:controller/:action/:id') ->to('example#welcome', id => 1); } 1;
  177. 177. package App; use base 'Mojolicious'; sub startup { my $self = shift; my $r = $self->routes; $r->route('/:controller/:action/:id') ->to('example#welcome', id => 1); } 1;
  178. 178. package App; use base 'Mojolicious'; sub startup { my $self = shift; my $r = $self->routes; $r->route('/:controller/:action/:id') ->to('example#welcome', id => 1); } 1;
  179. 179. package App; use base 'Mojolicious'; sub startup { my $self = shift; my $r = $self->routes; $r->route('/:controller/:action/:id') ->to('example#welcome', id => 1); } 1;
  180. 180. package App; use base 'Mojolicious'; sub startup { my $self = shift; my $r = $self->routes; $r->route('/:controller/:action/:id') ->to('example#welcome', id => 1); } 1;
  181. 181. package App; use base 'Mojolicious'; sub startup { my $self = shift; my $r = $self->routes; $r->route('/:id', id => qr/d+/) ->to('example#welcome'); } 1;
  182. 182. package App; use base 'Mojolicious'; sub startup { my $self = shift; my $r = $self->routes; for ($r->bridge->to('auth#check) { $_->route('/admin')->to('admin#welcome'); } }
  183. 183. package App; use base 'Mojolicious'; sub startup { my $self = shift; my $r = $self->routes; for ($r->bridge->to('auth#check) { $_->route('/admin')->to('admin#welcome'); } }
  184. 184. package App; use base 'Mojolicious'; sub startup { my $self = shift; my $r = $self->routes; $r->route('/:controller/:action/:id') ->to('example#welcome', id => 1); } 1;
  185. 185. script/mojolicious generate app App [write ] app/script/app [chmod] app/script/app 744 [write ] app/lib/App.pm [write ] app/lib/App/Example.pm [write ] app/t/basic.t [mkdir ] app/log
  186. 186. package App::Example; use base 'Mojolicious::Controller'; sub welcome { my $self = shift; warn $self->stash(‘id’); $self->render(message => 'РИТ++ 2010'); } 1;
  187. 187. package App::Example; use base 'Mojolicious::Controller'; sub welcome { my $self = shift; warn $self->stash(‘id’); $self->render(message => 'РИТ++ 2010'); } 1;
  188. 188. package App::Example; use base 'Mojolicious::Controller'; sub welcome { my $self = shift; warn $self->stash(‘id’); $self->render(message => 'РИТ++ 2010'); } 1;
  189. 189. script/mojolicious generate app App [mkdir] app/public [write ] app/templates/not_found.html.ep [write ] app/templates/exception.html.ep [write ] app/templates/layouts/default.html.ep [write ] app/templates/example/welcome.html.ep
  190. 190. script/mojolicious generate app App [mkdir] app/public [write ] app/templates/not_found.html.ep [write ] app/templates/exception.html.ep [write ] app/templates/layouts/default.html.ep [write ] app/templates/example/welcome.html.ep
  191. 191. script/mojolicious generate app App [mkdir] app/public [write ] app/templates/not_found.html.ep [write ] app/templates/exception.html.ep [write ] app/templates/layouts/default.html.ep [write ] app/templates/example/welcome.html.ep
  192. 192. % layout 'default'; <h2><%= $message %></h2> <a href="<%== url_for %>">click here</a>
  193. 193. % layout 'default'; <h2><%= $message %></h2> <a href="<%== url_for %>">click here</a>
  194. 194. % layout 'default'; <h2><%= $self->stash('message') %></h2> <a href="<%== url_for %>">click here</a>
  195. 195. % layout 'default'; <h2><%= $message %></h2> <a href="<%== url_for %>">click here</a>
  196. 196. % layout 'default'; <h2><%= stash 'message' %></h2> <a href="<%== url_for %>">click here</a>
  197. 197. % layout 'default'; <h2><%= $message %></h2> <a href="<%== url_for %>">click here</a>
  198. 198. script/mojolicious generate app App [mkdir] app/public [write ] app/templates/not_found.html.ep [write ] app/templates/exception.html.ep [write ] app/templates/layouts/default.html.ep [write ] app/templates/example/welcome.html.ep
  199. 199. <!doctype html> <html> <head><title>Welcome</title></head> <body> <%== content %> </body> </html>
  200. 200. <!doctype html> <html> <head><title>Welcome</title></head> <body> <%== content %> </body> </html>
  201. 201. script/mojolicious generate app App [mkdir] app/public [write ] app/templates/not_found.html.ep [write ] app/templates/exception.html.ep [write ] app/templates/layouts/default.html.ep [write ] app/templates/example/welcome.html.ep
  202. 202. script/mojolicious generate app App [mkdir] app/public [write ] app/templates/not_found.html.ep [write ] app/templates/exception.html.ep [write ] app/templates/layouts/default.html.ep [write ] app/templates/example/welcome.html.ep
  203. 203. Global symbol "$message2" requires explicit package name at (eval 280) line 2. 1: % layout 'default'; 2: <h2><%= $message2 %></h2> 3: ... { 'status' => 500, 'message' => ‘РИТ++ 2010’, ...
  204. 204. example/ welcome.html.ep Автоматическая генерация имени шаблона
  205. 205. controller/ action.format.handler Автоматическая генерация имени шаблона
  206. 206. example/ welcome.html.ep Автоматическая генерация имени шаблона
  207. 207. example/ welcome.xml.ep Автоматическая генерация имени шаблона
  208. 208. example/ welcome.rss.ep Автоматическая генерация имени шаблона
  209. 209. example/ welcome.json.ep Автоматическая генерация имени шаблона
  210. 210. example/ welcome.html.ep Автоматическая генерация имени шаблона
  211. 211. example/ welcome.html.epl Автоматическая генерация имени шаблона
  212. 212. example/ welcome.html.tt Автоматическая генерация имени шаблона
  213. 213. example/ welcome.html.cttp2 Автоматическая генерация имени шаблона
  214. 214. script/app COMMAND [OPTIONS] script/app cgi script/app fastcgi script/app psgi script/app daemon script/app daemon_prefork
  215. 215. script/app COMMAND [OPTIONS] script/app get script/app test script/app routes
  216. 216. Mojolicious::Lite use base 'Mojolicious';
  217. 217. script/mojolicious generate lite_app lite.pl [write ] lite.pl [chmod] lite.pl 744
  218. 218. use Mojolicious::Lite; get '/' => 'index'; get '/:groovy' => sub { my $self = shift; $self->render_text( $self->param('groovy') ); }; app->start;
  219. 219. use Mojolicious::Lite; get '/' => 'index'; get '/:groovy' => sub { my $self = shift; $self->render_text( $self->param('groovy') ); }; app->start;
  220. 220. use Mojolicious::Lite; get '/' => 'index'; get '/:groovy' => sub { my $self = shift; $self->render_text( $self->param('groovy') ); }; app->start;
  221. 221. use Mojolicious::Lite; post '/' => 'index'; get '/:groovy' => sub { my $self = shift; $self->render_text( $self->param('groovy') ); }; app->start;
  222. 222. use Mojolicious::Lite; any '/' => 'index'; get '/:groovy' => sub { my $self = shift; $self->render_text( $self->param('groovy') ); }; app->start;
  223. 223. use Mojolicious::Lite; get '/' => (agent => qr/Firefox/) => 'index'; get '/:groovy' => sub { my $self = shift; $self->render_text( $self->param('groovy') ); }; app->start;
  224. 224. use Mojolicious::Lite; get '/' => 'index'; get '/:groovy' => {groovy => 42} => sub { my $self = shift; $self->render_text( $self->param('groovy') ); }; app->start;
  225. 225. use Mojolicious::Lite; get '/' => 'index'; get '/:groovy' => [groovy => qr/d+/] => sub { my $self = shift; $self->render_text( $self->param('groovy') ); }; app->start;
  226. 226. use Mojolicious::Lite; ladder sub {}; # auth get '/' => 'index'; get '/:groovy' => sub { my $self = shift; $self->render_text( $self->param('groovy') ); }; app->start;
  227. 227. use Mojolicious::Lite; websocket '/echo' => sub { ... }; get '/' => 'index'; get '/:groovy' => sub { my $self = shift; $self->render_text( $self->param('groovy') ); }; app->start;
  228. 228. use Mojolicious::Lite; get '/' => 'index'; get '/:groovy' => sub { my $self = shift; $self->render_text( $self->param('groovy') ); }; app->renderer->default_handler('epl'); app->start;
  229. 229. use Mojolicious::Lite; get '/' => 'index'; get '/:groovy' => sub { my $self = shift; $self->render_text( $self->param('groovy') ); }; app->start;
  230. 230. __DATA__ @@ index.html.ep % layout 'funky'; Yea baby! @@ layouts/funky.html.ep <!doctype html><html> <head><title>Funky!</title></head> <body><%== content %></body> </html>
  231. 231. __DATA__ @@ index.html.ep % layout 'funky'; Yea baby! @@ layouts/funky.html.ep <!doctype html><html> <head><title>Funky!</title></head> <body><%== content %></body> </html>
  232. 232. lite.pl COMMAND [OPTIONS] lite.pl cgi lite.pl fastcgi lite.pl psgi lite.pl daemon lite.pl daemon_prefork
  233. 233. lite.pl COMMAND [OPTIONS] lite.pl test lite.pl get lite.pl routes lite.pl inflate
  234. 234. «Making hard things possible and everything fun!» Девиз Mojolicious
  235. 235. «Viva la revolution!» Девиз Mojolicious #2
  236. 236. «Duct tape for the HTML5 web» Девиз Mojolicious #3
  237. 237. Mojolicious-модули на CPAN • Mojolicious • MojoX::Routes::AsGraph • Mojo::Server::FCGI • MojoX::Log::* • AnyEvent::Mojo • MojoX::Renderer::* • Apache::Mojo Apache2::Mojo • TT • Catalyst::Engine::Mojo • CTTP2, HTP Squatting::On::Mojo • XSLT
  238. 238. Mojolicious::Lite vs. Dancer Соревнование
  239. 239. Стабильная версия, совместимость?
  240. 240. Обратная совместимость не гарантируется :) До первой стабильной версии
  241. 241. Mojolicious 0.999925 Последняя версия доступна на github.com
  242. 242. Стабильная версия, совместимость?
  243. 243. Документация • Пока не очень, зато очень хороший фидбек :) • Mojolicious::Lite и Mojolicious::Guides • Mojolicious Handbook на github.com @kvorg • Mojolicious FAQ на github.com @vti
  244. 244. Полезная информация • http://mojolicious.org • irc://irc.perl.org/#mojo • http://groups.google.com/group/mojolicious • Github: kraih, vti, xantus • Twitter: @kraih, @vtivti, @sharifulin • Juick: @vti, @sharifulin
  245. 245. «Особая разновидность современного программиста – программист, изучающий фреймворки»
  246. 246. «Каждый программист должен сделать 3 вещи: фреймворк, шаблонизатор и событийную машину»
  247. 247. use Mojolicious or die;
  248. 248. Viva la revolution!
  249. 249. use Perl or die;
  250. 250. JFDI
  251. 251. any ‘/questions’ => sub { shift->render( answer => ‘sharifulin’, ); };
  252. 252. Спасибо за внимание! Это 255й слайд Анатолий Шарифулин
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×