<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>
Construindo um Website Dinâmico
com Class::DBI, CGI::Application e Template Toolkit
</title>
<style type="text/css">
body { padding-left: 10%; padding-right: 8%; }
p { text-align:justify; font-family: sans-serif; }
div.title p:first-child{ font-weight: bold; font-size: 200%; text-align: center; }
div.title p{ font-weight: bold;
                 text-align: center;
                 font-size: 85%;
                 margin-left: 15%;
                 margin-right: 15%;
                 }
div.preface p:first-child { margin-top: 0px;
font-style: normal;
                                 font-weight: bold;
                                 font-size: 120%;
                                 text-align: left;
                                 text-decoration: underline;
                                }
div.preface p { text-align: justify;
                 font-style: italic;
                 margin-left: 10%;
                 margin-right: 10%;
                 font-size: 80%;
                 }
div.paragraph p { text-align: justify; }
div.paragraph p:first-child { margin-top: 0px;
                                 font-weight: bold;
                                 font-size: 120%;
                                 text-decoration: underline;
                                 }
pre.code { font-family: monospace;
font-size: 80%;
                 border: thin solid lightgray;
                 padding-top: 10px;
                 padding-left: 15px;
                 padding-bottom: 10px;
                 background-color: LightGray;
                 margin-left: 10%;
                 margin-right: 15%;
         }
