Breno G. de OliveiraPublicado em 21/12/2012
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!
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.
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!
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!
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':
<% $var="foo" % my>
%>
Olá, <%= $var %> !!
%=>
<%# % 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.
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 ;-)
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 dianteVocê 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';
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):
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.
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.
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');
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.
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
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.
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';
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ê!
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 :)
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
.
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'> %=l>
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;
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.
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 :-)
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;
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!
Breno G. de Oliveira