Mojolicious - A Web Divertida!

Breno G. de Oliveira
Publicado em 21/12/2012

Mojolicious - A Web Divertida!

Nos primórdios da Web, sites dinâmicos eram feitos com esse maravilhoso módulo chamado CGI.pm. O tempo foi passando, o uso da Web aumentando, novos padrões surgiram, a tecnologia evoluiu e a abordagem CGI tradicional já não era suficiente para atender as novas demandas dos sites populares. A era de ouro do CGI havia acabado.

Hoje a Web é um dos veículos de comunicação mais utilizados no mundo. Sites dinâmicos são a regra, com Web Services, REST, bibliotecas JavaScript, e tudo que você possa imaginar. Frameworks robustos e poderosos como o Catalyst oferecem soluções escaláveis e elegantes para seu site, compartimentando seus aplicativos no melhor estilo MVC.

Infelizmente, no entanto, a curva de aprendizado para tais frameworks é relativamente alta e exige certo comprometimento. O "programador casual" pode se sentir inibido a experimentar algo com configurações complexas ou muitos pré-requisitos. O que fazer?

Mojolicious é um framework Web moderno focado em minimalismo e simplicidade. Foi desenvolvido por Sebastian Riedel, criador do Catalyst, baseado em anos de experiência em desenvolvimento Web. Além dos tradicionais - e essenciais - controles de sessão e gerenciamento de cookies, Mojolicious traz o estado da arte em tecnologia de uma forma elegante e escalável, com suporte completo a HTTP 1.1, IPv6, TLS, Web Sockets, E/S assíncrona, templates, internacionalização (i18n), logging, Web Services e muito mais, tudo isso sem nenhum requisito não-core do Perl!

E o melhor: Mojolicious é um framework web simples o suficiente para você experimentar sem precisar de grandes conhecimentos de Perl 5, e ao mesmo tempo poderoso e flexível para mantê-lo empolgado durante todo o processo!

Mas chega de falatório: vamos aprender fazendo!

Instalação

Instalar Mojolicious direto do CPAN é uma moleza:

  cpan> install Mojolicious

Opcionalmente, você pode baixar a última versão aqui e instalar manualmente:

  perl Makefile.PL
  make && make test && sudo make install

Como não depende de nenhum módulo não-core do Perl 5, a instalação é rápida e não deve trazer nenhuma surpresa.

Mojolicious::Lite

A melhor maneira de experimentar Mojolicious é através do Mojolicious::Lite, uma interface simples construída em torno do Mojolicious e muito fácil de usar. Vamos chamar nossa aplicação de myapp.pl:

  #!/usr/bin/perl
  use Mojolicious::Lite;

  get '/' => 'index';

  shagadelic;

Pronto!

Pronto?!

Sim, isso mesmo, pronto! A declaração "use Mojolicious::Lite" ativa strict e warnings automaticamente, evitando erros comuns de programação. Ela também importa as funções que usaremos em nossa aplicação Web.

Nosso primeiro exemplo, claro, é muito simples, composto apenas pela linha que diz "get '/' => 'index'". Trata-se de uma indicação de que, se alguém fizer uma requisição do tipo get para a raiz de nossa aplicação ('/'), devemos renderizar o template 'index', localizado no diretório 'templates. 'index' aqui é o nome que demos a essa rota específica, e poderemos referenciá-la a qualquer momento em outras rotas para redirecionamento, obtenção de url correspondente, etc.

Finalmente, o comando 'shagadelic' inicia sua aplicação Mojolicious.

Executar então é mais fácil ainda:

   myapp.pl daemon

Isso vai ativar um servidor de desenvolvimento na máquina local, para que você possa testar sua aplicação diretamente do navegador web sem se preocupar em instalar/configurar servidores complexos. Rode sem argumentos para ver diferentes modos de execução, ou digite myapp.pl help COMANDO para saber como personalizar a execução ainda mais. Você pode, por exemplo, executar o modo daemon com a opção --reload, que vai reiniciar o servidor de desenvolvimento automaticamente sempre que você fizer modificações no arquivo!