pre.code span.reserved-word { font-weight:bold; }
div.display { text-align: center; }
</style>
</head>
<body>
<div class="title">
<p>Construindo um Website Dinâmico com Class::DBI, CGI::Application e Template Toolkit </p>
<p>Por Luis Campos de Carvalho, Líder do São Paulo Perl Mongers<br>
mailto: monsieur_champs [at] yahoo [dot] com [dot] br</p>
</div>
<div class="preface">
<p>Prefácio</p>
<p>Quando pensamos em aplicativos World Wide Web em Perl, normalmente nos vêem à cabeça aqueles programas gigantescos, desorganizados, cheios de problemas e praticamente impossíveis de se manter. Neste modesto e mal-escrito artigo, eu pretendo mostrar que é possível construir aplicativos dinâmicos, baseados nas tecnologias da World Wide Web, simples, rápidos, fáceis de manter e extensíveis, usando ferramentas de código fonte aberto, noções sobre o paradigma da Orientação a Objetos, e algumas técnicas de engenharia de software básica.</p>
</div>
<div class="paragraph">
<p>Molhando os Pés: primeiro contato com as tecnologias utilizadas neste artigo</p>
<p>Para começar, vamos falar da aplicação que desejamos fazer: é simples, e chega a ser até meio besta, para facilitar a compreensão. Desta forma, ao invés de se prender aos detalhes e complexidades da aplicação que vamos construir, o leitor terá sua atenção presa aos detalhes e complexidades envolvidas com <em>como</em> construir a aplicação.</p>
<p>Vamos construir um Módulo de Autenticação de Usuários.</p>
<p>A responsabilidade principal de um Módulo de Autenticação de Usuários é se certificar de que um usuário de nosso website é realmente quem ele diz ser, e permitir a ele acesso aos demais recursos dinâmicos disponíveis em nosso website fictício.</p>
<p>Para construir esta aplicação, vamos utilizar um <i>framework</i> de desenvolvimento de aplicações dinâmicas para a World Wide Web chamado <a href="http://search.cpan.org/~markstos/CGI-Application-4.04_02/">CGI::Application</a>, combinado com um gerenciador de banco de dados relacional (eu escolhi usar o <a href="http://www.postgresql.org">PostgreSQL</a>, mas outros, como o <a href="http://www.mysql.com">MySQL</a> e o <a href="http://www.oracle.com">Oracle</a> se encaixam aqui perfeitamente e não precisam de adaptações) através do uso de um conjunto de objetos Perl chamado <a href="http://search.cpan.org/~tmtm/Class-DBI-0.96/">Class::DBI</a>. Completam o nosso conjunto de bibliotecas-base um <i>framework</i> de visualização muito poderoso chamado <a href="http://www.template-toolkit.org">Template Toolkit</a> e um pouco de teoria sobre padrões de projeto e modelagem orientada a objetos.</p>
</div>
<div class="paragraph">
<p>O Projeto da Aplicação</p>
<p>Antes de continuarmos, vamos especificar e modelar rapidamente nossa aplicação. Isso, além de nos dar maior consciência sobre o que estamos construindo, ajuda as pessoas a compreender nosso trabalho (e a valorizá-lo).</p>
<p>Nosso Módulo de Autenticação de Usuários terá apenas um ponto de entrada: ele gerará uma página para coletar o nome e senha do usuário, tomará uma decisão baseado nos dados que o usuário lhe enviar e executará uma das duas únicas ações planejadas para ele: ou criará uma sessão para o usuário e o redirecionará para a parte restrita de nosso website dinâmico, ou enviará uma mensagem de erro, informando ao usuário que o par (nome, senha) apresentado não é válido. Em <em>Portugol</em>, para ficar simples:</p>
<pre class="code"> <span class="comment"># Processo 1: coleta de credenciais</span>
<span class="reserved-word">INÍCIO;</span>
constrói página de coleta de dados;
envia página de coleta de dados;
<span class="reserved-word">FIM.</span>
<span class="comment"># Processo 2: autenticação de usuários.</span>
<span class="reserved-word">INÍCIO;</span>
<usuário preenche informações de autenticação>;
recebe dados de autenticação de usuário;
verifica dados;
<span class="reserved-word">SE</span> dados válidos, <span class="reserved-word">ENTÃO</span>
<span class="reserved-word">INÍCIO</span>
gera informação de sessão;
registra informação de sessão;
envia <i>cookie</i> identificador ao usuário;
redireciona usuário autenticado para a aplicação;
<span class="reserved-word">FIM</span>
<span class="reserved-word">SENÃO</span>
<span class="reserved-word">INÍCIO</span>
gera página de informação de erro;
envia página ao usuário;
<span class="reserved-word">FIM;</span>
<span class="reserved-word">FIMSE;</span>
<span class="reserved-word">FIM;</span></pre>
<p>Agora que sabemos como deve ser nosso processo, vamos modelar rapidamente os dados necessários para autenticar um usuário. Precisamos apenas reter permanentemente algumas informações sobre os usuários, como um nome, e uma senha. Para isso, vamos criar uma tabela em nosso banco de dados relacional, utilizando este código SQL:</p>
<p>Isto vai criar uma tabela em nosso banco de dados, onde poderemos registrar nossos usuários.</p>
<pre class="code">-- Este script assume que você tem um banco de dados PostgreSQL, pode
-- conectar-se a ele e tem permissões mínimas para realizar criação de
-- tabelas e inclusão de registros nas tabelas criadas.
-- Se você tiver qualquer problema com isso, converse com seu DBA ou
-- Administrador de Sistemas local.
-- Copyleft 2005 Luis Campos de Carvalho
-- Copyleft 2005 Engenharia de Software Ltda
-- Doado aos São Paulo Perl Mongers
-- This script is free software; you can redistribute it and/or modify
-- it under the term of the GNU Public License.
CREATE SEQUENCE seq_usuario;
CREATE TABLE usuario (
uid INTEGER PRIMARY KEY DEFAULT NEXTVAL( 'seq_usuario'::text ),
login VARCHAR UNIQUE NOT NULL,
senha VARCHAR NOT NULL
);
-- EOF </pre>
<p>A aplicação do <a href="http://www.w3schools.com/sql/default.asp">SQL</a> no banco de dados varia muito. Por isso, vou deixar a tarefa de descobrir como se conectar ao banco de dados e criar tabelas para um futuro artigo sobre <a href="http://search.cpan.org/~timb/DBI-1.48/">DBI</a> e <a href="http://search.cpan.org/~tmtm/Class-DBI-0.96/">Class::DBI</a>.</p>
<p>Vamos passar agora à parte de modelagem de objetos e módulos necessários para a aplicação. Para isso, antes, precisamos conversar um pouco sobre <a href="http://en.wikipedia.org/wiki/MVC">MVC</a>, <a href="http://search.cpan.org/~tmtm/Class-DBI-0.96/">Class::DBI</a>, <a href="http://www.template-toolkit.org">Template Toolkit</a> e <a href="http://search.cpan.org/~markstos/CGI-Application-4.03/">CGI::Application</a>.</p>
</div>
<div class="paragraph">
<p>MVC, Class::DBI e CGI::Application</p>
<p>Agora vamos entrar na parte complicada deste tutorial. O leitor amigo possivelmente deve ter ouvido alguma coisa a respeito de <a href="">MVC</a>, ou, para os iniciados, <em>Model-View-Controller</em>. Este é um padrão de projeto (<em>Design Pattern</em>) que separa toda e qualquer aplicação em três componentes básicos: o Modelo(<em>Model</em>), a Visão (<em>View</em>) e o Controlador (<em>Controller</em>). Leiam um <a href="http://www.andywardley.com/computers/web/mvc.html">contraponto</a>, por <a href="http://www.wardley.org/">Andy Wardley</a>.</p>
<p>Em poucas palavras, já que eu não pretendo nem de longe esgotar este assunto: o <b>Modelo</b> é responsável por manter nossos dados e manipulá-los de acordo com as regras de negócio (que implementamos neles). São os modelos (geralmente temos muitos) que sabem como e onde armazenar e recuperar os dados que captamos e utilizamos.</p>
<p>Enquanto os modelos são responsáveis pelos dados e pelas regras de negócio (a forma de manipular os dados), as <b>Visões</b> tem como responsabilidade a exibição dos dados encontrados nos modelos. Uma visão normalmente conhece um modelo e sabe como exibir os dados daquele modelo por alguma mídia específica. Por exemplo: suponha que temos uma lista de usuários (um Modelo representando usuários) e desejamos exibir estes usuários pela internet (com <a href="http://www.w3.org/MarkUp/">HTML</a>) e em um telefone celular (com <a href="http://www.umtsworld.com/technology/wap.htm">WAP</a>). Para tornar isso possível, não precisamos mexer em nada relacionado com nossa lista de usuários: precisamos apenas implementar Visões capazes de exibir em HTML ou WAP. Assim, quando desejamos exibir a lista de usuários em HTML, utilizamos a visão capaz de exibir a lista em HTML e quando desejamos exibir nossos usuários em WAP, utilizamos a visão que implementa exibição WAP.</p>
<p>Finalmente, o <b>Controlador</b> tem um papel não muito intuitivo, mas muito importante, depois que se aprende a utilizá-lo corretamente: é dele a responsabilidade de decidir, para cada requisição enviada para nossa aplicação, qual modelo deve ser acionado para tratar dela. É também do Controlador a responsabilidade de verificar as credenciais de um usuário e determinar suas permissões de acesso. Normalmente, uma aplicação web tem apenas um controlador, não importa o quão grande ela seja.</p>
<p>A esta altura, você deve estar se perguntando qual a relação disso tudo com as bibliotecas Perl <a href="http://search.cpan.org/~tmtm/Class-DBI-0.96/">Class::DBI</a>, <a href="http://www.template-toolkit.org">Template Toolkit</a> e <a href="http://search.cpan.org/~markstos/CGI-Application-4.04_02/">CGI::Application</a>. É hora de explicar: cada uma desta bibliotecas implementa um dos elementos do MVC que vamos utilizar em nossa aplicação: <a href="http://search.cpan.org/~tmtm/Class-DBI-0.96/">Class::DBI</a> implementa modelos, <a href="http://www.template-toolkit.org">Template Toolkit</a> implementa visões e CGI::Aplication implementa nosso controlador. Vamos construir agora cada uma das diferentes partes do padrão de projeto que utilizaremos:</p>
</div>
<div class="paragraph">
<p>Implementação da Camada de Acesso a Dados com Class::DBI</p>
<p>Para implementar o modelo, vamos precisar de ajuda de mais um padrão de projetos, o <i>Data Access Object</i>, ou <a href="http://en.wikipedia.org/wiki/Data_Access_Object">DAO</a>, para os íntimos.</p>
<p>Este padrão de projeto consiste em manter objetos separados para cuidar das responsabilidades específicas de manipular e armazenar os dados. Fazemos isso para que, quando precisarmos alterar a forma ou o local de armanzenamento das informações, não precisemos alterar nossos processo de manipulação de dados, apenas o processo de armanzenamento e recuperação. Isto torna menor a possibilidade de introdução de novos erros em um sistema antigo por mudanças menos importantes para o negócio.</p>
<p>Para implementar o DAO, vamos utilizar uma biblioteca pronta. Isto é muito melhor que ser obrigado a "reinventar a roda" e construir nós mesmos.</p>
<p>Optamos pela biblioteca <a href="http://search.cpan.org/~tmtm/Class-DBI-0.96/">Class::DBI</a>. Poderíamos ter optado por outras semelhantes, como a <a href="http://search.cpan.org/~cwinters/SPOPS-0.87/">SPOPS</a>, que tem funcionalidade semelhante, mas oferece muitos recursos extras.</p>
<p><a href="http://search.cpan.org/~tmtm/Class-DBI-0.96/">Class::DBI</a> deve ser implementada através de extensão do objeto Class::DBI, e declaração de alguns métodos estáticos para informar à biblioteca sobre nosso banco de dados, nossas tabelas e nosso método preferido de carga de dados. Adicionalmente, podemos ir além e implementar métodos para desempenhar tarefas mais específicas, como algum tipo de <i>query</i> especial para nossa aplicação, ou outras funcionalidades semelhantes. Mas isso també está fora do nosso escopo de hoje. Vamos ver como deve ser nossa classe-base, devidamente comentada:</p>
<pre class="code">=head1 NAME
DAO::Base - Classe base para objetos de acesso a dados
=cut
package DAO::Base;
use strict;
use warnings;
use base qw( Class::DBI );
our $VERSION = sprintf '%d.%02d', q$Revision:$ =~ m/(\d+)\.(\d+)/;
=head1 SYNOPSIS
use base qw( DAO::Base );
=head1 DESCRIPTION
Esta é a classe base dos Objetos de Acesso a Dados, derivados de
Class::DBI.
=cut
__PACKAGE__->connection( 'dbi:Pg:dbname',
                         'dbuser', 'secret',
                         { AutoCommit => 1,
                         RaiseError => 1,
                         PrinError => 0,
                         }
                 );
