<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>DestaqueBlog &#187; dicas</title>
	<atom:link href="http://weblog.destaquenet.com/tag/dicas/feed/" rel="self" type="application/rss+xml" />
	<link>http://weblog.destaquenet.com</link>
	<description>Blog da equipe Destaquenet.</description>
	<lastBuildDate>Tue, 23 Nov 2010 17:06:50 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</generator>
		<item>
		<title>Emacs e Slime: Seu Laboratório Lisp</title>
		<link>http://weblog.destaquenet.com/2010/11/18/emacs-e-slime-seu-laboratorio-lisp/</link>
		<comments>http://weblog.destaquenet.com/2010/11/18/emacs-e-slime-seu-laboratorio-lisp/#comments</comments>
		<pubDate>Thu, 18 Nov 2010 12:50:40 +0000</pubDate>
		<dc:creator>Daniel Martins</dc:creator>
				<category><![CDATA[Português]]></category>
		<category><![CDATA[Tutoriais]]></category>
		<category><![CDATA[clojure]]></category>
		<category><![CDATA[dicas]]></category>
		<category><![CDATA[emacs]]></category>
		<category><![CDATA[lisp]]></category>
		<category><![CDATA[slime]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://weblog.destaquenet.com/?p=1175</guid>
		<description><![CDATA[Esse post vai para quem está estudando Clojure (ou outro Lisp qualquer) e está tendo dificuldades para configurar o Emacs. Não é, Ronaldo?! Esteja preparado para uma enorme quantidade de gambiarras. ELPA A primeira coisa que faço numa nova instalação &#8230; <a href="http://weblog.destaquenet.com/2010/11/18/emacs-e-slime-seu-laboratorio-lisp/">Continue lendo <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Esse post vai para quem está estudando <a href="http://clojure.org">Clojure</a> (ou outro <a href="http://en.wikipedia.org/wiki/Lisp_(programming_language)">Lisp</a> qualquer) e está tendo dificuldades para configurar o <a href="http://www.gnu.org/software/emacs/">Emacs</a>. Não é, <a href="http://twitter.com/rferraz">Ronaldo</a>?! <img src='http://weblog.destaquenet.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>Esteja preparado para uma enorme quantidade de gambiarras.</p>
<h3>ELPA</h3>
<p>A primeira coisa que faço numa nova instalação do Emacs é configurar o <a href="http://tromey.com/elpa">ELPA</a>, ou <em>Emacs Lisp Package Archive</em>. Para quem não conhece, pense no ELPA como se fosse um <code>apt-get</code> da vida, guardadas as devidas proporções (como veremos a seguir).</p>
<p>As instruções para instalação podem ser vistas <a href="http://tromey.com/elpa/install.html">nesta página</a>. Se tudo der certo, agora deve ser possível rodar o comando <code>M-x package-list-packages &lt;ENTER&gt;</code>. Leia <a href="http://sean.wenzel.net/docs/emacs/quick_reference/">este documento</a> caso tenha dificuldades para usar atalhos e comandos do Emacs.</p>
<p>Após alguns instantes, a lista de pacotes disponíveis para instalação via ELPA deve ser exibida num buffer secundário.</p>
<p><span id="more-1175"></span></p>
<h3>Clojure-Mode</h3>
<p>Coloque o cursor sobre a linha do pacote <code>swank-clojure</code> e pressione a tecla <code>I</code> para marcá-lo para instalação. Faça o mesmo com o pacote <a href="http://www.emacswiki.org/ParEdit"><code>paredit</code></a>. Se outros pacotes foram marcados acidentalmente, mova o cursor para as linhas correspondentes e pressione <code>U</code> para desfazer a marcação. Pressione <code>X</code> para prosseguir com a instalação.</p>
<p>Feito isso, ao rodar o comando <code>M-x slime &lt;ENTER&gt;</code>, o Emacs irá perguntar se você deseja que ele faça o download dos JARs de Clojure. Não adianta dizer que sim, pois não irá funcionar. <img src='http://weblog.destaquenet.com/wp-includes/images/smilies/icon_sad.gif' alt=':-(' class='wp-smiley' /> </p>
<p>Para resolver isso, baixe manualmente os <a href="http://clojure.org/downloads">JARs de Clojure</a> e os coloque no diretório <code>~/.clojure/</code>. Em seguida, baixe o <a href="http://clojars.org/repo/swank-clojure/swank-clojure/">JAR mais recente</a> projeto <a href="https://github.com/technomancy/swank-clojure">swank-clojure</a> e o coloque no diretório <code>~/.swank-clojure/</code>.</p>
<p>Pronto. Reinicie o Emacs e rode o comando <code>M-x slime &lt;ENTER&gt;</code> para iniciar um REPL Clojure dentro do Emacs!</p>
<div id="attachment_1363" class="wp-caption aligncenter" style="width: 310px"><a href="http://weblog.destaquenet.com/wp-content/uploads/2010/11/clj-repl.png"><img src="http://weblog.destaquenet.com/wp-content/uploads/2010/11/clj-repl-300x168.png" alt="REPL Clojure" title="clj-repl" width="300" height="168" class="size-medium wp-image-1363" /></a><p class="wp-caption-text">Clojure no Emacs Slime</p></div>
<p>Vale lembrar que o Slime fornece vários recursos para edição de código, como auto-completion, exibição de docstrings, entre outras coisas. Consulte o <a href="http://common-lisp.net/project/slime/">site do projeto</a> para mais informações.</p>
<h3>Outros Lisps</h3>
<p>Rode o comando abaixo para instalar duas das principais implementações open source do ANSI Common Lisp, <a href="http://www.gnu.org/software/clisp">CLISP</a> e <a href="http://www.sbcl.org/">SBCL</a>:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ <span style="color: #c20cb9; font-weight: bold;">sudo</span> <span style="color: #c20cb9; font-weight: bold;">apt-get</span> <span style="color: #c20cb9; font-weight: bold;">install</span> clisp sbcl</pre></div></div>

<p>Finalmente, abra o arquivo <code>~/.emacs</code> e cole o código a seguir na última linha:</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">;; paredit + lisp modes</span>
<span style="color: #66cc66;">&#40;</span>defvar *lisp-modes* '<span style="color: #66cc66;">&#40;</span>clojure-mode-hook lisp-mode-hook emacs-lisp-mode-hook
                       lisp-interaction-mode-hook slime-repl-mode-hook<span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;">;; config used in all lisp modes</span>
<span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">defun</span> lisp-config <span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>
  <span style="color: #66cc66;">&#40;</span>paredit-mode t<span style="color: #66cc66;">&#41;</span>
  <span style="color: #66cc66;">&#40;</span>show-paren-mode t<span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>
&nbsp;
<span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">mapc</span> <span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">lambda</span> <span style="color: #66cc66;">&#40;</span>mode<span style="color: #66cc66;">&#41;</span>
        <span style="color: #66cc66;">&#40;</span>add-hook mode 'lisp-config<span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span> *lisp-modes*<span style="color: #66cc66;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;">;; other lisp implementations</span>
<span style="color: #66cc66;">&#40;</span>eval-after-load <span style="color: #ff0000;">&quot;slime&quot;</span>
  '<span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">progn</span> <span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">setq</span> slime-lisp-implementations
                '<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#40;</span>sbcl <span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;/usr/bin/sbcl&quot;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>
                  <span style="color: #66cc66;">&#40;</span>clisp <span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;/usr/bin/clisp&quot;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<h3>Swank</h3>
<p>As últimas versões do Slime já trazem servidores Swank para diversas implementações Lisp, mas curiosamente essa parte do código não é baixada quando se instala o Slime via ELPA.</p>
<p>Certifique-se de que um cliente CVS esteja instalado e rode o comando abaixo para fazer o download da última versão do Slime:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ <span style="color: #c20cb9; font-weight: bold;">cvs</span> <span style="color: #660033;">-d</span> :pserver:anonymous:anonymous<span style="color: #000000; font-weight: bold;">@</span>common-lisp.net:<span style="color: #000000; font-weight: bold;">/</span>project<span style="color: #000000; font-weight: bold;">/</span>slime<span style="color: #000000; font-weight: bold;">/</span>cvsroot <span style="color: #c20cb9; font-weight: bold;">co</span> slime</pre></div></div>

<p>Em seguida, vá até o diretório <code>slime</code> recém-criado e copie todos os arquivos deste diretório por cima dos arquivos instalados via ELPA em <code>~/.emacs.d/elpa/slime-&lt;VERSION&gt;</code>. Aproveite e remova o arquivo <code>slime.elc</code> deste último diretório, já que tal arquivo é uma versão compilada de um arquivo que acabamos de sobrescrever.</p>
<p>Reinicie o Emacs. O comando <code>M-x slime &lt;ENTER&gt;</code> continua servindo para iniciar um REPL Clojure. Para iniciar um REPL SBCL, por exemplo, rode o comando <code>M-- M-x slime &lt;ENTER&gt;</code>, digite <code>sbcl</code> e pressione <code>&lt;ENTER&gt;</code>.</p>
<div id="attachment_1364" class="wp-caption aligncenter" style="width: 310px"><a href="http://weblog.destaquenet.com/wp-content/uploads/2010/11/sbcl-repl.png"><img src="http://weblog.destaquenet.com/wp-content/uploads/2010/11/sbcl-repl-300x168.png" alt="REPL SBCL" title="sbcl-repl" width="300" height="168" class="size-medium wp-image-1364" /></a><p class="wp-caption-text">SBCL no Emacs Slime</p></div>
<h4><em>&#8220;Nunca vi tanta gambiarra num lugar só&#8230;&#8221;</em></h4>
<p>Tudo seria mais fácil se o ELPA disponibilizasse versões minimamente atualizadas &#8212; e não-&#8221;capadas&#8221; &#8212; de certos pacotes, por isso creio que essa complicação deva ser temporária. Atualizarei este texto quando a coisa mudar. Para melhor, espero.</p>
]]></content:encoded>
			<wfw:commentRss>http://weblog.destaquenet.com/2010/11/18/emacs-e-slime-seu-laboratorio-lisp/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Programação Funcional: vale a pena aprender?</title>
		<link>http://weblog.destaquenet.com/2009/06/16/programacao-funcional-vale-a-pena-aprender/</link>
		<comments>http://weblog.destaquenet.com/2009/06/16/programacao-funcional-vale-a-pena-aprender/#comments</comments>
		<pubDate>Tue, 16 Jun 2009 19:32:54 +0000</pubDate>
		<dc:creator>Daniel Martins</dc:creator>
				<category><![CDATA[Português]]></category>
		<category><![CDATA[Programação]]></category>
		<category><![CDATA[clojure]]></category>
		<category><![CDATA[dicas]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[programação funcional]]></category>
		<category><![CDATA[project euler]]></category>
		<category><![CDATA[projeto]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://weblog.destaquenet.com/?p=511</guid>
		<description><![CDATA[Antes de começar com Python, eu não conhecia praticamente nada sobre Programação Funcional. Para falar a verdade, eu até achava que os paradigmas Funcional e Procedural eram a mesma coisa pelo fato do segundo se basear em funções métodos (e &#8230; <a href="http://weblog.destaquenet.com/2009/06/16/programacao-funcional-vale-a-pena-aprender/">Continue lendo <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Antes de começar com <a href="http://python.org">Python</a>, eu não conhecia praticamente nada sobre <a href="http://en.wikipedia.org/wiki/Functional_programming">Programação Funcional</a>. Para falar a verdade, eu até achava que os paradigmas Funcional e <a href="http://en.wikipedia.org/wiki/Procedural_programming">Procedural</a> eram a mesma coisa pelo fato do segundo se basear em <span style="text-decoration: line-through;">funções</span> métodos (e várias pessoas com as quais eu convivo e trabalho também têm esse equívoco). Felizmente, tudo começou a fazer sentido quando aprendi, graças ao Python, coisas  como <a href="http://docs.python.org/library/functions.html#map"><code>map()</code></a>, <a href="http://docs.python.org/library/functions.html#reduce"><code>reduce()</code></a>, <a href="http://docs.python.org/library/functions.html#filter"><code>filter()</code></a>, <a href="http://docs.python.org/tutorial/classes.html#generators">generators</a> e <a href="http://docs.python.org/tutorial/datastructures.html#list-comprehensions">list comprehensions</a>.</p>
<p>Como o mercado é dominado por linguagens imperativas, é bem difícil encontrar pessoas que realmente conheçam e utilizem linguagens funcionais com certa regularidade. Mas isso não significa, de forma alguma, que não há espaço para tais linguagens. Na verdade, a tendência é que elas ganhem cada vez mais espaço.</p>
<p><span id="more-511"></span></p>
<h3>Por que aprender uma linguagem funcional?</h3>
<p>Em linguagens imperativas, a programação de um <em>software</em> consiste basicamente na definição e manipulação de seu estado interno, conduzida de modo a resolver um determinado problema. Portanto, pode-se dizer que <em>softwares</em> escritos em tais linguagens são movidos a <em>efeitos colaterais</em>, pois a forma e a ordem com que seu estado interno é modificado é o que define o funcionamento do <em>software</em>.</p>
<p>Em <em>softwares</em> triviais isso não chega a ser um problema, mas quando falamos de <em>softwares</em> grandes e complexos, essa volatilidade complica, e muito, a vida de quem os desenvolve e os mantém.</p>
<p>Linguagens funcionais, por outro lado, são conhecidas por evitar efeitos colaterais ao máximo, e isso nos trás uma infinidade de vantagens. Assim como na matemática, executar uma função <em>f(x)</em> com o mesmo argumento <em>N</em> vezes produz <em>sempre</em> o mesmo resultado, e por isso:</p>
<ol>
<li>o código fica muito mais fácil de entender, debugar e testar. Tudo o que você precisa saber sobre uma função está nos seus argumentos;</li>
<li>o código, por também contar com estruturas de dados imutáveis, pode ser executado de forma concorrente, com poucas ou quaisquer modificações, e você nunca terá de se preocupar com locks, <a href="http://en.wikipedia.org/wiki/Race_condition">condições de corrida</a>, <a href="http://en.wikipedia.org/wiki/Deadlock">deadlocks</a> e coisas do tipo;</li>
<li>o compilador pode fazer otimizações impossíveis de serem feitas em linguagens imperativas, como automaticamente paralelizar ou reordenar chamadas a funções;</li>
</ol>
<p>Enfim, esse é só um resumo das vantagens que linguagens funcionais têm sobre linguagens imperativas. Confira os links no final deste post para saber mais.</p>
<h3>Escolhendo a linguagem</h3>
<p>Clojure, Erlang, Haskell, J, (Common) Lisp, Scheme. E essas são somente algumas das linguagens com forte base funcional disponíveis por aí.</p>
<p>Mas qual delas escolher? Cada pessoa segue um ritual diferente ao escolher uma nova ferramenta e por isso não sou eu quem irá te dizer qual delas escolher. O que eu posso fazer é dar algumas sugestões:</p>
<ul>
<li>Quer saber como tudo começou? Aprenda <a href="http://en.wikipedia.org/wiki/Common_Lisp">Common Lisp</a>.</li>
<li>Quer uma linguagem parecida com Lisp, mas que rode na JVM? <a href="http://clojure.org/">Clojure</a> pode ser uma boa.</li>
<li>Quer uma linguagem com uma sintaxe sucinta e poderosa? Dê uma olhada em <a href="http://www.jsoftware.com/">J</a>.</li>
<li>Quer uma linguagem que facilite a construção de <em>softwares</em> distribuídos e de alta disponibilidade? Vá de <a href="http://www.erlang.org/">Erlang</a>.</li>
<li><strong>Quer uma linguagem que mude a forma com que você pensa sobre programação? Aprenda qualquer uma delas!</strong></li>
</ul>
<p>Erlang e Haskell estão na minha mira mas, no momento, eu optei por aprender Clojure.</p>
<h3>Praticar é essencial</h3>
<p>Ok, escolhida a linguagem de programação, o próximo passo é ler muito e praticar mais ainda.</p>
<p>Para quem está interessado em aprender Clojure, material não falta. Apesar de bem nova (apenas 2 anos de existência), a linguagem vem ganhando popularidade e muito vêm sendo escrito sobre ela. Aliás, <a href="http://www.pragprog.com/titles/shcloj/programming-clojure">o primeiro livro sobre Clojure</a> foi publicado recentemente pela <a href="http://pragprog.com/">Pragmatic Bookshelf</a>. (Pode comprar sem medo, o livro é excelente)</p>
<p>Agora, independente da linguagem escolhida, uma forma interessante de aplicar os conhecimentos recém-adquiridos é tentar resolver os problemas propostos pelo <a href="http://projecteuler.net/">Project Euler</a>, um website com centenas de problemas do tipo <a href="http://projecteuler.net/index.php?section=problems&amp;id=12">&#8220;What is the value of the first triangle number to have over five hundred divisors?&#8221;</a>. No começo eu sofri um pouco para pegar as manhas da linguagem, mas depois de ter resolvido uns 10-15 problemas desse tipo, eu sinto que as coisas estão começando a fazer sentido.</p>
<p>Aos interessados, eu acabei de disponibilizar no Github <a href="http://github.com/danielfm/euler-clojure/tree/master">um projeto</a> contendo as soluções, todas em Clojure, para alguns dos problemas propostos.</p>
<h3>E vocês, o que recomendam?</h3>
<p>Depois de algumas semanas pesquisando sobre Programação Funcional, eu me sinto como se tivesse deixado de lado um vício: meio assustador no início, mas melhor a cada dia que passa!</p>
<p>Aos que já conhecem ou trabalham regularmente com linguagens funcionais, que dica vocês dão para quem acabou de tomar a <a href="http://en.wikipedia.org/wiki/Redpill">pílula vermelha</a>?</p>
<h3>Referências</h3>
<ul>
<li><a href="http://www.paulgraham.com/onlisp.html">On Lisp Book</a> (Obrigatório!);</li>
<li><a href="http://www.defmacro.org/ramblings/fp.html">Functional Programming For The Rest Of Us</a> (Obrigatório!);</li>
<li><a href="http://www2.computer.org/portal/web/computingnow/0609/whatsnew/cise">The Promises Of Functional Programming</a> (Atualização);</li>
<li><a href="http://www.pragprog.com/titles/shcloj/programming-clojure">Programming Clojure</a>;</li>
<li><a href="http://java.ociweb.com/mark/clojure/article.html">Clojure &#8211; Functional Programming for the JVM</a>;</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://weblog.destaquenet.com/2009/06/16/programacao-funcional-vale-a-pena-aprender/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Django e settings.py: dicas e boas práticas</title>
		<link>http://weblog.destaquenet.com/2009/04/13/django-e-settingspy-dicas-e-boas-praticas/</link>
		<comments>http://weblog.destaquenet.com/2009/04/13/django-e-settingspy-dicas-e-boas-praticas/#comments</comments>
		<pubDate>Tue, 14 Apr 2009 01:32:06 +0000</pubDate>
		<dc:creator>Daniel Martins</dc:creator>
				<category><![CDATA[Português]]></category>
		<category><![CDATA[Programação]]></category>
		<category><![CDATA[boas práticas]]></category>
		<category><![CDATA[configuração]]></category>
		<category><![CDATA[design]]></category>
		<category><![CDATA[dicas]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[testes]]></category>

		<guid isPermaLink="false">http://weblog.destaquenet.com/?p=415</guid>
		<description><![CDATA[Em vez de ficar rasgando elogios ao Django, como de costume, eu tentarei aproveitar o melhor o meu (e o seu) tempo e mostrar algo que é de fato útil. O assunto em questão envolve o módulo settings.py, cuja função &#8230; <a href="http://weblog.destaquenet.com/2009/04/13/django-e-settingspy-dicas-e-boas-praticas/">Continue lendo <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Em vez de ficar rasgando elogios ao <a href="http://djangoproject.com">Django</a>, como de costume, eu tentarei aproveitar o melhor o meu (e o seu) tempo e mostrar algo que é de fato útil. O assunto em questão envolve o módulo <code>settings.py</code>, cuja função é fornecer as configurações necessárias para que o projeto funcione. Apesar deste módulo ser uma peça importante em qualquer projeto Django, pouco se fala sobre ele, ou sobre boas práticas associadas a ele. Por isso, achei que seria interessante mostrar a minha visão pessoal sobre o assunto.</p>
<h3>O poder do Python</h3>
<p>Quem não está acostumado a trabalhar com Django pode estranhar o fato deste utilizar um script <a href="http://python.org">Python</a> para fazer a configuração do projeto: o famoso <code>settings.py</code>. E essa estranheza é justificada, afinal muitos desenvolvedores &#8212; principalmente os que vêm de outras linguagens &#8212; estão acostumados a trabalhar com ferramentas onde tal tarefa é feita com arquivos XML ou coisa assim.</p>
<p>O fato é que utilizar um script Python para tal é uma excelente idéia, pois você deixa de depender de algum tipo de estrutura estática (e muitas vezes inconveniente) para algo mais flexível e poderoso, que, no caso, é a própria linguagem de programação. Isso faz uma grande diferença em situações onde tal flexibilidade é necessária.</p>
<p>Quer exemplos?<span id="more-415"></span></p>
<h3>Ambientes de desenvolvimento/produção</h3>
<p>Eis como fizemos aqui. Primeiramente, defina a variável de ambiente <code>WORKSPACE</code>. No Linux, isso pode ser feito através do arquivo <code>~/.bashrc</code>:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #7a0874; font-weight: bold;">export</span> <span style="color: #007800;">WORKSPACE</span>=<span style="color: #000000;">1</span></pre></div></div>

<p>A idéia é possibilitar a criação de perfis de configuração, cada qual correspondente a um ambiente de execução. A existência ou não da variável de ambiente é o que define o ambiente no qual a aplicação roda.</p>
<p>Continuando com o exemplo, no diretório raíz do seu projeto Django, crie o módulo <code>environment.py</code>, com o seguinte conteúdo:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> production_mode<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #ff7700;font-weight:bold;">not</span> development_mode<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> development_mode<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #483d8b;">'WORKSPACE'</span> <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">environ</span></pre></div></div>

<p>Finalmente, modifique o módulo <code>settings.py</code> para que este utilize tais métodos onde for necessário definir configurações diferentes para ambientes de execução diferentes:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> environment <span style="color: #ff7700;font-weight:bold;">import</span> production_mode
&nbsp;
<span style="color: #808080; font-style: italic;"># Configurações padrão</span>
PREPEND_WWW = <span style="color: #008000;">False</span>
DEBUG = <span style="color: #008000;">True</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Configurações para ambiente de produção</span>
<span style="color: #ff7700;font-weight:bold;">if</span> production_mode<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    PREPEND_WWW = <span style="color: #008000;">True</span>
    DEBUG = <span style="color: #008000;">False</span>
&nbsp;
MIDDLEWARE_CLASSES = <span style="color: black;">&#40;</span>
    <span style="color: #483d8b;">'django.middleware.common.CommonMiddleware'</span>,
    <span style="color: #483d8b;">'django.contrib.sessions.middleware.SessionMiddleware'</span>,
    <span style="color: #483d8b;">'django.contrib.auth.middleware.AuthenticationMiddleware'</span>,
    <span style="color: #483d8b;">'django.middleware.doc.XViewMiddleware'</span>,
    <span style="color: #483d8b;">'pages.middleware.PageFallbackMiddleware'</span>,
    <span style="color: #483d8b;">'django.contrib.redirects.middleware.RedirectFallbackMiddleware'</span>,
<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Habilitando caching em modo de produção</span>
<span style="color: #ff7700;font-weight:bold;">if</span> production_mode<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    MIDDLEWARE_CLASSES += <span style="color: black;">&#40;</span>
        <span style="color: #483d8b;">'django.middleware.cache.UpdateCacheMiddleware'</span>,
        <span style="color: #483d8b;">'django.middleware.cache.FetchFromCacheMiddleware'</span>,
    <span style="color: black;">&#41;</span></pre></div></div>

<p>Não tenha medo de utilizar os recursos do Python onde eles se fizerem necessários, apenas tomando cuidado para não exagerar e acabar criando acidentalmente um <a href="http://en.wikipedia.org/wiki/Genetic_algorithm">Algoritmo Genético</a> para configurar seu projeto!</p>
<h3>Informações secretas (IPs, usernames, passwords, etc)</h3>
<p>Praticamente não há quem ignore a importância de um bom sistema de controle de revisões (<a href="http://subversion.tigris.org">Subversion</a>, <a href="http://git-scm.org">Git</a>, etc). O problema é que, muitas vezes, não tomamos o devido cuidado com o que colocamos dentro dos nossos repositórios. Sempre que possível, informações secretas &#8212; como endereços de IP, usernames e passwords &#8212; devem ser mantidas fora dos repositórios.</p>
<p>Em um projeto Django, as informações de acesso a servidores de e-mail e bancos de dados devem estar disponíveis através do módulo  <code>settings.py</code>. Felizmente, existe um jeito simples de manter essas informações separadas das outras configurações.</p>
<p>Primeiramente, abra o arquivo <code>settings.py</code> do seu projeto e remova todas as senhas e outras informações que você considere secretas. Em seguida, cole o seguinte trecho de código no final do arquivo:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">try</span>:
    <span style="color: #ff7700;font-weight:bold;">from</span> private <span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #66cc66;">*</span>
<span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">ImportError</span>:
    <span style="color: #ff7700;font-weight:bold;">pass</span></pre></div></div>

<p>Feito isso, crie o módulo <code>private.py</code> no diretório raíz do seu projeto Django. Coloque nele as configurações que você removeu do <code>settings.py</code> no passo anterior. Exemplo:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> environment <span style="color: #ff7700;font-weight:bold;">import</span> production_mode
&nbsp;
SECRET_KEY = <span style="color: #483d8b;">'secret_key_do_seu_projeto'</span>
&nbsp;
EMAIL_HOST = <span style="color: #483d8b;">'localhost'</span>
EMAIL_HOST_PASSWORD = <span style="color: #483d8b;">'senha_email_local'</span>
&nbsp;
DATABASE_HOST = <span style="color: #483d8b;">'localhost'</span>
DATABASE_USER = <span style="color: #483d8b;">'username_db_local'</span>
DATABASE_PASSWORD = <span style="color: #483d8b;">'senha_db_local'</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> production_mode<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    EMAIL_HOST = <span style="color: #483d8b;">'ip_email_producao'</span>
    EMAIL_HOST_PASSWORD = <span style="color: #483d8b;">'senha_email_producao'</span>
&nbsp;
    DATABASE_HOST = <span style="color: #483d8b;">'ip_db_producao'</span>
    DATABSE_USER = <span style="color: #483d8b;">'username_db_producao'</span>
    DATABASE_PASSWORD = <span style="color: #483d8b;">'senha_db_producao'</span></pre></div></div>

<p>Certifique-se de adicionar o arquivo <code>private.py</code> no <em>ignore</em> (<code>.cvsignore</code> para <a href="http://www.nongnu.org/cvs/">CVS</a>, <code>.gitignore</code> para Git, etc) correspondente ao sistema de controle de revisões sendo utilizado. Faça o commit e pronto.</p>
<p>Para finalizar, se o seu repositório já contém uma versão &#8220;insegura&#8221; do módulo <code>settings.py</code>, mude as senhas dos seus servidores.</p>
<h3>E você, o que recomenda?</h3>
<p>Tem alguma dica útil para compartilhar conosco? Nós adoraríamos ouvir o que você tem a dizer!</p>
]]></content:encoded>
			<wfw:commentRss>http://weblog.destaquenet.com/2009/04/13/django-e-settingspy-dicas-e-boas-praticas/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Por favor, usem typesafe enums!</title>
		<link>http://weblog.destaquenet.com/2009/02/18/por-favor-usem-typesafe-enums/</link>
		<comments>http://weblog.destaquenet.com/2009/02/18/por-favor-usem-typesafe-enums/#comments</comments>
		<pubDate>Thu, 19 Feb 2009 02:20:35 +0000</pubDate>
		<dc:creator>Daniel Martins</dc:creator>
				<category><![CDATA[Português]]></category>
		<category><![CDATA[Programação]]></category>
		<category><![CDATA[análise]]></category>
		<category><![CDATA[banco de dados]]></category>
		<category><![CDATA[boas práticas]]></category>
		<category><![CDATA[design]]></category>
		<category><![CDATA[dicas]]></category>
		<category><![CDATA[enums]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[modelagem]]></category>

		<guid isPermaLink="false">http://weblog.destaquenet.com/?p=349</guid>
		<description><![CDATA[Enquanto conversava com um amigo, há alguns dias atrás, surgiu um assunto a respeito de algo que costumamos ver com certa frequência. Vou tentar contextualizar essa conversa com o diagrama UML Entidade-Relacionamento a seguir: Nem irei perder tempo explicando esse &#8230; <a href="http://weblog.destaquenet.com/2009/02/18/por-favor-usem-typesafe-enums/">Continue lendo <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Enquanto conversava com um amigo, há alguns dias atrás, surgiu um assunto a respeito de algo que costumamos ver com certa frequência. Vou tentar contextualizar essa conversa com o diagrama <span style="text-decoration: line-through;">UML</span> Entidade-Relacionamento a seguir:</p>
<div id="attachment_358" class="wp-caption aligncenter" style="width: 310px"><a href="http://weblog.destaquenet.com/wp-content/uploads/2009/02/diagram01.png"><img class="size-medium wp-image-358" title="Diagrama" src="http://weblog.destaquenet.com/wp-content/uploads/2009/02/diagram01-300x218.png" alt="Modelo-exemplo de banco de dados" width="300" height="218" /></a><p class="wp-caption-text">Modelo-exemplo de banco de dados</p></div>
<p>Nem irei perder tempo explicando esse modelo, até porque não há nada nele que precise de explicações. Bem, na verdade, há uma coisa sim: <strong>por que raios existe uma tabela só para armazenar os tipos de contato?!</strong> Qual é o problema com os bons e velhos códigos fixos (1-email, 2-celular, N-&#8230;)?</p>
<p>Um argumento a favor dessa abordagem é que &#8220;o usuário pode querer cadastrar um contato cujo tipo não seja suportado pela aplicação&#8221;. Mas será mesmo que colocar essa informação no banco de dados resolve o problema? E, mais importante, existe algum efeito colateral?</p>
<p>Bem, na minha experiência, embora essa abordagem pareça boa o bastante para resolver a questão num sistema simples de &#8220;cadastro&#8221;, a bomba explode quando o sistema passa a depender dessas flags para a realização de lógicas de negócio que vão além do famigerado CRUD. <span id="more-349"></span></p>
<h3>Anti-pattern?</h3>
<p>E se eu te dissesse que usar tabelas no banco de dados para armazenar flags é um anti-pattern? Digo isso pois, como veremos a seguir, as vantagens são irrisórias frente às desvantagens:</p>
<p><strong>Poluição do modelo de dados.</strong> Um sistema não muito grande provavelmente conta com mais de uma centena de tabelas. Isso todo mundo sabe. O que todo mundo também sabe, mas parece ignorar, é que não seria surpreendente constatar que 10-15% dessas tabelas serviriam apenas para armazenar flags (situação de pedido, estado civil, forma de pagamento, e por aí vai). E o pior: essas tabelas, quase sempre, possuem exatamente a mesma estrutura, que apelidei carinhosamente de <strong>tabelas código-descrição</strong>. A tabela <code>TipoContato</code>, mostrada no diagrama que encabeça este texto, é um exemplo desse tipo de tabela.</p>
<p><strong>Ruim para o meio ambiente.</strong> Poluir o modelo de dados com dezenas de <strong>tabelas código-descrição</strong> torna o modelo desnecessariamente maior e mais difícil de compreender. Imprimir um modelo desses consome mais papel, o que é ruim numa época onde o desenvolvimento sustentável está em voga. Quantas árvores suas <strong>tabelas código-descrição</strong> já derrubaram?</p>
<p><strong>Ultra-normalização.</strong> Quanto mais tabelas uma consulta envolver, mais joins, e mais dados indo e vindo para o banco de dados. E, considerando que o banco de dados é (quase) sempre o gargalo, isso não é muito bom para a performance do seu sistema.</p>
<p><strong>Poluição do modelo de objetos.</strong> As linguagens com suporte à orientação a objetos figuram entre as <a href="http://www.tiobe.com/content/paperinfo/tpci/index.html">mais usadas atualmente</a>, por isso eu diria que muito provavelmente você utiliza esse paradigma no desenvolvimento dos seus sistemas. Se este for o caso, também é bem provável que você utilize algum framework ORM para facilitar a tarefa de integração do software com o banco de dados. Como muitos frameworks ORM exigem um modelo de objetos compatível com o modelo de dados, bem, você acaba sendo forçado a poluir seu modelo de objetos com classes burras que apelidei carinhosamente de (pasmem!) <strong>classes código-descrição</strong>.</p>
<p><strong>Se tem tabela, tem cadastro.</strong> Bom, já que todas as flags possuem suas tabelinhas no banco de dados, isso significa que você, pobre desenvolvedor, terá que fazer &#8220;cadastros&#8221; para todas elas (e reze para não precisar fazer relatórios, também). Além da chatisse costumeira de se fazer um cadastro, devemos levar em consideração que fazê-los consome tempo e, tempo é dinheiro.</p>
<p><strong>Código quebradiço.</strong> Essa é feia. Vamos supor que, nessa nossa aplicação de gerenciamento de clientes/contatos, você queira incluir uma funcionalidade que permita enviar um torpedo ao cliente caso ele possua um contato do tipo <code>Celular</code>. Como mostrado no trecho de código a seguir, você fatalmente terá que incluir no código-fonte referências a registros armazenados no banco de dados, tornando o código quebradiço. Consequentemente, um único <code>UPDATE</code> ou <code>DELETE</code> executado por engano no banco de dados pode fazer o sistema cair de joelhos.</p>
<p>Enviando torpedos a um cliente:</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> enviarTorpedo<span style="color: #009900;">&#40;</span>Cliente cliente, <span style="color: #003399;">String</span> texto<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #666666; font-style: italic;">// Tenta localizar um contato do tipo celular</span>
    <span style="color: #000000; font-weight: bold;">for</span> <span style="color: #009900;">&#40;</span>Contato contato <span style="color: #339933;">:</span> cliente.<span style="color: #006633;">getContatos</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
         <span style="color: #666666; font-style: italic;">// WTF!! Estamos referenciando um registro do banco de dados!!</span>
         <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>contato.<span style="color: #006633;">getCodigo</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> 10L<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            SMSUtil.<span style="color: #006633;">enviarTorpedo</span><span style="color: #009900;">&#40;</span>contato.<span style="color: #006633;">getValor</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>, texto<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #000000; font-weight: bold;">break</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<h3>A solução</h3>
<p>Com certeza existem outros problemas que eu acabei deixando passar, mas espero que os problemas que listei já sejam o suficiente para te convencer que guardar flags em tabelas no banco de dados é uma má idéia.</p>
<p>Mas, como resolver isso então? Simples: <strong>use enums sempre que possível.</strong> A única desvantagem é que os usuários não poderão mais sair criando novas flags a torto e a direito, o que não é realmente um problema frente às vantagens que as enums oferecem<strong>.<br />
</strong></p>
<p><strong>Poluição do modelo de dados?</strong> As flags virariam campos numéricos ou alfanuméricos nas tabelas que as usam e o framework ORM se encarregaria de converter esses campos de/para objetos enum. Diga adeus às <strong>tabelas código-descrição</strong>.</p>
<p><strong>Ruim para o meio-ambiente?</strong> Sem as <strong>tabelas código-descrição</strong>, seu modelo fica menor, mais enxuto, facilitando a visualização e gastando menos papel ao imprimí-lo. A natureza agradece.</p>
<p><strong>Ultra-normalização?</strong> Usando enums você diminui a quantidade de ligações entre as tabelas, tornando as entidades um pouco mais auto-contidas. Menos tabelas, menos joins, mais performance.</p>
<p><strong>Poluição do modelo de objetos?</strong> As enums tomariam o lugar das <strong>classes código-descrição</strong>, o que por si só tornaria o código mais claro, pois, ao contrário das classes &#8220;normais&#8221;, as enums possuem um propósito bem definido.</p>
<p><strong>Se tem tabela, tem cadastro?</strong> Ué, por que você está com esse sorriso estampado no rosto?</p>
<p><strong>Código quebradiço?</strong> Quem mais se beneficia das enums são os trechos de código onde as lógicas de negócio são definidas. Mais especificamente, seu código deixa de referenciar registros do banco de dados, eliminando a fragilidade e melhorando a legibilidade. As enums também impedem que valores arbitrários sejam usados em atribuições e comparações, tornando o código ainda mais confiável. Um exemplo disso pode ser visto no código a seguir, que é uma adaptação <span style="text-decoration: line-through;">da atrocidade</span> do código mostrado anteriormente:</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> enviarTorpedo<span style="color: #009900;">&#40;</span>Cliente cliente, <span style="color: #003399;">String</span> texto<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">for</span> <span style="color: #009900;">&#40;</span>Contato contato <span style="color: #339933;">:</span> cliente.<span style="color: #006633;">getContatos</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
         <span style="color: #666666; font-style: italic;">// Bem melhor agora!</span>
         <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>contato.<span style="color: #006633;">getTipo</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> TipoContato.<span style="color: #006633;">CELULAR</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            SMSUtil.<span style="color: #006633;">enviarTorpedo</span><span style="color: #009900;">&#40;</span>contato.<span style="color: #006633;">getValor</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>, texto<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #000000; font-weight: bold;">break</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>O fato de a sua linguagem do coração não ter suporte nativo a enums não deve ser um impedimento para que você deixe de usufruir de suas vantagens. <a href="http://www.ditchnet.org/wp/2005/05/08/typesafe-enum-pattern-in-python/">Existem formas</a> de se conseguir uma funcionalidade semelhante sem muito esforço.</p>
<h3>Conclusão</h3>
<p>Desenvolver software é, antes de tudo, fazer escolhas. É o custo/benefício, os prós e contras antes de se tomar uma decisão. A boa notícia é que o nosso produto, o software, é maleável o suficiente para permitir que decisões ruins sejam re-feitas sempre que elas resultam em problemas como o descrito neste texto.</p>
<p>Bom carnaval!</p>
]]></content:encoded>
			<wfw:commentRss>http://weblog.destaquenet.com/2009/02/18/por-favor-usem-typesafe-enums/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>VPN PPTP no Ubuntu Intrepid</title>
		<link>http://weblog.destaquenet.com/2008/12/02/vpn-pptp-no-ubuntu-intrepid/</link>
		<comments>http://weblog.destaquenet.com/2008/12/02/vpn-pptp-no-ubuntu-intrepid/#comments</comments>
		<pubDate>Wed, 03 Dec 2008 02:51:35 +0000</pubDate>
		<dc:creator>Daniel Martins</dc:creator>
				<category><![CDATA[Português]]></category>
		<category><![CDATA[Tutoriais]]></category>
		<category><![CDATA[conectividade]]></category>
		<category><![CDATA[configuração]]></category>
		<category><![CDATA[dicas]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[network manager]]></category>
		<category><![CDATA[pptp]]></category>
		<category><![CDATA[rede]]></category>
		<category><![CDATA[ubuntu]]></category>
		<category><![CDATA[vpn]]></category>

		<guid isPermaLink="false">http://weblog.destaquenet.com/?p=304</guid>
		<description><![CDATA[Um dia após o lançamento do Intrepid (versão 8.10) do Ubuntu Linux, eu já tinha feito o download e a instalação na minha máquina de trabalho, um notebook Dell. Vou poupar elogios, até porque eu utilizo o Ubuntu diariamente desde &#8230; <a href="http://weblog.destaquenet.com/2008/12/02/vpn-pptp-no-ubuntu-intrepid/">Continue lendo <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Um dia após o lançamento do Intrepid (versão 8.10) do <a href="http://www.ubuntu.com/">Ubuntu Linux</a>, eu já tinha feito o <em>download</em> e a instalação na minha máquina de trabalho, um notebook Dell. Vou poupar elogios, até porque eu utilizo o Ubuntu diariamente desde o lançamento da release Dapper Drake (versão 6.06), de Julho de 2006. De lá para cá, muito trabalho vem sendo feito para tornar o Ubuntu o sistema ideal para uso em desktops e laptops.</p>
<p>Um dos aspectos que se encontram em constante evolução é a parte de conectividade. O <a href="http://projects.gnome.org/NetworkManager/">Network Manager</a>, gerenciador de conexões utilizado nas versões mais recentes do Ubuntu, torna simples a tarefa de se conectar com praticamente qualquer tipo de rede, inclusive com as recém chegadas redes 3G.</p>
<p>Apesar de facilitar a vida na maioria dos casos, o Network Manager presente na última versão do Ubuntu parece não funcionar do jeito que deveria, principalmente quando tentamos conectar numa VPN PPTP (Point-to-Point Tunneling Protocol). Na versão anterior do Ubuntu, eu conseguia conectar em VPNs PPTP sem problemas, coisa que deixou de acontecer após a instalação da última versão do Ubuntu.</p>
<p>A boa notícia é que, depois de perder algumas horas pesquisando, eu consegui resolver o meu problema. E é isso que mostrarei a seguir! <span id="more-304"></span></p>
<h3>Atualizando o Network Manager</h3>
<p>A primeira coisa que fiz foi tentar substituir a versão pré-instalada do Network Manager pela <a href="http://launchpad.net/network-manager">versão distribuída</a> pelos próprios desenvolvedores.</p>
<p>Para isso, abra o Synaptic e selecione o menu <em>Settings &#8211; Repositories</em>. Em seguida, selecione a aba <em>Third-Party Software</em> e clique no botão <em>Add</em>. Finalmente, cole a linha abaixo e confirme:</p>
<pre>deb http://ppa.launchpad.net/network-manager/ubuntu intrepid main</pre>
<p>Feche a tela e pressione CTRL+R para baixar as informações atualizadas dos repositórios. Após o término, pressione CTRL+G para iniciar o processo de atualização dos pacotes. Quando a instalação terminar, o Ubuntu irá pedir que você faça o reboot. Obedeça. <img src='http://weblog.destaquenet.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<h3>Configurando a VPN</h3>
<p>Após o reboot, o sistema já deve estar funcionando com a nova versão do Network Manager. Clique no Applet do Network Manager e selecione o menu <em>VPN Connections &#8211; Configure VPN</em>. Aqui, basta configurar de acordo com as instruções que você deve ter recebido do pessoal responsável pela parte de redes da sua empresa.</p>
<p>Para não terminar o post aqui, eu vou mostrar como eu fiz para configurar a conexão VPN que estou utilizando atualmente. É obvio que cada empresa configura sua VPN de um jeito diferente, por isso o que segue abaixo é apenas um exemplo de configuração.</p>
<h4>Informações básicas</h4>
<p>Na primeira tela você deve informar os dados para conexão com a VPN, como IP do servidor da VPN, usuário e senha:</p>
<div id="attachment_308" class="wp-caption aligncenter" style="width: 230px"><a href="http://weblog.destaquenet.com/wp-content/uploads/2008/12/vpn01.png"><img class="size-medium wp-image-308" title="Informações básicas da VPN" src="http://weblog.destaquenet.com/wp-content/uploads/2008/12/vpn01-220x300.png" alt="Informações básicas" width="220" height="300" /></a><p class="wp-caption-text">Informações básicas</p></div>
<p>Clique no botão <em>Advanced</em> para configurar em detalhes como deverá ser a comunicação entre seu computador e a VPN:</p>
<div id="attachment_309" class="wp-caption aligncenter" style="width: 239px"><a href="http://weblog.destaquenet.com/wp-content/uploads/2008/12/vpn02.png"><img class="size-medium wp-image-309" title="Opções avançadas" src="http://weblog.destaquenet.com/wp-content/uploads/2008/12/vpn02-229x300.png" alt="Opções avançadas" width="229" height="300" /></a><p class="wp-caption-text">Opções avançadas</p></div>
<p>No meu caso, o servidor da VPN foi configurado para fazer autenticação via CHAP; marque ou desmarque as opções disponíveis na tela, se necessário.</p>
<h4>Configurando as rotas</h4>
<p>Prosseguindo, na aba <em>IPv4 Settings</em> você poderá configurar o seu IP na VPN. No meu caso, o IP local, juntamente com o servidor DNS e Gateway, são fornecidos automaticamente pelo servidor. Por isso eu optei por manter a opção <em>Automatic (VPN)</em> no campo <em>Method</em>.</p>
<p>Neste ponto você já deve ter a VPN funcionando, mas com um pequeno inconveniente: pelo menos aqui, a VPN passou a ser acessível através da rota padrão (IP 0.0.0.0). Isso significa que <strong>todo</strong> o tráfego é direcionado para a VPN!</p>
<p>Para solucionar esta questão, é preciso configurar uma rota através da qual a VPN possa ser acessada, mantendo inalterada a rota padrão. Para isso, basta clicar no botão <em>Routes&#8230;</em> e configurar a(s) rota(s):</p>
<div id="attachment_316" class="wp-caption aligncenter" style="width: 310px"><a href="http://weblog.destaquenet.com/wp-content/uploads/2008/12/vpn03.png"><img class="size-medium wp-image-316" title="Declarando rotas" src="http://weblog.destaquenet.com/wp-content/uploads/2008/12/vpn03-300x166.png" alt="Declarando rotas" width="300" height="166" /></a><p class="wp-caption-text">Declarando rotas</p></div>
<p>Aqui, eu substituí as rotas obtidas automaticamente pelas rotas informadas na tabela. No meu caso, a faixa de IP da VPN é 192.168.0.X, por isso o <em>Prefix</em> foi configurado como 24 (<a href="http://linux-ip.net/html/routing-intro.html">aprenda aqui</a> o que esse número significa). O campo <em>Gateway</em> foi configurado para o &#8220;IP remoto virtual&#8221; da VPN, fazendo com que os pacotes destinados a IPs 192.168.0.X sejam redirecionados para a VPN. O campo <em>Metric</em> foi <a href="http://linux.about.com/od/lna_guide/a/gdelna23t04.htm">deixado como zero</a>.</p>
<h3>Feito!</h3>
<p>Isso deve bastar. Tente conectar com a VPN:</p>
<div id="attachment_313" class="wp-caption aligncenter" style="width: 310px"><a href="http://weblog.destaquenet.com/wp-content/uploads/2008/12/vpn04.png"><img class="size-medium wp-image-313" title="Conectando na VPN" src="http://weblog.destaquenet.com/wp-content/uploads/2008/12/vpn04-300x150.png" alt="Conectando na VPN" width="300" height="150" /></a><p class="wp-caption-text">Conectando na VPN</p></div>
<p>Não conectou? Confira os dados de conexão e modifique os parâmetros avançados de conexão (métodos de autentiação, encriptação, etc). Tente encontrar o motivo nos logs <code>/var/log/messages</code> e <code>/var/log/syslog</code>. Conectou? Ótimo, agora faça o seguinte teste:</p>
<ol>
<li>Dispare um ping contra algum computador da VPN;</li>
<li>Dispare um ping contra algum computador da sua rede local;</li>
<li>Dispare um ping contra algum computador da Internet (ex: google.com);</li>
</ol>
<p>O teste falhou? Com certeza tem algo errado com a(s) sua(s) rota(s). Ainda conectado na VPN, utilize os comandos <code>ip route</code> e/ou <code>route -n</code> para ver como ficaram as rotas; faça os ajustes necessários, reze e tente novamente. O teste foi um sucesso? Parabéns, você conseguiu! <img src='http://weblog.destaquenet.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p><strong>Update</strong>: Aos usuários do Fedora 10, o procedimento de configuração é praticamente o mesmo. O que muda é a configuração das rotas:</p>
<p style="text-align: center;">
<div id="attachment_343" class="wp-caption aligncenter" style="width: 310px"><img class="size-medium wp-image-343" title="rotas_fedora10" src="http://weblog.destaquenet.com/wp-content/uploads/2008/12/rotas_fedora10-300x182.png" alt="Declarando as rotas (Fedora 10)" width="300" height="182" /><p class="wp-caption-text">Declarando rotas (Fedora 10)</p></div>
]]></content:encoded>
			<wfw:commentRss>http://weblog.destaquenet.com/2008/12/02/vpn-pptp-no-ubuntu-intrepid/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Deployment de aplicações Python com Fabric</title>
		<link>http://weblog.destaquenet.com/2008/10/05/deployment-de-aplicacoes-python-com-fabric/</link>
		<comments>http://weblog.destaquenet.com/2008/10/05/deployment-de-aplicacoes-python-com-fabric/#comments</comments>
		<pubDate>Sun, 05 Oct 2008 10:00:05 +0000</pubDate>
		<dc:creator>Daniel Martins</dc:creator>
				<category><![CDATA[Português]]></category>
		<category><![CDATA[Programação]]></category>
		<category><![CDATA[Tutoriais]]></category>
		<category><![CDATA[capistrano]]></category>
		<category><![CDATA[deployment]]></category>
		<category><![CDATA[dicas]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[fabric]]></category>
		<category><![CDATA[ferramenta]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://weblog.destaquenet.com/?p=101</guid>
		<description><![CDATA[Uma coisa é fato: subir uma nova versão de uma aplicação é um procedimento que, se feito manualmente, costuma demandar tempo precioso, independente do tamanho da aplicação e dos frameworks utilizados no projeto. Não há o que discutir. Na verdade, &#8230; <a href="http://weblog.destaquenet.com/2008/10/05/deployment-de-aplicacoes-python-com-fabric/">Continue lendo <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Uma coisa é fato: subir uma nova versão de uma aplicação é um procedimento que, se feito manualmente, costuma demandar tempo precioso, independente do tamanho da aplicação e dos <em>frameworks</em> utilizados no projeto. Não há o que discutir.</p>
<p>Na verdade, ninguém parecia se importar em automatizar tarefas &#8220;mecânicas&#8221; como o <em>deployment</em>, coisa que mudou de figura ao passo que <a href="http://en.wikipedia.org/wiki/Agile_software_development">metodologias ágeis</a> &#8212; juntamente com técnicas como <a href="http://martinfowler.com/articles/continuousIntegration.html">Integração Contínua</a> &#8212; passaram a se tornar populares. Desde então, algum esforço vem sendo investido para criar ferramentas para automatização de muitas dessas tarefas.</p>
<p><strong>Atualização:</strong> Trechos de código adaptados para as versões mais recentes do Fabric (22/06/2010).</p>
<p><span id="more-101"></span></p>
<h3>Fabric: ferramenta Python para deployment</h3>
<p>Apesar de já ter utilizado algumas dessas <a href="http://maven.apache.org/">ferramentas</a> no desenvolvimento de projetos em <a href="http://java.sun.com/">Java</a>, plataforma na qual eu possuo uma experiência maior, eu ainda não conhecia nenhuma ferramenta em <a href="http://python.org/">Python</a> que me permitisse automatizar o <em>deployment</em> de uma aplicação.</p>
<p>Como eu estava começando a ficar de saco cheio de ter que atualizar os arquivos de uma aplicação <a href="http://djangoproject.org/">Django</a> via SCP, eu resolvi que já era hora de ir atrás de alguma ferramenta que me ajudasse a automatizar esse processo. Finalmente, depois de muita procura, eu encontrei <a href="http://www.nongnu.org/fab/index.html">Fabric</a>, uma ferramenta mais ou menos parecida com um <a href="http://www.capify.org/">Capistrano</a> &#8220;capado&#8221;.</p>
<p>A instalação do Fabric é bem simples para quem costuma usar o <a href="http://peak.telecommunity.com/DevCenter/EasyInstall">easy_install</a>:</p>
<pre>$ sudo easy_install Fabric</pre>
<p>Basta executar esse comando para que os fontes da última versão do Fabric (0.0.9 no momento da publicação deste texto) sejam baixados e instalados.</p>
<p><strong>Atenção</strong>: Quem utiliza máquinas Windows, provavelmente precisará do <a href="http://www.cygwin.com/">Cygwin</a>, já que o Fabric assume o uso de ambientes Unix.</p>
<h3>Fabric a 3.000 pés</h3>
<p>Neste momento você já deve conseguir executar o comando <code>fab</code> no terminal. Se tudo correu bem até aqui, estamos prontos continuar.</p>
<p>Vamos começar criando o arquivo <code>fabfile.py</code> no diretório-raíz da aplicação. Coloque o seguinte conteúdo neste arquivo:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> fabric.<span style="color: black;">api</span> <span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #66cc66;">*</span>
&nbsp;
env.<span style="color: black;">project</span> = <span style="color: #483d8b;">'project_name'</span>,
env.<span style="color: black;">package</span> = <span style="color: #483d8b;">'$(project).zip'</span>,
&nbsp;
<span style="color: #808080; font-style: italic;"># Remote servers</span>
env.<span style="color: #dc143c;">user</span>  = <span style="color: #483d8b;">'usuario_ssh'</span>,
env.<span style="color: black;">hosts</span> = <span style="color: black;">&#91;</span><span style="color: #483d8b;">'servidor1.com'</span>, <span style="color: #483d8b;">'servidor2.com'</span><span style="color: black;">&#93;</span>,
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> deploy<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;Deploys the application to the production servers.&quot;</span>
    build<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> build<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;Builds the application.&quot;</span>
    prepare<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> prepare<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;Prepares the build directory.&quot;</span>
    clean<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> clean<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;Removes the build directory.&quot;</span>
    <span style="color: #ff7700;font-weight:bold;">pass</span></pre></div></div>

<p>Cada método definido no arquivo é equivalente a uma <em>task</em> do Capistrano. No início temos as variáveis <code>env.*</code> necessárias para o <em>script</em>. Destaque para a variável <code>env.hosts</code>, que indica os servidores nos quais os comandos remotos devem ser executados.</p>
<p>Ok, tente executar o comando <code>fab -l</code> no terminal, no mesmo diretório onde se encontra o arquivo <code>fabfile.py</code>. Se tudo correr bem, você receberá a seguinte saída:</p>
<pre>
$  fab -l
Available commands:
   build      Build the application.
   clean      Remove the build directory.
   deploy     Deploy the application to the production servers.
   help       Display Fabric usage help, or help for a given command.
   license    Display the Fabric distribution license text.
   list       Display a list of commands with descriptions.
   prepare    Prepare the build directory.
   set        Set a Fabric variable.
   shell      Start an interactive shell connection to the specified hosts.
   warranty   Display warranty information for the Fabric software.
</pre>
<p>Veja que as <em>tasks</em> que criamos estão disponíveis para serem executadas via linha de comando. Para executar as <em>tasks</em> <code>clean</code> e <code>deploy</code>, por exemplo, utilizaríamos o seguinte comando:</p>
<pre>$  fab clean deploy</pre>
<p>Simples, não é? Antes de vermos um exemplo de script completo, vamos conhecer os três principais métodos utilizados em <em>scripts</em> Fabric: <code>local</code>, <code>run</code> e <code>put</code>:</p>
<ul>
<li><code>local</code>: Serve para executar comandos na máquina local. Ex: <code>local('mkdir build')</code>;</li>
<li><code>run</code>: Igual ao <code>local</code>, mas executa os comandos nas máquinas remotas (indicadas na variável <code>env.hosts</code>);</li>
<li><code>put</code>: Envia um arquivo para as máquinas remotas. Ex: <code>put('arquivo.zip', '/home/user/app.zip')</code>.</li>
</ul>
<h3>Finalmente, o script completo!</h3>
<p>Antes de mostrar o <em>script</em> que eu venho utilizando para fazer o deployment <a href="http://www.destaquenet.com/">de uma aplicação</a> Django, veja como seus arquivos e diretórios estão organizados: (isso deve ajudar a entender melhor o <em>script</em>)</p>
<div id="attachment_131" class="wp-caption aligncenter" style="width: 214px"><a href="http://weblog.destaquenet.com/wp-content/uploads/2008/10/destaqueweb_structure.png"><img src="http://weblog.destaquenet.com/wp-content/uploads/2008/10/destaqueweb_structure-204x300.png" alt="Estrutura do projeto" title="Estrutura do projeto" width="204" height="300" class="size-medium wp-image-131" /></a><p class="wp-caption-text">Estrutura do projeto</p></div>
<p>E, finalmente, o <em>script</em>:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#!/usr/bin/env python</span>
<span style="color: #808080; font-style: italic;"># -*- coding: utf-8 -*-</span>
&nbsp;
<span style="color: #483d8b;">&quot;&quot;&quot;Build script.
&quot;&quot;&quot;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">from</span> fabric.<span style="color: black;">api</span> <span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #66cc66;">*</span>
&nbsp;
&nbsp;
env.<span style="color: black;">project</span> = <span style="color: #483d8b;">'destaqueweb'</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Build output directory</span>
env.<span style="color: black;">build_path</span> = <span style="color: #483d8b;">'build'</span>
env.<span style="color: black;">package</span>    = <span style="color: #483d8b;">'%s.zip'</span> <span style="color: #66cc66;">%</span> env.<span style="color: black;">project</span>
env.<span style="color: black;">local_path</span> = <span style="color: #483d8b;">'%s/%s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>env.<span style="color: black;">build_path</span>, env.<span style="color: black;">package</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Media</span>
env.<span style="color: black;">media_src</span>   = <span style="color: #483d8b;">'media'</span>
env.<span style="color: black;">media_root</span>  = <span style="color: #483d8b;">'public_html'</span>
env.<span style="color: black;">media_path</span>  = <span style="color: #483d8b;">'%s/%s/%s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>env.<span style="color: black;">media_root</span>, env.<span style="color: black;">project</span>, env.<span style="color: black;">media_src</span><span style="color: black;">&#41;</span>
env.<span style="color: black;">media_build</span> = <span style="color: #483d8b;">'%s/%s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>env.<span style="color: black;">build_path</span>, env.<span style="color: black;">media_path</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Application code</span>
env.<span style="color: black;">app_root</span>  = <span style="color: #483d8b;">'wsgi_apps'</span>
env.<span style="color: black;">app_path</span>  = <span style="color: #483d8b;">'%s/%s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>env.<span style="color: black;">app_root</span>, env.<span style="color: black;">project</span><span style="color: black;">&#41;</span>
env.<span style="color: black;">app_build</span> = <span style="color: #483d8b;">'%s/%s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>env.<span style="color: black;">build_path</span>, env.<span style="color: black;">app_path</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Remote servers</span>
env.<span style="color: black;">hosts</span>       = <span style="color: black;">&#91;</span><span style="color: #483d8b;">'destaquenet.com'</span><span style="color: black;">&#93;</span>
env.<span style="color: black;">destination</span> = <span style="color: #483d8b;">'/home/destaquenet'</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> clean<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;Removes the build directory.
    &quot;&quot;&quot;</span>
    local<span style="color: black;">&#40;</span><span style="color: #483d8b;">'rm -fR %s'</span> <span style="color: #66cc66;">%</span> env.<span style="color: black;">build_path</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> prepare<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;Prepares the build directory.
    &quot;&quot;&quot;</span>
    clean<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    local<span style="color: black;">&#40;</span><span style="color: #483d8b;">'mkdir -p %s'</span> <span style="color: #66cc66;">%</span> env.<span style="color: black;">media_build</span><span style="color: black;">&#41;</span>
    local<span style="color: black;">&#40;</span><span style="color: #483d8b;">'mkdir -p %s'</span> <span style="color: #66cc66;">%</span> env.<span style="color: black;">app_build</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> build<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;Builds the application.
    &quot;&quot;&quot;</span>
    local<span style="color: black;">&#40;</span><span style="color: #483d8b;">'cp -R %s/* %s/%s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>env.<span style="color: black;">project</span>, env.<span style="color: black;">build_path</span>, env.<span style="color: black;">app_path</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    local<span style="color: black;">&#40;</span><span style="color: #483d8b;">'cp -R %s/* %s/%s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>env.<span style="color: black;">media_src</span>, env.<span style="color: black;">build_path</span>, env.<span style="color: black;">media_path</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    local<span style="color: black;">&#40;</span><span style="color: #483d8b;">'find %s -name *.pyc -delete'</span> <span style="color: #66cc66;">%</span> env.<span style="color: black;">build_path</span><span style="color: black;">&#41;</span>
    local<span style="color: black;">&#40;</span><span style="color: #483d8b;">'find %s -path *upload* -delete'</span> <span style="color: #66cc66;">%</span> env.<span style="color: black;">build_path</span><span style="color: black;">&#41;</span>
    local<span style="color: black;">&#40;</span><span style="color: #483d8b;">'find %s -path *fixtures* -delete'</span> <span style="color: #66cc66;">%</span> env.<span style="color: black;">build_path</span><span style="color: black;">&#41;</span>
    local<span style="color: black;">&#40;</span><span style="color: #483d8b;">'cd %s;zip -r %s %s %s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>env.<span style="color: black;">build_path</span>, env.<span style="color: black;">package</span>, env.<span style="color: black;">media_root</span>, env.<span style="color: black;">app_root</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> reload_app<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;Reloads the app.
    &quot;&quot;&quot;</span>
    run<span style="color: black;">&#40;</span><span style="color: #483d8b;">'touch %s/%s/%s/index.wsgi'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>env.<span style="color: black;">destination</span>, env.<span style="color: black;">media_root</span>, env.<span style="color: black;">project</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    run<span style="color: black;">&#40;</span><span style="color: #483d8b;">'python %s/%s/manage.py ping_google /sitemap.xml'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>env.<span style="color: black;">destination</span>, env.<span style="color: black;">app_path</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> deploy<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;Deploys the application to the production server.
    &quot;&quot;&quot;</span>
    build<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    put<span style="color: black;">&#40;</span>env.<span style="color: black;">local_path</span>, env.<span style="color: black;">destination</span><span style="color: black;">&#41;</span>
    run<span style="color: black;">&#40;</span><span style="color: #483d8b;">'cd %s; unzip -uo %s; rm %s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>env.<span style="color: black;">destination</span>, env.<span style="color: black;">package</span>, env.<span style="color: black;">package</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    reload_app<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Este código deve ser facilmente entendido por quem tem trabalha com uma certa facilidade no terminal.</p>
<p>Basicamente o que o código faz é organizar os arquivos da aplicação para que estes fiquem dispostos da mesma forma utilizada pelo servidor de produção, enviar os arquivos dentro de um pacote ZIP para os servidores e descompactá-los no local correto, além de executar um <code>touch</code> no <em>script</em> <acronym title="Web Server Gateway Interface">WSGI</acronym> para que o <a href="http://apache.org/">Apache</a> recarregue a aplicação.</p>
<p>Tudo pronto. Agora basta executar <code>fab deploy</code> no terminal para subir a aplicação para o(s) servidor(es) desejado(s).</p>
<h3>Fabric e ambiente de homologação</h3>
<p>Uma coisa que vem se tornando comum é o uso de diferentes ambientes onde uma aplicação costuma ser implantada (ex: ambiente de homologação). Se sua empresa costuma trabalhar dessa forma, o Fabric ainda pode te ajudar.</p>
<p>Por exemplo, vamos supor que sua empresa possua um servidor de produção e um servidor de homologação. Podemos, então, adequar o <em>script</em> para que ele possa trabalhar com ambos os ambientes:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># ...</span>
env.<span style="color: #dc143c;">user</span>  = <span style="color: #483d8b;">'production'</span>,
env.<span style="color: black;">hosts</span> = <span style="color: black;">&#91;</span><span style="color: #483d8b;">'app.com'</span><span style="color: black;">&#93;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> staging<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #808080; font-style: italic;"># deploy to staging servers</span>
    env.<span style="color: #dc143c;">user</span>  = <span style="color: #483d8b;">'staging'</span>
    env.<span style="color: black;">hosts</span> = <span style="color: black;">&#91;</span><span style="color: #483d8b;">'staging.app.com'</span><span style="color: black;">&#93;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># other tasks...</span></pre></div></div>

<p>Fazendo as mudanças mostradas acima, para executar o <em>deployment</em> no ambiente de homologação basta rodar o comando <code>fab staging deploy</code>; o deployment em ambiente de produção continua disponível através do comando <code>fab deploy</code>.</p>
<p>Isso é possível pois podemos substituir o valor de uma variável (ou configurar novas variáveis) a qualquer momento. Então, quando executamos o comando <code>fab staging deploy</code>, o Fabric executa a <em>task</em> <code>staging</code>, substituindo o valor das variáveis <code>env.user</code> e <code>env.hosts</code>, fazendo com que os comandos contidos na <em>task</em> <code>deploy</code> sejam executados nos servidores de homologação em vez de nos servidores de produção.</p>
]]></content:encoded>
			<wfw:commentRss>http://weblog.destaquenet.com/2008/10/05/deployment-de-aplicacoes-python-com-fabric/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>6 lições que aprendi sobre desenvolvimento de softwares</title>
		<link>http://weblog.destaquenet.com/2008/09/28/6-licoes-que-aprendi-sobre-desenvolvimento-de-softwares/</link>
		<comments>http://weblog.destaquenet.com/2008/09/28/6-licoes-que-aprendi-sobre-desenvolvimento-de-softwares/#comments</comments>
		<pubDate>Sun, 28 Sep 2008 15:30:20 +0000</pubDate>
		<dc:creator>Daniel Martins</dc:creator>
				<category><![CDATA[Off Topic]]></category>
		<category><![CDATA[Português]]></category>
		<category><![CDATA[Programação]]></category>
		<category><![CDATA[dicas]]></category>
		<category><![CDATA[lições]]></category>
		<category><![CDATA[software]]></category>

		<guid isPermaLink="false">http://weblog.destaquenet.com/?p=60</guid>
		<description><![CDATA[É natural do ser humano aprender com os seus próprios erros. Quem aqui nunca vivenciou uma situação inusitada na qual mesmo tendo a impressão de que algo está errado, acabamos ignorando e seguindo em frente. E assim continua até que, &#8230; <a href="http://weblog.destaquenet.com/2008/09/28/6-licoes-que-aprendi-sobre-desenvolvimento-de-softwares/">Continue lendo <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>É natural do ser humano aprender com os seus próprios erros. Quem aqui nunca vivenciou uma situação inusitada na qual mesmo tendo a impressão de que algo está errado, acabamos ignorando e seguindo em frente. E assim continua até que, num belo dia, a bomba explode e você pensa consigo mesmo: &#8220;<strong>eu sabia</strong>!&#8221;.</p>
<p>Essas situações, embora aparentam ser ruins em um primeiro momento, são excelentes oportunidades para nos ensinar algumas coisas que provavelmente não aprenderíamos de outra forma. Aliás, talvez seja até mais fácil aprender com nossos próprios erros do que ler sobre isso em algum livro ou artigo. O problema é que nem sempre é fácil saber tirar algo de bom de situações problemáticas.</p>
<p>Por isso, tentarei fazer um gancho entre esse assunto e a área de desenvolvimento de <em>softwares</em>, mostrando algumas das lições em que aprendi errando. Também seria interessante se os leitores que se identificarem com o assunto também compartilhassem algumas lições que aprenderam em períodos turbulentos. Afinal, errar <strong>faz bem</strong>! <span id="more-60"></span></p>
<h3>Lição número 1: confie no código e não no documento</h3>
<p>Deixe-me contar uma experiência interessante que tive na faculdade. Certa vez, um de nossos professores chegou na aula, colocou no projetor um Diagrama de Classes e um Modelo Entidade-Relacionamento e disse para a sala:</p>
<blockquote><p>
Esse é o modelo de um sistema de locadora. Nesses diagramas vocês tem tudo o que é preciso pra fazer isso funcionar. Não quero nem saber se vocês não gostam do cara que está sentado do seu lado, se acha que ele tem chulé ou mau hálito; eu quero que vocês aí se organizem e me entreguem isso funcionando até o final da aula.
</p></blockquote>
<p>Enfim, todo mundo se levantou e começou a andar de um lado para o outro e, alguns momentos depois, tínhamos nos organizado para dar início a programação. Pois bem&#8230; depois de algum tempo programando o que estava nos diagramas, o professor se volta para a sala e diz, em tom de sarcasmo:</p>
<blockquote><p>
Vem cá, vocês não encontraram nada estranho, não?
</p></blockquote>
<p>Praticamente todo mundo da sala havia percebido que alguns relacionamentos estavam faltando, outros sobrando, outros com a multiplicidade incorreta, entre outras coisas. Ninguém só não havia dito nada antes por pensar que o diagrama era <strong>indefectível</strong>. Se isso aconteceu em um &#8220;projeto de mentira&#8221; com duas dúzias de pessoas, quem imagina o que pode acontecer em projetos com dezenas e dezenas de pessoas envolvidas?</p>
<p>O fato é que os erros foram colocados lá propositalmente e isso acabou me fazendo perceber que nem sempre um documento representa fielmente um sistema. O que <strong>sempre</strong> representa fielmente um sistema é o seu <strong>código-fonte</strong>!</p>
<p>Durante um tempo, trabalhei numa empresa onde praticamente tudo o que precisava ser desenvolvido era entregue aos programadores na forma de textos (para fins de documentação, diziam). Para você perceber a gravidade da situação, se eu implementasse exatamente o que estava escrito nas folhas que recebia, a chance do sistema sequer compilar era altíssima. Além do mais, as especificações normalmente vinham incompletas, o que fazia com que nós tivéssemos de nos preocupar não em simplesmente programar, mas também de tentar driblar possíveis falhas na especificação.</p>
<p>Eu confesso que, naquele tempo, a minha vontade era dar um tiro na minha própria cabeça. Mas hoje, olhando para trás, percebo que isso fez com que eu adquirisse algumas habilidades que não seriam desenvolvidas se as especificações chegassem perfeitas em minhas mãos.</p>
<h3>Lição número 2: escreva testes automatizados</h3>
<p>Novamente remetendo aos tempos da faculdade, eu não poderia ter escolhido um tema melhor para o meu <acronym title="Trabalho de Conclusão de Curso">TCC</acronym> do que o desenvolvimento de um <em>framework</em>. Me lembro que fui alertado dezenas de vezes pelos professores de que tratava-se de algo inusitado e que eu não teria direito de reclamar caso algo saísse errado. Bem ameaçador, não acha?</p>
<p>Apesar de assumir o desafio <strong>sozinho</strong> &#8211; pois a faculdade não dispunha de um orientador com experiência em <em>design</em> de <em>frameworks</em> &#8211; tudo correu bem nas primeiras semanas de desenvolvimento, afinal o código estava bonito e funcionava bem. O que mais eu poderia querer? Entretanto, um sério (e óbvio) problema foi se instalando sem que eu percebesse: <strong>a ausência de testes automatizados</strong>.</p>
<p>No começo, quando o <em>framework</em> possuía apenas algumas dezenas de classes &#8211; era fácil localizar e corrigir os problemas que apareciam. Mas, quanto mais o código crescia, menos controle eu tinha sobre ele. Fazer experimentações no código já não era mais seguro.</p>
<p>Nessa época eu já sabia como escrever testes e tudo mais. O meu grande equívoco foi pensar que, se poupasse o tempo utilizado na escrita dos testes, eu teria mais tempo para entregar aquilo que prometi no começo do ano. Obviamente, eu não poderia estar mais <strong>errado</strong>.</p>
<p>Escrever testes automatizados (e até mesmo levá-los a outro nível, como <a href="http://weblog.destaquenet.com/2008/09/18/test-driven-development-com-java-swing/">acontece</a> com o <a href="http://en.wikipedia.org/wiki/Test-driven_development"><acronym title="Test Driven Development">TDD</acronym></a>) é <strong>essencial</strong> para que as aplicações evoluam de forma consistente. Sem a segurança que tais testes proporcionam, é impossível garantir que o que foi feito hoje continue funcionando amanhã. Não testar o código é comparável a um <a href="http://en.wikipedia.org/wiki/Monopoly_%28game%29#Board">jogo de tabuleiros</a>, onde, num momento de azar, você acaba caindo numa casa que diz &#8220;volte à primeira casa&#8221;.</p>
<p>A grande lição que eu tirei disso foi aprender que os testes não servem unicamente para comprovar que tudo está funcionando (pois isso eu já sabia). Eu aprendi que deixar de escrever testes alegando falta de tempo é <strong>burrice</strong>. Falando nisso, ainda encontramos pessoas que dizem que testes são ruins porque &#8220;é mais código para dar manutenção&#8221;, ou &#8220;escrever testes consome tempo que não temos&#8221; ou ainda &#8220;mas quem vai testar os testes?&#8221;. Não irei responder a essas perguntas, mas posso dizer que são poucas as coisas que fazem tão bem a um <em>software</em> quanto a criação de testes automatizados.</p>
<h3>Lição número 3: desenvolva <em>software</em> para o cliente, não para a infraestrutura</h3>
<p>Algumas pessoas, ao se iniciar um novo projeto, já tenta desde o início juntar e configurar o <em>framework</em> de injeção de dependências, o <em>framework</em> web, o <em>framework</em> <acronym title="Object-Relational Mapping">ORM</acronym>, e por aí vai. A minha pergunta é: <strong>pra quê</strong>? Confesso que eu mesmo já fiz isso, mas devo dizer que trabalhar dessa forma não faz muito sentido na maioria dos casos.</p>
<p>Voltando aos tempos de faculdade, uma outra coisa que eu gostava era que nós montávamos os sistemas sem pensar em bancos de dados, latência da rede, essas coisas. Nosso banco de dados era nada mais que algumas instâncias de <code>LinkedHashSet</code>! Então, considerando que algumas <strong>boas práticas</strong> foram usadas e que  o sistema esteja funcionando em memória, trocar o mecanismo de persistência (ou lidar com qualquer outra questão de infraestrutura) era fácil.</p>
<p>Por exemplo, em um sistema de locadora, o caso de uso <strong>Alugar Filme</strong> não fará nada de diferente caso estejamos persistindo os objetos em um banco de dados em vez da memória. Da mesma forma, o caso de uso <strong>Reservar Filme</strong> (que envia uma mensagem para o usuário informando quando o filme está disponível) não mudará se trocarmos a implementação atual &#8211; que executa&#8230;</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #003399;">System</span>.<span style="color: #006633;">out</span>.<span style="color: #006633;">println</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;Filme disponível&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>&#8230;para uma outra que utiliza a <acronym title="Application Programming Interface">API</acronym> <a href="http://java.sun.com/products/javamail/">JavaMail</a> para enviar uma mensagem via <acronym title="Simple Mail Transfer Protocol">SMTP</acronym>. O que importa é que os objetos estão sendo armazenados em algum lugar e que o usuário é notificado de algum jeito quando o filme que ele reservou está disponível. Acredite, isso é o bastante durante boa parte do desenvolvimento de uma aplicação.</p>
<h3>Lição número 4: seja preguiçoso</h3>
<p>Uma coisa interessante que aprendi foi que nem todo programador preguiçoso é um bom programador, mas <strong>todo bom programador é preguiçoso</strong>!</p>
<p>Os bons desenvolvedores possuem um faro aguçado que os permite identificar situações nas quais soluções melhores podem ser adotadas de modo a diminuir esforços, tornando o código (e a aplicação de um modo geral) mais conciso e organizado. Alguns chamam tais situações de <strong>cheiros</strong> (ou <em>smells</em>), termo este que &#8220;pegou&#8221; após a publicação do livro <a href="http://www.amazon.com/exec/obidos/ASIN/0201485672">Refactoring: Improving the Design of Existing Code</a>, de <a href="http://www.martinfowler.com/">Martin Fowler</a>.</p>
<p>Para identificar tais situações, vamos começar do básico: o que um desenvolvedor de <em>software</em> mais costuma odiar em seu trabalho? <strong>Trabalho repetitivo</strong>. Se você precisa fazer uma determinada tarefa e tal tarefa exige a repetição de procedimentos que você suspeita serem supérfluos, então faça um favor para si mesmo: deixe sua preguiça <strong>trabalhar por você</strong>!</p>
<p>Não sei se você é assim, mas eu costumo aprender as coisas mais rapidamente através de exemplos&#8230; então, vamos a eles.</p>
<p>Imagine que você tenha sido selecionado para trabalhar no <em>deployment</em> de uma aplicação. Se você já tentou implantar uma aplicação &#8220;no braço&#8221;, certamente sabe que, se você for fazer o trabalho manualmente, terá de fazer sempre as mesmas coisas, <strong>indefinidamente</strong>. O <em>deployment</em> de uma aplicação simples poderia ser exemplificada da seguinte forma:</p>
<ol>
<li>Organizar as classes compiladas em uma estrutura de diretórios específica;</li>
<li>Copiar bibliotecas que devem estar disponíveis a nível de servidor (como <em>drivers</em> JDBC) no diretório de bibliotecas do <em>Container</em>;</li>
<li>Disponibilizar as bibliotecas usadas pela aplicação em um diretório específico;</li>
<li>Mover os descritores e arquivos de configuração aos locais apropriados;</li>
<li>Gerar um <acronym title="Web ARchive">WAR</acronym> ou <acronym title="Enterprise ARchive">EAR</acronym>;</li>
<li>Implantar o WAR ou EAR no <em>Container</em>;</li>
<li>(Re)iniciar o <em>Container</em> (caso este não suporte <em>hot deployment</em>).</li>
</ol>
<p>Veja que fazer o <em>deployment</em> de uma aplicação não é assim tão complicado, mas perceba que você terá de fazer essas mesmas tarefas <strong>sempre</strong> que você precisar rodar a aplicação. Trata-se de algo completamente <strong>mecânico</strong> e <strong>chato</strong>.</p>
<p>Neste exemplo, você teria <strong>duas opções</strong>: continuar fazendo isso manualmente até o dia da sua morte, ou criar um <em>script</em> para automatizar essa tarefa para você. O que você prefere?</p>
<h3>Cuidado com os extremos!</h3>
<p>Uma coisa importante é não levar essa lição ao <strong>extremo</strong> (como qualquer outra coisa). Querer achar uma solução mais simples para <strong>todos</strong> os problemas nem sempre é a melhor saída. Já dizia <a href="http://en.wikipedia.org/wiki/Albert_einstein">Albert Einstein</a>:</p>
<blockquote><p>
Faça as coisas o mais simples que você puder, porém não se restrinja às mais simples.
</p></blockquote>
<h3>Lição número 5: não tente adivinhar o futuro</h3>
<p>Um dos meus professores de faculdade nos contou sobre uma experiência que ele havia feito com outra classe. Ele propôs que os alunos, em grupos, implementassem um <strong>sistema qualquer</strong>, cuja única restrição era, ao se partir para a fase de implementação, o código <strong>deveria</strong> refletir <strong>exatamente</strong> o modelo criado no início do processo, e este, por sua vez, não <strong>poderia mais ser alterado</strong>! Bem no estilo <a href="http://en.wikipedia.org/wiki/Waterfall_model">Waterfall</a> mesmo. Loucura, não? Embora a tarefa não parecesse difícil para os alunos em um primeiro momento, nem é preciso dizer o quanto estavam enganados em relação a isso.</p>
<p>Adivinhem o resultado da experiência? Sistemas que pareciam &#8220;faltar uma perna&#8221;. Quer dizer, todos os sistemas funcionavam, mas <strong>não resolviam os problemas que deveriam</strong>. O interessante é que todos os projetos acabaram <strong>falhando</strong>, apesar de a maioria desses alunos terem escolhido desenvolver algo que conheciam <strong>bem</strong> (por terem experiências profissionais e tudo mais).</p>
<p>Graças às metodologias ágeis, essa coisa de se tentar prever o futuro está perdendo a força. Acho que todo mundo já entendeu que isso não serve de nada, que mudanças são inevitáveis. O melhor a fazer, então, é saber reagir frente a elas.</p>
<h3>Lição 6: aprenda que sempre&#8230;</h3>
<p>&#8230; há algo a aprender.</p>
]]></content:encoded>
			<wfw:commentRss>http://weblog.destaquenet.com/2008/09/28/6-licoes-que-aprendi-sobre-desenvolvimento-de-softwares/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Debugando aplicações JavaEE no Glassfish v2</title>
		<link>http://weblog.destaquenet.com/2008/09/22/debugando-aplicacoes-javaee-no-glassfish-v2/</link>
		<comments>http://weblog.destaquenet.com/2008/09/22/debugando-aplicacoes-javaee-no-glassfish-v2/#comments</comments>
		<pubDate>Tue, 23 Sep 2008 01:51:40 +0000</pubDate>
		<dc:creator>Daniel Martins</dc:creator>
				<category><![CDATA[Português]]></category>
		<category><![CDATA[Programação]]></category>
		<category><![CDATA[Tutoriais]]></category>
		<category><![CDATA[debugging]]></category>
		<category><![CDATA[dicas]]></category>
		<category><![CDATA[glassfish]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[javaee]]></category>
		<category><![CDATA[netbeans]]></category>

		<guid isPermaLink="false">http://weblog.destaquenet.com/?p=50</guid>
		<description><![CDATA[Este post, na verdade, é uma dica rápida para quem possui aplicações JavaEE rodando em servidores Glassfish e está tendo dificuldades na hora de rastrear a origem de possíveis bugs no código. Habilitando o modo de debug Existem duas maneiras &#8230; <a href="http://weblog.destaquenet.com/2008/09/22/debugando-aplicacoes-javaee-no-glassfish-v2/">Continue lendo <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Este <em>post</em>, na verdade, é uma dica rápida para quem possui aplicações <a href="http://java.sun.com/javaee/">JavaEE</a> rodando em servidores <a href="http://glassfish.dev.java.net/">Glassfish</a> e está tendo dificuldades na hora de rastrear a origem de possíveis <em>bugs</em> no código. <span id="more-50"></span></p>
<h3>Habilitando o modo de debug</h3>
<p>Existem duas maneiras de se habilitar o modo de <em>debug</em> no Glassfish: através do painel de administração ou durante a inicialização do domínio.</p>
<p>Para habilitar o modo de <em>debug</em> por padrão, através do painel de administração, basta marcar o campo <em>Debug</em> na tela <em>JVM Settings</em> e clicar em <em>Save</em>:</p>
<div id="attachment_51" class="wp-caption aligncenter" style="width: 310px"><a href="http://weblog.destaquenet.com/wp-content/uploads/2008/09/debug_mode.png"><img src="http://weblog.destaquenet.com/wp-content/uploads/2008/09/debug_mode-300x254.png" alt="Habilitando o modo de Debug" title="Modo de Debug" width="300" height="254" class="size-medium wp-image-51" /></a><p class="wp-caption-text">Habilitando o modo de Debug</p></div>
<p>Basta reiniciar o domínio para ver que o servidor abre a porta <code>9009</code>, através da qual podemos conectar um <em>debugger</em>:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">asadmin start-domain domain1</pre></div></div>

<p>Eis um resumo da saída ecoada pelo Glassfish:</p>
<pre>
Starting Domain domain1, please wait.
Log redirected to .../server.log.
Redirecting output to .../server.log
Listening for transport dt_socket at address: 9009
Application server is listening at address 9009 for debugger to attach using transport dt_socket
...
</pre>
<p>A outra forma de se habilitar o modo de <em>debug</em> é fornecer o parâmetro <code>--debug</code> ao iniciar o domínio. Por exemplo:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">asadmin start-domain <span style="color: #660033;">--debug</span> domain1</pre></div></div>

<h3>Conectando o debugger da IDE ao servidor</h3>
<p>Agora, basta usar os recursos de <em>debugging</em> presentes na sua IDE Java para debugar o código. Por exemplo, se você usa o <a href="http://netbeans.org/">NetBeans</a> 5.5, basta acessar o menu <em>Run &#8211; Attach Debugger&#8230;</em> e o configurar da seguinte forma:</p>
<div id="attachment_52" class="wp-caption aligncenter" style="width: 310px"><a href="http://weblog.destaquenet.com/wp-content/uploads/2008/09/screenshot-attach.png"><img src="http://weblog.destaquenet.com/wp-content/uploads/2008/09/screenshot-attach-300x152.png" alt="Anexando o debugger do NetBeans" title="Anexando o debugger" width="300" height="152" class="size-medium wp-image-52" /></a><p class="wp-caption-text">Anexando o debugger do NetBeans</p></div>
<p>O procedimento é semelhante em outras IDEs Java.</p>
<p>Pronto! Feito isso, é só colocar os <em>breakpoints</em> e disparar a execução do código problemático.</p>
]]></content:encoded>
			<wfw:commentRss>http://weblog.destaquenet.com/2008/09/22/debugando-aplicacoes-javaee-no-glassfish-v2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