Mas não se preocupe: na hora de botar seu aplicativo em produção num servidor web robusto como Apache ou lighttp, o ambiente costuma ser detectado automaticamente, facilitando ainda mais o processo. Caso contrário, você pode sempre passar o modo de execução diretamente para a função shagadelic (por exemplo, shagadelic('fastcgi')).

Vamos deixar nossa aplicação em execução no modo daemon e abrir nosso navegador Web em http://localhost:3000. Uma mensagem simples dizendo 'File Not Found' ("Arquivo Não Encontrado") é exibida. Claro, não criamos nosso template!

Template integrado: ep (Embedded Perl)

Vamos criar rapidamente o arquivo templates/index.html.ep com o seguinte conteúdo:

  
     

Feliz Equinócio!!

Recarregando a página em nosso navegador, vemos que o site agora encontra e renderiza nosso arquivo de template corretamente. Mas o conteúdo é estático, e queremos adicionar elementos dinamicamente. Sem problemas! Mojolicious vem com um sistema de templates minimalista integrado e pronto para ser usado, basta utilizar os seguintes 'escapes':

* Coloque qualquer código Perl dentro de tags <% %> e ele será processado na hora (sem exibir nada):
   <% $var="foo" % my>

* Colocando um '=' na frente da tag, o valor retornado pelo código Perl aparecerá no lugar da tag:
   Olá, <%= $var %> !!

* Para fazer comentários, basta começar a tag com '#':
   <%# % comentário eh isso um>

Ah, outra coisa. Se a linha tiver apenas Perl, basta que o primeiro caractere (da linha) seja '%'. As mesmas regras de retornar valor (=) e comentários (#) se aplicam, mas você não precisa das tags:

   % foreach my $num ( 1..10 ) {
        

essa eh uma linha normal dentro do loop.
essa também, e agora temos o numero ao quadrado:
%= $num ** 2 %# (essa linha eh um comentário e nao serah exibida)

% } # <-- fim do foreach

E você não precisa nem sair do código para fazer seus templates. Com o Mojolicious, você pode escrevê-los direto no código! Vejamos como fica nosso exemplo original:

   #!/usr/bin/perl
   use Mojolicious::Lite;

   get '/' => 'index';

   shagadelic;
   __DATA__
   @@ index.html.ep
   
     

Feliz Equinócio!!

Usamos o campo __DATA__ do Perl para embutir nosso template. Repare que a única diferença é a linha que diz @@ index.html.ep, indicando o início do template. De fato, podemos incluir quantos templates quisermos, basta demarcá-los com @@!

Embora fazer seus templates dentro do programa em si seja ótimo para desenvolvimento rápido, é fácil notar que não escala bem. Uma aplicação complexa pode conter dezenas (ou mesmo centenas) de templates, e em pouco tempo navegar por todos eles em apenas um arquivo torna-se impraticável. Além disso, muitas vezes os responsáveis pelo html/css/javascript são diferentes dos que mexem na aplicação em si, então separar seus templates em arquivos é particularmente importante. Mas pra que se preocupar com isso antes do tempo? Com um simples comando você pode mandar o Mojolicious extrair todos os templates para uma estrutura de diretórios e arquivos a qualquer momento! Assim, você pode desenvolver seus templates no próprio arquivo da aplicação e, quando achar que é hora, basta digitar:

   myapp.pl inflate

Pronto! Todos os templates foram duplicados em arquivos independentes, tudo no lugar certo. Os originais continuam lá no campo __DATA__, mas o Mojolicious dará sempre preferência ao que estiver do lado de fora.

Peraí, e o Controller?!

Já vimos como é fácil criar rotas e apontá-las para nossos templates, mas a graça toda é obter dados no controller e repassá-los à view (os templates). Com Mojolicious::Lite isso também é muito fácil - basta definir o método entre a rota e o destino! Em nosso exemplo, em vez de:

   get '/' => 'index';

poderíamos ter algo como:

   get '/' => sub {
       my $self = shift;
       $self->stash->{linguagem} = 'Perl 5';
   } => 'index';

Caso esteja começando no mundo Perl, a primeira linha, my $self = shift, obtém o objeto principal de nossa aplicação e coloca na variável $self. Através desse objeto, podemos manipular a aplicação como quisermos! No exemplo acima, utilizamos $self->stash, um método do Mojolicious para armazenar valores de forma não persistente, definindo que 'linguagem' aponta para a string 'Perl 5'. Você pode colocar qualquer coisa no stash, e depois acessar os elementos diretamente do template index.html.ep:

   __DATA__
   @@ index.html.ep
   
     

Feliz Equinócio de <%= $linguagem %>!!

Gostou? O melhor vem agora ;-)