=head1 AUTHOR
Luis Campos de Carvalho
Engenharia de Software Ltda.
=head1 REVISION
$Revision: $
=head1 COPYRIGHT
Copyleft 2005 Engenharia de Software Ltda.
Doado aos São Paulo Perl Mongers.
This module is free software; you can redistribute it and/or modify it
under the term of the GNU Public License.
=head1 SEE ALSO
L<Class::DBI>
</pre>
<p>Este módulo é composto de algumas declarações interessantes: primeiramente, o nome do pacote <code>package DAO::Base</code> foi escolhido com finalidade didática. É simples modificar isto quando for utilizado em um projeto sério (sim, estou acreditando que as pessoas vão se aproveitar deste código em projeots sérios!).</p>
<p>A próxima coisa interessante a se notar é relacionada com herança: eu declarei que este pacote tem como base (e herda funcionalidades de) Class::DBI, com a asserção <code>use base qw( Class::DBI );</code> isto é equivalente a dizer <code>BEGIN{ require Class::DBI; push @ISA, 'Class::DBI'; }</code>, mas é bem mais curto e fácil de entender.</p>
<p>Também é neste módulo que devemos declarar os dados de conexão com nosso banco de dados, para que ela possa ser compartilhada transparentemente entre todos os módulos derivados deste. Declaramos nossa conexão utilizando o método estático <code>Class::DBI::connection()</code>, mas o chamamos com <code>__PACKAGE__->connection()</code>. Isto é boa prática de programação orientada a objetos em Perl, já que não podemos assumir qual o tipo que será passado para nós quando da instanciação desta classe. A diretriz <code>__PACKAGE__</code> cuida sozinha destes detalhes, fazendo a coisa certa por nós. Ela sempre vai resolver para o nome do pacote correto, não importa o que aconteça. Para saber mais sobre os parâmetros passados ao banco de dados, por favor consultem a documentação do <a href="http://search.cpan.org/~timb/DBI-1.48/">DBI</a>.</p>
<p>Depois de declarar nossa conexão com o banco de dados, estamos prontos para extender esta classe e especializá-la para cada uma das tabelas que teremos.</p>
<p>A única tabela que teremos neste sistema é a tabela de usuários. Mas lembrem-se: para extender este sistema para um sistema completo e útil, basta seguir a mesma linha de pensamento que estamos desenvolvendo aqui e continuar implementando as funcionalidades restantes.</p>
<p>Nosso objeto de acesso a dados de usuário será declarado desta forma:</p>
<pre class="code">=head1 NAME
DAO::Usuario - Objeto de Acesso a Dados da tabela 'usuario'.
=cut
package DAO::Usuario;
use warnings;
use strict;
use base qw( DAO::Usuario );
our $VERSION = sprintf '%d.%02d', q$Revision:$ =~ m/(\d+)\.(\d+)/;
=head1 SYNOPSIS
use DAO::Usuario; $user = DAO::Usuario->create( ... ); $user =
DAO::Usuario->retrieve( ... ); $user = DAO::Usuario->search( ... );
=head1 DESCRIPTION
Este é o módulo de acesso a dados da tabela de usuarios do sistema de
autenticacao de usuarios desenvolvido para o mini-curso da UNISAL.
=cut
__PACKAGE__->table( 'usuario' );
__PACKAGE__->columns( All => qw[ id login senha ] );
=head1 AUTHOR
Luis Campos de Carvalho Engenharia de Software Ltda.
=head1 REVISION
$Revision: $
=head1 COPYRIGHT
Copyleft 2005 Engenharia de Software Ltda.
Doado aos São Paulo Perl Mongers.
This module is free software; you can redistribute it and/or modify
it under the term of the GNU Public License.
=head1 SEE ALSO
L<Class::DBI>
</pre>
<p>Reparem que este módulo se parece muito com o outro. Declaramos um pacote no mesmo nível, <code>DAO::Usuario</code>, para modelar nossos usuários. Em seguida, declaramos que herdamos as fucnionalidades de <code>DAO::Base</code>, em especial duas muito importantes: 1) é de lá que vem nossas informações de banco de dados, que vamos precisar para fazer com que este módulo possa recuperar e armazenar alguma informação no banco de dados; e 2) como <code>DAO::Base</code> herda de <code>Class::DBI</code> (lembra?), também nosso módulo herda as funcionalidades implementadas lá. É este pequeno detalhe que faz com que tudo isso funcione perfeitamente.</p>
<p>As declarações que precisamos fazer aqui são simples: precisamos declarar qual tabela este módulo acessa, e quais os campos da tabela que estarão visíveis para nossa aplicação. Não, você não precisa declarar todos os campos como visíveis. Mas não há como o <code>Class::DBI</code> conhecer (ou utilizar) os campos que você não declarar. Use isto com sabedoria.</p>
<p>Para declarar tabelas, utilizamos a diretriz auto-explicativa <code>__PACKAGE__->table( 'usuario' )</code>. Como todos já perceberam, o único parâmetro passado (o nome da tabela) é obrigatório.</p>
<p><strong>Depois</strong> (lembrem-se: <strong>Depois</strong>!) de declarar qual tabela desejamos acessar, podemos então declarar quais colunas desta tabela desejamos que nosso módulo veja. Note que isso implica que podemos ter tabelas com quantos atributos desejar, e podemos controlar quais módulos acessam que colunas através desta declaração.</p>
<p>Para declarar as colunas visíveis, utilizamos <code>__PACKAGE__->columns( All => qw[ id login senha ] );</code> isto declara três colunas visíveis, e <strong>implicitamente</strong> declara que a primeira coluna da lista (<code>id</code>) é nossa chave primária.</p>
<p>Com isso, encerramos a parte de construção da camada de acesso a dados. O que temos até este momento são dois objetos simples: <code>DAO::Base</code> e <code>DAO::Usuario</code>. </p>
<p>Por agora, vamos apenas reservar estes módulos. Vamos precisar deles mais adiante.</p>
</div>
<div class="paragraph">
<p>Implementação das Visões com Template Toolkit</p>
<p>Agora que podemos contar com nossos objetos de acesso a dados, vamos implementar nossas visões. São elas que darão um rosto para nossa aplicação, e por isso mesmo devem ser tratadas com um certo cuidado. Como esta é uma aplicação reutilizável, devemos tomar alguns cuidados extras com a documentação dos <em>templates</em>, para que seja sempre possível a um desenvolvedor adaptá-lo às suas necessidades.</p>
<p>Nossa primeira visão precisa pedir ao usuário que entre com seu nome e senha em duas caixas de texto. Também precisa oferecer a ele um botão que vai nos levar de volta para a aplicação. Nossa segunda visão será utilizada apenas em caso de erros: caso o nome ou senha fornecidos pelo usuário não o identifiquem corretamente, precisamos informar isto a ele. Em caso de autenticação positiva, vamos utilizar um redirecionamento HTTP (código de resposta 302) para indicar ao <em>browser</em> do usuário para onde ir em seguida, e ao mesmo tempo transmitir a este programa as credenciais do usuário que ele conduz.</p>
<p>Com algum esforço de desenvolvimento, construimos a primeira visão, que vamos chamar de <code>ask-credentials.tmpl</code>. Ela se parece muito com isso:</p>
<div class="display">
<img align="center" valign="middle" src="images/login.gif" alt="quadro de login" border="0" />
</div>
<p>O código fonte HTML utilizado para gerar isto é assim:</p>
<pre class="code"><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title></title>
<meta name="AUTHOR" content="Luis 'Champs' de Carvalho">
<style type="text/css">
form { font-family: monospace;
border: thin solid gray;
         position: absolute;
         top: 45%; left: 60%;
         padding-left: 5%;
         padding-right: 5%;
         background-color: lightyellow;
         }