Extendendo nossa Aplicação: Rotas!

Até agora vimos rotas com nomes estáticos:

   get '/'    => 'index';
   get '/foo' => 'bar';

Mas aplicações Web modernas recebem seus parâmetros diretamente da url. Com Mojolicious, você pode capturar parâmetros e manipular rotas como quiser!

Parâmetros são capturados da seguinte forma:

* :var - captura apenas o que está naquela posição até achar '.' ou '/'
* (.var) - captura apenas o que está naquela posição até achar '/'
* (*var) - captura tudo, daquela posição em diante

Você pode ter em sua rota tantas capturas quanto quiser. Os valores capturados estarão disponíveis tanto em $self->param('var'), para serem usados em seu controller, quanto em $self->stash('var'), para acesso direto em seu template. Experimente o seguinte programa:

    #!/usr/bin/env perl
    use Mojolicious::Lite;

    # captura /item/123, /item/abc, etc
    get '/item/:id' => 'item';

    # captura /item/123/editar, /item/abc/editar, etc
    get '/item/:id/editar' => 'editar';

    # captura 2 argumentos, 'nome' e 'quantidade'. Você
    # pode fazer isso quantas vezes quiser!
    get '/item/:nome/:quantidade' => 'vender';

    # Podemos até criar rotas com capturas no meio de um parâmetro
    # estático. Essa abaixo casa com /iniciomeiofim, /iniciosemfim, etc.
    get '/inicio(:meio)fim' => 'teste';

    # qualquer url que termine com '/fim'
    get '(*url)/fim' => 'fim';

    shagadelic;

    __DATA__
    @@ item.html.ep
    detalhes do item com id <%= $id %>

    @@ editar.html.ep
    vamos editar o item <%= $id %> ?

    @@ vender.html.ep
    temos <%= $quantidade %> do item <%= $nome %>, quer?

    @@ teste.html.ep
    o elemento do meio foi '<%= $meio %>'

    @@ fim.html.ep
    antes de 'fim', tivemos <%= $url %>




Rode e veja como rotas são divertidas! Experimente criar suas próprias rotas e testá-las em seu navegador. Uma coisa que vai reparar rapidamente é que o Mojolicious escolhe sempre a primeira rota que casar com a URL digitada pelo usuário. Em nosso exemplo, digitar no navegador o caminho /item/foo/fim vai apontar para vender, não para fim. Dessa forma, podemos organizar nossas rotas na estrutura que acharmos melhor.

Existem outras formas bacanas de personalizar rotas. Suponha a seguinte rota, por exemplo:

    get '/item/:id' => sub {
        # ... conteúdo qualquer
    } => 'item';

Podemos garantir que o parâmetro capturado case apenas com valores numéricos, adicionando a seguinte expressão regular:

    get '/item/:id' => [ id => qr/\d+/ ] => sub {
        # ...
    } => 'item';

Podemos ainda tornar o parâmetro completamente opcional, adicionando um valor padrão:

    get '/item/:id' => { id => 42 } => sub {
        # ...
    } => 'item';




Parte II - Métodos Auxiliares

Além do stash, o objeto principal nos dá várias formas de manipular nossa aplicação Web. Descrevemos as principais a seguir (clique no nome para abrir a descrição, clique de novo para fechá-la):

$self->redirect_to('nome')

Redireciona para a rota 'nome', que pode ser o nome da rota, o caminho para ela, ou mesmo uma url externa:

    get '/teste' => sub {
        my $self = shift;
        # ... nosso código ...

        $self->redirect_to('index');
    } => 'shazam';

Repare que demos o nome 'shazam' à nossa rota, mas aqui é apenas uma forma de referenciá-la. Afinal, nossa última linha acima faz com que ela seja sempre redirecionada para a rota 'index', de modo que nunca teremos necessidade de um template próprio.

$self->cookie

Cria ou obtém cookies rapidamente:

    get '/um' => sub {
        my $self = shift;
        $self->cookie( nome => 'Mojolicious' );
    } => 'um';

    get '/dois' => sub {
        my $self = shift;
        my $nome = $self->cookie('nome') || '';
    } => 'dois';

No exemplo acima, ao visitar /dois no navegador, $nome será preenchido com 'Mojolicious' apenas se você visitar /um antes. Senão, ficará com ''.

Cookies possuem alguns atributos (não se preocupe com isso se você não sabe o que significam) que podem ser personalizados numa referência de hash passada como segundo argumento:

   $self->cookie( calendario => 'Maia',
                  { path    => '/',
                    domain  => '.example.com',
                    secure  => 1,
                    expires => 'Fri, 21 Dec 2012 00:00:00 GMT',
                    comment => 'adeus, mundo cruel!',
                  }
                );

Quer fazer um cookie expirar em alguns segundos? Fácil:

   $self->cookie( foo => 'bar', { max_age => 60 } );

Para segurança extra, você pode criar cookies assinados simplesmente trocando ->cookie() por ->signed_cookie(). Os dados continuam armazenados no cliente como um cookie normal, mas a aplicação descarta automaticamente todos os cookies que falharem a verificação de assinatura! Para criar uma assinatura, basta colocar a seguinte chamada no início de sua aplicação:

    app->secret('minha senha secreta');

A "senha secreta" padrão é 'mojolicious', então se for usar cookies assinados não esqueça de criar uma senha diferente.

$self->session

Criar e manipular sessões é tão fácil quanto cookies. :

   # preencha dados de sessoes como quiser
   $self->session( nome => 'Mojolicious' );

   # ... e depois leia cada elemento facilmente
   my $nome = $self->session('nome');

$self->flash

A flash é uma forma prática de criar valores que ficarão disponíveis por exatamente uma requisição. É muito útil para passar valores em um redirecionamento:

    get '/' => sub {
        my $self = shift;
        $self->flash->{mensagem} = 'mooooooooo';
        $self->redirect_to('moo');
    } => 'index';

    get '/moo' => sub {
        my $self = shift;
        my $mensagem = $self->flash('mensagem');
        if ( $mensagem ) {
            # usuário veio de 'index'!
        }
    } => 'moo';

Aqui, se você abrir o navegador e for para '/', será redirecionado para '/moo' e $mensagem será definida. Assim que for consumida, a variável na flash é removida. Assim, se você recarregar a página em '/moo', a variável $mensagem não terá nada.

$self->param('nome')

Essa é uma forma rápida de acessar parâmetros passados para sua rota, ou elementos de um formulário. Vejamos um exemplo simples:

    any '/linguagem' => sub {
        my $self = shift;

        if ( $self->param('escolha') eq 'Perl' ) {
            $self->stash->{mensagem} = 'Legal!!';
        }
        else {
            $self->stash->{mensagem} = 'Experimente Perl!';
        }
    } => 'linguagem';

e, no campo __DATA__:

    @@ linguagem.html.ep
    
      
Qual a sua linguagem favorita?

Note que dessa vez usamos any em vez de get para nossa rota. Isso porque estamos usando um form que submete via POST, então queremos que essa rota aceite tanto "gets" (página carregada normalmente) quanto "posts" (página carregada através da submissão do form). Se quiséssemos uma rota aceitando apenas "posts", usaríamos... 'post' :)

Ah! O $self->param() também captura parâmetros de query, ou seja, poderíamos pedir a URL /linguagem?escolha=Perl e tudo funciona de forma transparente.

$self->render

Podemos usar esse método para renderizar um template qualquer, independente do nome da sua rota atual.

   get '/' => sub {
       my $self = shift;
       $self->render('outra');
   } => 'index';

$self->res

Esse método nos oferece Acesso rápido ao objeto de resposta HTTP 1.1. Com ele você pode definir explicitamente o código da sua resposta:

   $self->res->code(200); # ok
   $self->res->code(404); # not found

o tipo de conteúdo:

   $self->res->headers->content_type('image/png');

ou mesmo o conteúdo em si:

   $self->res->body('Alo, mundo!');