form p:first-child { font-weight: bold; }
form p.submit { text-align: right; }
</style>
</head>
<body>
<form action="[% action %]" method="POST" enctype="application/x-www-form-urlencoded">
<p>Entrada do Sistema</p>
<p>Login:
<input type="text" name="login" size="16" tabindex="1" align="middle">
</p>
<p>Senha:
<input type="password" name="passwd" size="16" tabindex="2" align="middle">
</p>
<p class="submit"><input type="submit" name="mode" value="Login" tabindex="3"></p>
</form>
</body>
</html>
</pre>
<p>Preste atenção a alguns pequenos detalhes: os nomes dos campos que estamos utilizando são importantes. Vamos conferir: o campo onde o usuário escreverá seu <em>login</em> chama-se <code>login</code>; o campo onde escreverá sua senha é especial (do tipo <code>password</code>, e chama-se <code>passwd</code>; e finalmente, o botão de envio chama-se <code>mode</code>. Isto é muito importante, pois é através deste campo que a aplicação determinará qual deve ser o próximo passo.</p>
<p>Note também que preenchemos o atributo <code>action</code> do <em>tag</em> <code>form</code> com uma espécie de <em>tag</em> especial. Isto é um comando para o Template Toolkit. Vamos voltar a estes detalhes em breve. Por enquanto, basta prestar atenção nisso.</p>
<p>Precisamos ainda de mais uma visão, para informar o usuário que ele não possui credenciais válidas. Ele deve ser assim:</p>
<pre class="code"><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<!-- $Id$ -->
<head>
<title>Erro na Autenticação!</title>
<meta name="AUTHOR" content="Luis 'Champs' de Carvalho">
<style type="text/css">
p.error { border: thin solid black;
background-color: red;
         font-weight: bold;
         text-align: center;
         position: absolute;
         left: 35%;
         top: 35%;
         }
</style>
</head>
<body>
<p class="error">[% error_message %]</p>
</body>
</html>
</pre>
<p>Note que voltamos a utilizar um daqueles comandos especiais para o Template Toolkit. Vamos falar sobre isso em alguns instantes. O importante agora é que isso liqüida com o assunto das visões, e nos permite começar a explicar como deve ser construío o núcleo da aplicação.</p>
</div>
<div class="paragraph">
<p>Introdução ao conceito de Stash</p>
<p><b>Stash</b> é simplesmente um conceito. É um repositório de dados semi-público, que utilizamos para acumular informações sobre a requisição que estamos processando. Eu digo semi-público por que não podemos acessar o stash de uma requisição exceto se estivermos processando esta requisição.</p>
<p>Vamos implementar nosso stash com um <i>design pattern</i> chamado <a href="">Singleton</a>, que no nosso caso servirá para garantir que todas as referências para o stash sejam processadas pelo mesmo stash, dentro de uma determinada requisição.</p>
<p>Nosso método stash() é implementado automaticamente pela biblioteca <a href="">Class::Accessor</a>, que cria automaticamente métodos de acesso a estruturas de dados auxiliares, com declarações simples como "<code>__PACKAGE__->mk_accessors( qw[ stash ] );</code>".</p>
</div>
<div class="paragraph">
<p>Introdução ao Class::Accessor</p>
<p>Acabamos de falar que alguns métodos são gerados automáticamente pela biblioteca Class::Accessor. Por favor não se assustem. Gerar código em Perl em tempo de execução é uma coisa normal e até esperada. Esta é uma das principais vantagens e desvantagens das linguagens de programação interpretadas em tempo de execução: todas elas permitem que você execute trechos de código que acabou de gerar.</p>
<p>Para tornar curta uma história bem longa, o importante aqui é o seguinte: ao invéz de codificarmos um método assim:</p>
<pre class="code">sub stash{
my $self = shift;
my $new_stash = shift;
return $new_stash? $self->{stash} = $new_stash : $self->{stash};
}</pre>
<p>Simplesmente solicitamos para a biblioteca Class::Accessor que gere um método para nós com esta "forma". Todos os métodos gerados fazem a mesma coisa (permitem acesso a um objeto ou estrutura de dados conhecido pelo objeto que estamos implementando), mas não precisam ser escritos. Isto economiza tempo e faz os programas mais simples de ler.</p>
</div>
<div class="paragraph">
<p>Introdução ao AppConfig</p>
<p>Esta biblioteca é uma das mais poderosas ferramentas de configuração de sistemas que eu conheço.</p>
<p>Entre as funcionalidades mais interessantes do <code>AppConfig</code>, vou destacar duas que vamos utilizar bastante. A primeira é a facilidade de verificação de consistência dos arquivos de configuração processados: basta declarar quais palavras-chave são aceitas e quantos argumentos elas podem ter para se obter este resultado. Por exemplo, para aceitar como diretriz de configuração qualquer palavra-chave com apenas um argumento associado, passamos ao construtor de nosso <code>AppConfig</code> os argumentos</p>
<pre class="code"> my $cfg = new AppConfig
( { CASE => 1,
        PEDANTIC => 1,
        CREATE => 1,
        GLOBAL => { EXPAND => 1,
                 ARGCOUNT => ARGCOUNT_ONE,
                 },
}
);</pre>
<p>que essencialmente dizem "diferencie maiúsculas de minúsculas" (<code>CASE=>1</code>); "tenha tolerância zero com discrepâncias" (<code>PEDANTIC=>1</code>); "aceite qualquer palavra como diretriz de configuração" (<code>CREATE=>1</code>); "aplique expansão de variáveis (<code>EXPAND=>1</code>) a todas as diretivas"; e "aceite apenas um argumento associado a cada diretiva" (<code>ARGCOUNT=>ARGCOUNT_ONE</code>).
</p>
<p>A segunda funcionalidade que utilizamos é a capacidade de transformar um conjunto de diretivas de configuração em uma estrutura de dados arbitrária, como um hash de hashes, assim:</p>
<pre class="code"> { error => { $cfg->varlist( '^error_message_', 1 ) },
template => { $cfg->varlist( '^template_', 1 ) },
};</pre>
<p>Em poucas palavras, o método <code>AppConfig::varlist()</code> retorna todas as diretivas de configuração que baterem com a expressão regular passada como primeiro argumento em forma de um hash, associando-as com os seus respectivos valores. Caso o segundo argumento seja um valor verdadeiro, este método ainda transforma as diretivas, removendo delas a expressão regular passada como primeiro argumento (de forma que voc&eˆ não precisa ficar repetindo a string "error_" sempre que deseja pegar uma mensagem de erro confirgurada pelo usuário).</p>
</div>
<div class="paragraph">
<p>Introdução ao Template Toolkit</p>
<p>Todo mundo que um dia pensou em programar Perl para a web com seriedade tentou escrever uma biblioteca capaz de gerar código HTML a partir de arquivos contendo a parte "fixa" do código e "substituindo" palavras-chave pré-determinadas por expressões calculadas dinâmicamente em tempo de execução. Um sujeito chamado <a href="http://www.wardley.org/">Andy Wardley</a> pensou nisso e escreveu <b>a</b> biblioteca para realizar este tipo de opera¸ão, o Template Toolkit. Trata-se de um interpretador para uma meta-linguagem de programação que pode ser utilizada até mesmo para gerar HTML para nossa aplicação de exemplo. Eu, particularmente, já a utilizei até para fazer manutenção em bancos de dados <a href="http://www.oracle.com.br/">Oracle</a>.</p>
<p>Para utilizar templates baseados em Template Toolkit em nosso sistema de exemplo, vai nos bastar umas poucas diretivas de configuração e uma instância do objeto Template, o interpretador da meta-linguagem dos templates:</p>
<pre class="code"> my $options =
{ INCLUDE_PATH => $ROOTDIR.'/'.$self->config->{template}{include_path},
PRE_CHOMP => $self->config->{template}{pre_chomp} || 0,
POST_CHOMP => $self->config->{template}{post_chomp} || 0,
TRIM => $self->config->{template}{trim} || 0,
COMPILE_EXT => $self->config->{template}{compile_ext} || 'c',
COMPILE_DIR => $self->config->{template}{compile_dir} || '/tmp/ttc',
};
# Retorna um template prontinho para usar.
return new Template( $options );</pre>
<p>Outra vez, para tornar mais curta uma história bem longa, as diretivas dizem: "localize templates dentro do diretório configurado pelo usuário" (<code>INCLUDE_PATH</code>); "remova automáticamente todos os espaços encontrados antes de um valor aplicado sobre o template" (<code>PRE_CHOMP</code>); "remova todos os espaços encontrados depois de um valor aplicado sobre o template" (<code>POST_CHOMP</code>); "remova linhas em branco antes e depois de um valor aplicado sobre o template" (<code>TRIM</code>); "armazene templates pré-compilados com a extensão definida em (<code>COMPILE_EXT</code>)"; e "armazene arquivos de templates pré-compilados no diretório (<code>COMPILE_DIR</code>)".</p>
<p>Depois de obter uma instância do objeto Template, basta utilizá-lo para processar os arquivos contendo código HTML mesclado com meta-código Template Toolkit para obter páginas HTML completas e prontas para enviar ao browser. Vamos falar sobre como fazer isto durante a análise do código fonte, mais adiante.</p>
</div>
<div class="paragraph">
<p>Introdução ao CGI::Application</p>
<p>Toda aplicação para a world-wide-web executa basicamente um ciclo de processamento idêntico para atender cada requisição enviada para ela:
<ol>
<li>Obtêm as informações enviadas pelo browser (cliente)</li>
<li>Determina qual o estado atual da aplicação para este cliente em específico</li>
<li>Decide qual a próxima ação a ser executada para esta situação</li>
<li>Aloca os recursos necessários para a execução da ação</li>
<li>Executa a ação planejada</li>
<li>Libera recursos utilizados localmente para o processamento da requisição.</li>
<li>Envia resultados</li>
</ol>
</p>
<p>Claro, não é preciso dizer que este processo, ao se misturar com o código da nossa aplicação, pode potencialmente criar uma confusão emaranhada, complicada de manter e cheia de problemas de implementação, que vai dar dor-de-cabeças homéricas nos mantenedores do código e obrigar os responsáveis a contratar sujeitos como eu para consertar o problema, a preços absurdos.</p>
<p>Para evitar que este seja o destino de seus aplicativos para a web, alguns programadores criaram suas próprias bibliotecas de implementação de processamento de requisições, que realizavam as tarefas listadas de uma forma abstrata, permitindo a eles extender o código para implementar a lógica de negócio de seus programas separadamente, de uma forma mais organizada e simples de entender. Foi assim que nasceu a biblioteca CGI::Application, a mais veloz e flexível de todas as bibliotecas de processamento de requisições disponíve para a linguagem Perl. Ela será a base de nosso código fonte, nos permitindo concentrar nas particularidades que nos interessam, sem precisar nos preocupar com coisas menores, como saber qual a próxima ação que devemos tomar para continuar processando uma requisição enviada por um usuário.</p>
</div>
<div class="paragraph">
<p>Implementação do Controlador com CGI::Application</p>
<p>Lembram-se de que falei sobre o padrão de projeto MVC, há pouco? Pois bem, vamos implementar o "C" do MVC, nosso controlador de processo. Ele será baseado na biblioteca CGI::Application, e será assim: </p>
<pre class="code">package Auth::Base;
use warnings;
use strict;
use base qw( CGI::Application Class::Accessor );
use Auth::RootDir qw( $ROOTDIR );
use AppConfig qw( :argcount );
use Template;
=head1 NAME
Auth::Base - Classe base da aplicação de autenticação de usuários.
=head1 SYNPOSIS
use base qw( Auth::Base );
=head1 DESCRIPTION
Classe base para o framework MVC utilizado para o desenvolvimento de
uma aplicação de autenticação de usuários didática.
=head1 ATTRIBUTES
=over 4
=item config
Campo para manter o nosso objeto de configuração. Para esta aplicação,
vamos utilizar um L<AppConfig>, by Andy Wardley, o meu favorito.
=item template
Campo para manter o nosso objeto de processamento de templates. Isto
ajuda muito a gerar HTML na saída. Felizmente, a maior parte dos
mortais pode simplesmente utilizar este framework e esquecer que isso
existe. Vamos usar um objeto L<Template>, do Template Toolkit, by Andy
Wardley. Este também é um dos meus módulos favoritos.
=item stash
Um Stash é nada mais, nada menos que um conceito. Um stash é um
"contexto", um conjunto de variáveis logicamente relacionadas e
agrupadas com a finalidade de facilitar seu uso e manutenção. Nada
mais de convenções de chamadas de métodos, ou de contagem de
argumentos para saber se todos os parâmetros foram
passados. Simplesmente inclua os dados relevantes no stash, baseados
na chave correta, e permita que o método interessado os utilize quando
e como bem entender.
O stash é inicializado a cada nova requisição com as informações
provenientes dela, e, depois do processamento, é passado na íntegra
para o objeto Template, que disponibilizará as informações contidas
nele para o template que será processado para a geração de conteúdo.
=back
=cut
__PACKAGE__->mk_accessors( qw[ config template stash ] );
=head1 METHODS
=over 4
=item init_config
Inicializa e carrega o subsistema de configuração.
Utilizamos um objeto AppConfig para interpretar o nosso arquivo de
configuração, e simplemente mantemos toda a configuração de que
precisamos em memória em tempo integral. Desta forma, economizamos
recursos mais escassos que memória RAM, Pois não precisamos mais ler e
interpretar o arquivo de configuração.
=cut
sub init_config{
my $self = shift;
my $cfg = new AppConfig
( { CASE => 1,
        PEDANTIC => 1,
        CREATE => 1,
        GLOBAL => { EXPAND => 1,
                 ARGCOUNT => ARGCOUNT_ONE,
                 },
}
);
$cfg->file( $ROOTDIR.'/etc/auth-app.cfg' );
return { error => { $cfg->varlist( '^error_message_', 1 ) },
         template => { $cfg->varlist( '^template_', 1 ) },
         };
}
=item init_template
Inicializa e instancia um objeto Template, interpretador de scripts de
template do Template Toolkit. Um único objeto Template dá conta de
interpretar e fazer funcionar todas as páginas dinâmicas baseadas em
templates da nossa aplicação.
=cut
sub init_template{
my $self = shift;
# inicializa o template engine, baseado na configuração incorporada.
my $options =
{ INCLUDE_PATH => $ROOTDIR.'/'.$self->config->{template}{include_path},
PRE_CHOMP => $self->config->{template}{pre_chomp} || 0,
POST_CHOMP => $self->config->{template}{post_chomp} || 0,
TRIM => $self->config->{template}{trim} || 0,
COMPILE_EXT => $self->config->{template}{compile_ext} || 'c',
COMPILE_DIR => $self->config->{template}{compile_dir} || '/tmp/ttc',
};
# Retorna um template prontinho para usar.
return new Template( $options );
}
=item cgiapp_init
Sobrecarga de método do CGI::Application para inicialização da nossa
aplicação. Este método é chamado automaticamente pelo CGI::Application
quando da inicialização do objeto de aplicação.
=cut
sub cgiapp_init{
my $self = shift;
$self->config( $self->init_config );
$self->template( $self->init_template );
}
=item setup
Método sobrecarregado do objeto CGI::Application para permitir a
configuração do nosso objeto de aplicação.
Este método é chamado sempre que um novo objeto de aplicação é criado,
logo depois de cgiapp_init(), mas antes de cgiapp_prerun() / run() /
cgiapp_postrun().
É aqui que estabelecemos valores e comportamento default para nossa
aplicação.
=cut
sub setup{
my $self = shift;
$self->mode_param( 'mode' );
$self->start_mode( 'action' );
$self->run_modes( action => 'action' );
}
=item init_stash
Stash é o conceito de que tudo o que está relacionado com os
diferentes métodos da aplicação deve permanecer junto. Por isso,
preparamos uma referência para um hash, que inicializamos com os
diversos parâmetros provenientes da requisição e o disponibilizamos
para o método de geração de conteúdo. Assim, o método de geração de
conteúdo simplesmente não precisa mais se preocupar com a proveniência
ou o destino dos parâmetros iniciais ou do conteúdo gerado. Basta que
se posicione estas coisas em seus devidos lugares no "stash" e passar
o controle para o próximo método da nossa cadeia de responsabilidades.
=cut
sub init_stash{
my $self = shift;
# inicializa uma referência de hash caso não tenhamos uma...
my $stash = $self->stash || {};
# Recupera uma referência para um tied hash contendo os parâmetros
# passados para o script na requisição.
my $vars = $self->query->Vars;
# copia organizadamente estes parâmetros, tomando o cuidado de
# expandir novamente as listas de valores que alguns deles podem
# conter.
map { $stash->{$_} = $vars->{$_} =~ /\0/?
         [ split qr/\0/, $vars->{$_} ] : $vars->{$_};
} keys %$vars;
# inicializa nosso stash em sua posição definitiva.
$self->stash( $stash );
}
=item cgiapp_prerun
Chamado automaticamente pelo CGI::Application a cada nova requisição,
antes que o método associado ao modo de operação determinado por
CGI::Application::run() tenha chance de rodar.
Vamos utilizar este "gancho" para inicializar o Stash de nossa
aplicação corretamente, de modo a permitir que os métodos geradores de
conteúdo ignorem a proveniência dos parâmetros utilizados em cada
requisição. Tudo o que precisam estará no Stash.
=cut
sub cgiapp_prerun{
my $self = shift;
# Prepara o stash que vai ser utilizado nesta requisição.
$self->init_stash;
return $self;
}
=item cgiapp_postrun
Chamado automaticamente pelo CGI::Application a cada nova requisição,
logo depois que o método de geração de conteúdo terminar de rodar.
Vamos utilizar este método para gerar efetivamente o conteúdo, já que
os métodos geradores de conteúdo apenas realizam seu processamento
interno e colocam todas as informações necessárias para a geração de
conteúdo nas variáveis definidas no Stash, juntamente com uma variável
especial chamada "template". Esta chave indica qual o template que
devemos passar ao Template Toolkit para obter o conteúdo que
desejamos.
Depois de rodar, este método substitui o que quer que tenha sido
gerado como "conteúdo" pelo método associado com o run-mode atual e
permite ao CGI::Application enviar corretamente o conteúdo gerado ao
browser.
=cut
sub cgiapp_postrun{
my $self = shift;
my $output;
my $out = shift;
if( UNIVERSAL::isa( $self->template, 'Template' ) ){
$self->template->process( $self->stash->{template},
                         $self->stash,
                         \$output
                         )
or $self->template->process( $self->config->{template}{error},
                                 { %{$self->stash},
                                 error => $self->template->error()
                                 },
                                 \$output
                                 );
}else{
$output = $self->config->{error}{template_not_defined};
$self->header_type( 'header' );
$self->header_add( type => 'text/plain' );
}
$$out = $output;
}
=item action
Finalmente, mas não menos importante, implementamos a "ação default"
de um módulo (ou seja, um erro, já que este módulo é considerado
"abstrato" e não deveria ser instanciado ou rodado como um módulo
qualquer...).
=cut
sub action{
my $self = shift;
$self->stash->{template} = $self->config->{template}{error};
$self->stash->{error_message}
= $self->config->{error}{action_not_defined_for_this_object};
}
=back
=head1 AUTHOR
Luis Campos de Carvalho - Engenharia de Software Ltda.
=head1 REVISION
$Revision: $
=head1 COPYRIGHT
Copyleft 2005 Engenharia de Software Ltda.
Doado aos São Paulo Perl Mongers.
This module is free software; you can redistribute it and/or modify it
under the term of the GNU Public License.
=head1 SEE ALSO
L<CGI::Application>
1;
</pre>
</div>
<div class="paragraph">
<p>Como utilizar o Controlador implementado</p>
<p>Como extender o contorlador implementado aqui para implementar ações e novos controladores conforme a necessidade.</p>
</div>
<div class="paragraph">
<p>Como implementar a página dinâmica (CGI)</p>
<p>Como implementar o script CGI que será chamado para disparar o processamento.</p>
</div>
<div class="paragraph">
<p>Construindo mais páginas com o mesmo controlador</p>
<p>Como continuar escrevendo a aplicação, repetindo parte do processo descrito neste artigo.</p>
</div>
<div class="paragraph">
<p>Interligando aplicativos</p>
<p>Como interligar aplicativos através de cookies, parâmetros de estado e redirecionamentos.</p>
</div>
<div class="paragraph">
<p>Uma palavra de cautela</p>
<p>Lembre-os do planejamento, do cuidado com a implementação e da necessidade de testes e documentação!</p>
</div>
<div class="paragraph">
<p>Conclusão</p>
</div>
<div class="paragraph">
<p>Créditos</p>
<p>Este artigo foi escrito por Luis Campos de Carvalho, também conhecido por <i>Monsieur Champs</i>, Líder dos <a href="http://sao-paulo.pm.org/">São Paulo Perl Mongers</a>, empresário e fanático por Perl e Bancos de Dados Oracle.</p>
<p>Contato: (11)9297-4646 ou mailto:<a href="mailto:monsieur_champs@yahoo.com.br">monsieur_champs@yahoo.com.br</a></p>
</div>
<div class="paragraph">
<p>Licensa de Uso e Distribuição</p>
<pre class="code">Este artigo é um software livre; você pode redistribuí-lo e/ou
modificá-lo dentro dos termos da Licença Pública Geral GNU como
publicada pela Fundação do Software Livre (FSF); na versão 2 da
Licença, ou (na sua opnião) qualquer versão.
Este artigo é distribuído na esperança que possa ser útil,
mas SEM NENHUMA GARANTIA; sem uma garantia implicita de ADEQUAÇÃO
a qualquer MERCADO ou APLICAÇÃO EM PARTICULAR. Veja a
Licença Pública Geral GNU para maiores detalhes.
Você deve ter recebido uma cópia da Licença Pública Geral GNU
junto com este programa. Se não, escreva para a Fundação do Software
Livre(FSF) Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA</pre>
</div>
</body>
</html>