se você não sabe o que essas coisas significam, você não precisa delas. Deixe que o Mojolicious faça todo o trabalho pra você!

Robustez e Escalabilidade

Sua aplicação Web precisa de recursos complexos? Não se preocupe! Mojolicious vem com uma série de recursos avançados direto da caixa :)

Logging

Mojolicious possui por padrão um sistema de logs dividido em 5 níveis: debug, info, warn, error e fatal.

    # nivel de log padrão eh 'debug'
    app->log->level('warn');

    # logs vao pro STDERR por padrao
    app->log->path('myapp.log');

    get '/' => sub {
        my $self = shift;
        $self->app->log->warn('entramos em index!');
    } => 'index';

Note que, dentro da sub, podemos tanto chamar app diretamente quanto via $self->app.

Internacionalização (I18n)

Para exibir conteúdo em mais de um idioma, basta usar o plugin 'i18n', nativo do Mojolicious. No início de sua aplicação, invoque o plugin indicando o nome raiz ('namespace') de seus pacotes de idiomas:

    plugin 'i18n' => { namespace => 'MyApp::I18n' };

depois crie um pacote de idioma para cada língua alternativa. Para o inglês ('en'), poderíamos ter algo assim:

    package MyApp::I18n::en
    use base 'MyApp::I18n';

    our %Lexicon = (
            'minha página bilíngue' => 'my bilingual website',
            'alo, mundo' => 'hello, world',
    );

    1;

finalmente, use a função 'l' (um 'L' minúsculo) em seus templates para que a mensagem seja traduzida se o navegador do usuário estiver definido para dar preferência ao outro idioma:

    
<%=l % 'alo, mundo'>

unicode

Quando trabalhamos com idiomas acentuados ou com caracteres não-ASCII, podemos carregar o módulo auxiliar Mojo::ByteStream:

   use Mojo::ByteStream 'b';

Com isso, nosso aplicativo ganha a função b(), que pode ser usada para codificar/decodificar diferentes charsets:

   my $mensagem = $self->param('mensagem');

   # queremos guardar 'mensagem' em utf-8
   $mensagem = b($mensagem)->encode('UTF-8')->to_string;

Compartilhamento de Código

Rotas podem compartilhar código facilmente através da função ladder. Todas as rotas definidas depois de uma ladder passarão por ela primeiro, e só serão testadas se a ladder retornar um valor verdadeiro:

    # essa rota nao passa pela ladder
    get '/' => 'index';

    # nossa ladder só deixa passar se o parâmetro 'linguagem'
    # contém o valor 'Perl' :)
    ladder sub {
        my $self = shift;

        my $linguagem = $self->param('linguagem') || '';

        if ($linguagem eq 'Perl') {

            # podemos colocar valores no stash para
            # serem acessados pelas outras rotas!
            $self->stash->{mensagem} = 'boa escolha!';

            return 1;
        }
        return;
    };

    # essa rota veio depois, então só será aceita
    # se nossa ladder 'autorizar'
    get '/teste' => 'teste';

Ladders são ideais para compartilhar código entre rotas, e você pode definir tantas quanto quiser - basta lembrar que apenas rotas definidas depois da ladder serão influenciadas por ela.

Web Services? É pra já

Com o uso de rotas e do renderizador JSON nativo do Mojolicious, criar Web Services é imediato:

    get '/dados' => sub {
        my $self = shift;
        $self->stash->{dados} = [ 'foo', 'bar' ];

        $self->render_json($self->stash('dados'))
            if $self->stash('format') eq 'json';
    } => 'dados';

O exemplo acima cria a rota /dados e coloca em nosso stash a estrutura de dados que queremos exibir. Agora vem a parte legal: o Mojolicious é capaz de detectar automaticamente pedidos por formatos específicos através de extensões. Se, por exemplo, o usuário acessar a URL /dados, verá o conteúdo do template dados.html.ep. Mas se acessar a URL /dados.json, o valor de $self->stash('format') será 'json', e nosso comando $self->render_json entrará em ação, exibindo os dados em formato JSON sem chamar o template.

Falando em detecção automática de extensão, podemos fazer melhor ainda. Crie uma rota normal, colocando seus dados no stash e renderizando via template:

    get '/dados' => sub {
        my $self = shift;
        $self->stash->{dados} = [ 'foo', 'bar' ];
    } => 'dados';

    shagadelic;

    __DATA__
    @@ dados.html.ep
    
    % foreach my $item (@{$dados}) {
      
exibindo item '<%= $item %>'
% }

Reparou que ao longo deste artigo você sempre definiu a extensão .html para seus templates? Isso porque html é o formato padrão, mas podemos tirar vantagem da detecção automática de formatos do Mojolicious e criar versões diferentes de exibição da mesma rota:

    @@ dados.json.ep
    % include json => $dados

Em outras palavras, é possível adicionar suporte a Web Services em suas aplicações Mojolicious com apenas duas linhas! E tudo isso sem qualquer modificação na lógica do seu programa. Quer retirar o Web Service daquela rota? Basta apagar o template dados.json.ep. Quer renderizar outra variável, ou embutir outras informações, é só editá-lo!

Outra forma bacana é obter os dados através de uma ladder (lembra dela? Logo acima!) e criar uma rota para o conteúdo normal e outra apenas para o WebService:

    # essa 'ladder' obtem os dados comuns
    # para as rotas abaixo
    ladder sub {
        my $self = shift;
        $self->stash->{dados} = [ 'foo', 'bar' ];
        return 1;
    };

    # rota do nosso webservice
    # repare que não precisamos nem dar um nome pra ela, já que
    # não temos template pra ela, nem precisamos referenciá-la
    get '/service/dados' => sub {
        my $self = shift;
        $self->render_json( $self->stash('dados') );
    };

    # rota normal
    get '/dados' => 'dados';

Aqui, se o usuário acessar a rota /dados, receberá o template dados.html.ep. Mas se acessar /service/dados, receberá a estrutura em formato JSON :-)

Testes

Não podemos garantir que a aplicação faz o que queremos sem testes. Felizmente, Mojolicious traz o módulo Test::Mojo, que permite a criação rápida e eficiente de testes. Basta criar um diretório t e colocar seus testes, como numa aplicação Perl tradicional:

    use Test::More;
    use Test::Mojo;
    require '../myapp.pl';

    my $t = Test::Mojo->new;
    $t->get_ok('/');
    $t->status_is(200);

    $t->post_form_ok( '/busca', { nome => 'Mojolicious' } );
    $t->content_like( qr/The Web in a Box!/ );

    # podemos também encadear testes!
    $t->get_ok('/livros/lista.json')->status_is(200)->content_type_is('text/json');

    done_testing;

Mojolicious x Catalyst

Catalyst é sem dúvida um framework muito mais maduro e estável, usado em sites de grande porte e atendendo a literalmente milhões de requisições por dia. Possui excelente documentação e uma comunidade ativa e engajada.

Mojolicious é um framework novo e excitante, com uma curva de aprendizado mínima e nenhum pré-requisito além de módulos que vêm no próprio Perl, o que torna sua instalação muito simples e auto-contida. Ainda não é considerado estável, mas mesmo assim já é usado em produção em diversos sites.

Se você é um iniciante em Perl e quer desenvolver aplicações Web já, sem se preocupar com complexidades adicionais e sintaxes específicas, Mojolicious foi feito para você. Se, por outro lado, você tem uma aplicação complexa planejada, está acostumado com Perl 5, preza por estabilidade e robustez, sabe e quer usufruir do poder do CPAN, não pense duas vezes - Catalyst é o seu framework.

Mas não tome o parágrafo anterior como verdade absoluta. No final, a escolha perfeita resume-se ao seu gosto pessoal. Há sempre mais de uma forma de se fazer as coisas em Perl, e a melhor solução é aquela que mais se adequa ao seu modo de pensar e estilo de programar. O importante é que, independente da escolha, desenvolvimento Web seja divertido e recompensador!

Veja Também

* site oficial do mojolicious
* Informações sobre o Framework: Mojolicious, Mojolicious::Lite
* Documentação (em andamento): The Mojolicious Guide to The Galaxy
* Documentação dos Templates: Mojo::Template, Funções auxiliares
* Documentações internas úteis: Controller, Request, Response

AUTOR

Breno G. de Oliveira

blog comments powered by Disqus