<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7709637092684013725</id><updated>2012-02-03T14:28:37.451-03:00</updated><category term='ruby'/><category term='gnuplot'/><category term='flash'/><category term='wmp'/><category term='jdbc'/><category term='javascript'/><category term='testes'/><category term='cache'/><category term='encoding'/><category term='phpunit'/><category term='perl'/><category term='junit'/><category term='github'/><category term='tomcat'/><category term='selenium'/><category term='sun tech days'/><category term='applet'/><category term='listener'/><category term='xul'/><category term='rpm'/><category term='fisl'/><category term='fms'/><category term='opensocial'/><category term='busca'/><category term='firefox'/><category term='css'/><category term='git'/><category term='python'/><category term='nginx'/><category term='rails'/><category term='servlet'/><category term='rmagick'/><category term='irc'/><category term='unicode'/><category term='oauth'/><category term='pog'/><category term='apache'/><category term='linux'/><category term='vraptor'/><category term='extensions'/><category term='timotta-api'/><category term='mysql'/><category term='java'/><category term='jasperreport'/><category term='php'/><category term='ajax'/><category term='globo.com'/><category term='cucumber'/><category term='httparty'/><category term='bash'/><category term='thread'/><category term='oracle'/><category term='jmeter'/><category term='comet'/><category term='captcha'/><category term='scrum'/><category term='html'/><category term='pattern'/><category term='weblogic'/><category term='taglib'/><category term='watir'/><category term='heroku'/><category term='jms'/><category term='iptables'/><category term='rhino'/><category term='scwcd'/><category term='fakeweb'/><category term='duck typing'/><title type='text'>Programando sem cafeína</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default?start-index=101&amp;max-results=100'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>115</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-6677017559886694301</id><published>2011-03-22T22:56:00.000-03:00</published><updated>2011-03-22T22:56:45.266-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='encoding'/><title type='text'>Corrigindo o enconding retornado pelo Net::HTTP do ruby 1.9</title><content type='html'>A String retornada pelo Net::HTTP.get no ruby 1.9 sempre é definida com encoding "ASCII-8BIT". Acontece que muitas vezes esse não é o encoding correto da String, e portanto, erros esquisitos podem acontecer. Um exemplo pode ser visto abaixo quando utilizei essa String em um erb:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;invalid byte sequence in UTF-8&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Ou o seguinte, que acontece ao tentar encodar essa String para UTF-8:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;&gt; minha_string.encode("UTF-8")&lt;br /&gt;Encoding::UndefinedConversionError: "\xC3" from ASCII-8BIT to UTF-8&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;A solução para este problema é antes de encodar, definir o encoding correto da String. Para isso podemos utilizar o header "content-type" retornado na requisição. Se por exemplo ele retornasse 'text/xml; charset=ISO-8859-1', podemos converter a String da seguinte forma:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;&gt; minha_string.force_encoding("ISO-8859-1").encode("UTF-8")&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Para não ter que fazer isso a cada requisição HTTP, criei uma lib que altera o metodo body do HttpResponse. O código pode ser pego aqui:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://gist.github.com/882465"&gt;http://gist.github.com/882465&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Eu até pensei em alterar o metodo force_encoding da String para aceitar o valor do content-type, mas achei que isso seria dar muita responsabilidade para a String. Como o valor de content-type está apenas no ambito do HTTP, faz sentido colocar na classe Net::HttpResponse.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-6677017559886694301?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/6677017559886694301/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2011/03/corrigindo-o-enconding-retornado-pelo.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/6677017559886694301'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/6677017559886694301'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2011/03/corrigindo-o-enconding-retornado-pelo.html' title='Corrigindo o enconding retornado pelo Net::HTTP do ruby 1.9'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-8332728477990781646</id><published>2010-11-03T10:36:00.003-03:00</published><updated>2010-11-03T10:43:36.754-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>Gem brazilian-rails em versões de rails antigas</title><content type='html'>Se você estiver usando uma versão antiga do brazilian-rails é bem provável que ao tentar instalar esta gem ela comece a instalar também versões superiores do rails. Isso porque esta gem possui um conjunto de dependências definidas somente com o operador "&gt;=". &lt;br /&gt;&lt;br /&gt;Ou seja, se você tentar instalar a versão 2.1.13 por exemplo, que funciona com o rails 2.3.2, ele tentará baixar a dependência da gem brdinheiro "&gt;= 2.1.13", acabando por baixar a versão 3.0.0, que depende do activerecord 3.0.0, do rails 3. Nem preciso dizer que isso acabará dando merda.&lt;br /&gt;&lt;br /&gt;A solução é baixar todos os componentes independentemente sem suas dependências da seguinte forma:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;sudo gem install brnumeros --version=2.1.13 --ignore-dependencies&lt;br /&gt;sudo gem install brdinheiro --version=2.1.13 --ignore-dependencies&lt;br /&gt;sudo gem install brcep --version=2.1.13 --ignore-dependencies&lt;br /&gt;sudo gem install brdata --version=2.1.13 --ignore-dependencies&lt;br /&gt;sudo gem install brhelper --version=2.1.13 --ignore-dependencies&lt;br /&gt;sudo gem install brstring --version=2.1.13 --ignore-dependencies&lt;br /&gt;sudo gem install brcpfcnpj --version=2.1.13 --ignore-dependencies&lt;br /&gt;sudo gem install brI18n --version=2.1.13 --ignore-dependencies&lt;br /&gt;sudo gem install brazilian-rails --version=2.1.13 --ignore-dependencies&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-8332728477990781646?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/8332728477990781646/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2010/11/gem-brazilian-rails-em-versoes-de-rails.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/8332728477990781646'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/8332728477990781646'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2010/11/gem-brazilian-rails-em-versoes-de-rails.html' title='Gem brazilian-rails em versões de rails antigas'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-7322738270110127641</id><published>2010-09-02T11:53:00.004-03:00</published><updated>2010-09-02T12:09:04.979-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='nginx'/><title type='text'>Solucionando IO bloqueante do mysql no ruby</title><content type='html'>Fui a uma palestra muito interessante sobre performance na Oscon. A palestra &lt;a href="http://www.oscon.com/oscon2010/public/schedule/detail/13709"&gt;No Callbacks, No Threads: Async &amp; Cooperative Web Servers with Ruby 1.9&lt;/a&gt;, tratava do problema de IO bloqueante do driver do mysql para ruby, e como solução era proposto o uso de recursos como &lt;a href="http://rubyeventmachine.com/"&gt;Event Machine&lt;/a&gt; e &lt;a href="http://www.infoq.com/news/2007/08/ruby-1-9-fibers"&gt;Fibers&lt;/a&gt; do ruby 1.9. Contudo, a solução não ficava nada elegante, tornando a manutenção do código muito complicada. Embora hoje haja um esforço para tornar esse trabalho transparente, consegui obter o mesmo resultado em performance basicamente aumentando o número de processos a atenderem as requisições.&lt;br /&gt;&lt;br /&gt;Mas antes de explicar a solução é preciso demonstrar o problema. O caso é que embora tenhamos threads no &lt;a href="http://ruby-lang.org/pt/"&gt;ruby&lt;/a&gt;, alguns drivers como o do mysql são bloqueantes, ou seja, quando estão em uma operação de IO eles bloqueiam o processo inteiro, inclusive todas suas threads. Veja por exemplo o seguinte código:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;class TestesController &lt; ApplicationController&lt;br /&gt;  def index&lt;br /&gt;    Thread.new { Teste.connection.execute("insert into testes (id) select sleep(2)") }&lt;br /&gt;    render :text =&gt; 'ok'&lt;br /&gt;  end&lt;br /&gt;end&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Teoricamente ao fazermos a requisição a este controller de teste, a requisição não deveria durar os dois segundos de espera pelo retorno do insert ao mysql. Mas não é isso que acontece. As requisições acabam sendo enfileiradas pois o processo inteiro fica bloqueado a cada execução de query no banco. Isso pode ser comprovado utilizando o &lt;a href="http://httpd.apache.org/docs/2.0/programs/ab.html"&gt;Apache Benchmark&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;&gt; ab -c 10 -n 10 http://localhost:3000/testes&lt;br /&gt;...&lt;br /&gt;Concurrency Level:      10&lt;br /&gt;Time taken for tests:   20.514 seconds&lt;br /&gt;Complete requests:      10&lt;br /&gt;...&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;É bom deixar claro que o bloqueio ocorre somente ao usar o método execute do driver, que é responsável por fazer atualizações no banco. Fazendo somente consultas, o bloqueio não ocorre.&lt;br /&gt;&lt;br /&gt;Na palestra em questão, foi demonstrado que utilizando &lt;a href="http://rubyeventmachine.com/"&gt;Event Machine&lt;/a&gt; e &lt;a href="http://www.infoq.com/news/2007/08/ruby-1-9-fibers"&gt;Fibers&lt;/a&gt; é possivel desbloquear o processo utilizando callbacks. No final o tempo total foi reduzido para 2 segundos e alguns milésimos. Esse mesmo resultado eu obtive configurando um nginx com passenger configurado com 10 forks. Uma solução bem mais limpa. São apenas dois parametros, um do &lt;a href="http://nginx.net/"&gt;nginx&lt;/a&gt;, e outro do &lt;a href="http://www.modrails.com/documentation/Users%20guide%20Nginx.html"&gt;passenger&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;worker_processes  10;&lt;br /&gt;#...&lt;br /&gt;http {&lt;br /&gt;    passenger_max_pool_size 10;&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;E o resultado do teste:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;&gt; ab -c 10 -n 10 http://localhost/testes&lt;br /&gt;&lt;br /&gt;Concurrency Level:      10&lt;br /&gt;Time taken for tests:   2.424 seconds&lt;br /&gt;Complete requests:      10&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;É claro que ainda sim a solução proposta na palestra é mais performática, até porque nela o consumo de memória é bem menor. Resta saber se essa economia vale a pena quando se pesa na balança o custo de manter um código mais complicado e os problemas que a concorrência podem trazer ao seu projeto. E para demonstrar o quão escalável é dividir as requisições em processos, fiz ainda um ultimo teste, com 1000 requisições, sendo 200 simultâneas, configurando o &lt;a href="http://nginx.net/"&gt;nginx&lt;/a&gt; e o &lt;a href="http://www.modrails.com/documentation/Users%20guide%20Nginx.html"&gt;passenger&lt;/a&gt; para trabalhar com 200 forks:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;&gt; ab -c 200 -n 1000 http://localhost/testes&lt;br /&gt;&lt;br /&gt;Concurrency Level:      200&lt;br /&gt;Time taken for tests:   13.043 seconds&lt;br /&gt;Complete requests:      1000&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Ou seja, o tempo total de teste manteve-se estável. Levando- em conta que dificilmente alguém fará um insert no banco com sleep, acredito que esssa seja uma boa solução para o problema de IO bloqueante. Ao invés de threads, utilizar processos.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-7322738270110127641?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/7322738270110127641/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2010/09/solucionando-io-bloqueante-do-mysql-no.html#comment-form' title='3 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/7322738270110127641'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/7322738270110127641'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2010/09/solucionando-io-bloqueante-do-mysql-no.html' title='Solucionando IO bloqueante do mysql no ruby'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-3830513509421407387</id><published>2010-08-09T13:24:00.000-03:00</published><updated>2010-08-09T13:25:19.228-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scrum'/><title type='text'>Aumento de produtividade por ponto de complexidade</title><content type='html'>Tenho ouvido ultimamente muitos amigos da área comentando sobre pressão por aumento de produtividade baseada em pontos de complexidade. Isso me deixa bastante preocupado. Embora seja nobre o desejo de aumentar as entregas da área de desenvolvimento, quantificar isso usando os pontos de complexidade das histórias não quer dizer muita coisa. &lt;br /&gt;&lt;br /&gt;Contudo antes de simplesmente reclamar contra a pressão, é preciso analisar as possiveis causas de produtividade baixa que possam estimular esse tipo de pressão. Pensando bastante cheguei a três possibilidades, e com elas, possiveis soluções, que seriam mais eficazes do que estimular aumento desse tipo de numero.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;1&lt;/span&gt;- Não há confiança de que o time esteja trabalhando em seu máximo. Ou seja, o agente gerador de pressão acredita que os integrantes estão fazendo corpo mole ou gastando o dia com amenidades ao invés de se focar na entrega do projeto. A pressão por aumento de pontos de complexidade pode até resolver esse problema temporariamente, mas também pode ser mascarado por integrantes do time que se sentem coagidos a fazer horas extras para cobrir as entregas esperadas. Ou seja, o problema só se resolve mesmo com conversas francas e com uma presença mais ativa do interessado. &lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;2&lt;/span&gt;- O time percebendo folga na iteração aproveita para melhorar a qualidade da entrega ainda mais. Esse tipo de preciosismo costuma acontecer com bastante frequência. Muitas vezes o desenvolvedor por ter mais tempo para pensar aproveita para implementar testes e fluxos mais rebuscados evitando bugs que no futuro tomariam o triplo do tempo, e o designer aproveita para criar e rebuscar as interfaces e assim encantar ainda mais o cliente. Neste caso a pressão pelo aumento de entrega de pontos de complexidade apenas estimula a diminuição da qualidade. Ou seja, embora haja um aumento imediato na velocidade, em pouco tempo ela cairá por causa das correções de bugs e dos ajustes visuais.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;3&lt;/span&gt;- O time é inexperiente ou não conhece a tecnologia adotada. Neste caso pressionar pelo aumento de entrega de pontos de complexidade de nada adianta. De certa forma, com o tempo, naturalmente as entregas serão maiores, ou em muitos casos menores pois o time começará a estimar com menos pontos, demonstrando assim a ineficácia desse numeros para medir produtividade. Para resolver este problema existem diversas opções como, organizar dojos, estimular programação em par, indicar livros e treinamentos, estimular a experimentação com tempo para projetos pessoais.&lt;br /&gt;&lt;br /&gt;Todas três possibilidades acima eu vi acontecer de perto nos times em que trabalhei. E quase sempre o problema foi resolvido sem utilizar os pontos de complexidade como parâmetro de medição. E vocês lembram de alguma outra causa de produtividade baixa? Tem idéia de como solucionar? Qual sua opinião sobre o assunto?&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-3830513509421407387?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/3830513509421407387/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2010/08/aumento-de-produtividade-por-ponto-de.html#comment-form' title='4 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3830513509421407387'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3830513509421407387'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2010/08/aumento-de-produtividade-por-ponto-de.html' title='Aumento de produtividade por ponto de complexidade'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-7408406842323660006</id><published>2010-05-17T19:14:00.009-03:00</published><updated>2010-05-17T19:38:49.114-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='globo.com'/><title type='text'>Site de Passione estréia nova plataforma de novelas da Rede Globo</title><content type='html'>Entrou no ar hoje a tarde o novo site de &lt;a href="http://passione.globo.com"&gt;Passione&lt;/a&gt;, a nova novela das oito da &lt;a href="http://redeglobo.com"&gt;Rede Globo&lt;/a&gt;. Mas por trás deste site está muito mais que um site. É o ínicio de uma &lt;a href="http://redeglobo.globo.com/novidades/novelas/noticia/2010/04/passione-estreia-nova-plataforma-de-internet-no-dia-17-de-maio.html"&gt;nova experiência&lt;/a&gt; de consumo da dramaturgia da &lt;a href="http://redeglobo.com"&gt;Rede Globo&lt;/a&gt; na internet.&lt;br /&gt;&lt;br /&gt;Para quem não sabe, depois de quatro anos na equipe de &lt;a href="http://video.globo.com"&gt;videos&lt;/a&gt; da &lt;a href="http://globo.com"&gt;globo.com&lt;/a&gt;, me engajei logo no inicio deste projeto no time dedicado a ele. Apesar de ser a mesma empresa, é um mundo totalmente novo. Novas tecnologias, novas formas de gestão, novas pessoas. Tudo novo. Foi e está sendo muito bom.&lt;br /&gt;&lt;br /&gt;E agora, com a entrega no prazo, e o feedback de dezenas de pessoas no twitter &lt;a href="http://twitter.com/#search?q=passione"&gt;elogiando nosso trabalho&lt;/a&gt;, só tenho a agradecer ao meu time que me acolheu tão bem, e a todos as equipes que nos ajudaram, seja nos dando suporte, seja nos dando dicas, seja nos incentivando. Sem vocês o resultado não seria igual. Parabéns a todos nós!&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-7408406842323660006?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/7408406842323660006/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2010/05/site-de-passione-estreia-nova.html#comment-form' title='3 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/7408406842323660006'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/7408406842323660006'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2010/05/site-de-passione-estreia-nova.html' title='Site de Passione estréia nova plataforma de novelas da Rede Globo'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-5087112262762484179</id><published>2010-03-03T20:03:00.017-03:00</published><updated>2010-03-03T23:41:02.352-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='testes'/><title type='text'>Objetos fake em diversas linguagens para divertir minha vó</title><content type='html'>Quase todas as linguagens que trabalhei possuem ferramentas para criar objetos fakes e assim auxiliar na construção de testes. Mas e se essas ferramentas não existissem? Estaríamos perdidos? Claro que não! &lt;a href="http://martinfowler.com/articles/mocksArentStubs.html#TheDifferenceBetweenMocksAndStubs"&gt;Mocks e stubs&lt;/a&gt; podem ser criados de diversas formas diferentes nas diversas linguagens. &lt;br /&gt;&lt;br /&gt;Esse é um ótimo assunto pra se discutir em um jantar de família. Quer coisa mais divertida que explicar pra sua avó como construir objetos fake em diversas linguagens? Por exemplo um simples stub que conta quantas vezes um método que notifica visualização de um filme em um serviço externo é chamado. Algo similar ao que o código abaixo faz. Diversão garantida!&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;servico.should_receive(:notificar_visualizacao!).with(filme).once&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Em Ruby&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Em &lt;a href="http://www.ruby-lang.org/"&gt;ruby&lt;/a&gt; podemos simplesmente criar uma nova classe em tempo de execução ou adicionar um método a um objeto qualquer. É a maneira mais simples de garantir um sorrisão da sua vó tamanha facilidade. No caso do exemplo abaixo resolvi adicionar métodos a um objeto qualquer e ao final executo o teste utilizando o &lt;a href="http://rspec.info/"&gt;rspec&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;servico_fake = Object.new&lt;br /&gt;servico_fake.instance_eval do&lt;br /&gt;  def notificar_visualizacao!(filme)&lt;br /&gt;    @filme_visualizado = filme&lt;br /&gt;    @quantidade_visualizacoes = quantidade_visualizacoes + 1&lt;br /&gt;  end&lt;br /&gt;  def filme_visualizado&lt;br /&gt;    @filme_visualizado&lt;br /&gt;  end&lt;br /&gt;  def quantidade_visualizacoes&lt;br /&gt;    @quantidade or 0&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;filme = Filme.new&lt;br /&gt;filme.servico = servico_fake&lt;br /&gt;filme.visualizar!&lt;br /&gt;&lt;br /&gt;servico_fake.filme.should be_equal(filme)&lt;br /&gt;servico_fake.quantidade.should == 1&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Em Python&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Em &lt;a href="http://www.python.org/"&gt;Python&lt;/a&gt; é possivel alterar métodos de instancias em tempo de execução, mas não é possivel adicionar métodos a um objeto da classe &lt;span style="font-weight:bold;"&gt;object&lt;/span&gt;. Neste momento minha vozinha fica decepcionada. Para fazer algo semelhante ao que fizemos em ruby teríamos então que instanciar um objeto da classe original e só depois modificar o método. Ficaria mais ou menos assim:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;filme_visualizado = None&lt;br /&gt;quantidade_visualizacoes = 0&lt;br /&gt;&lt;br /&gt;def notificar_visualizacao_fake(filme):&lt;br /&gt;  global filme_visualizado, filme_visualizado &lt;br /&gt;  filme_visualizado = filme&lt;br /&gt;  quantidade_visualizacoes += 1&lt;br /&gt;&lt;br /&gt;servico_fake = ServicoExterno()&lt;br /&gt;servico_fake.notificar_visualizacao = notificar_visualizacao_fake&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Esta opção passa a ser ruim quando o construtor da classe original executa algumas tarefas que são custosas para o nosso teste. Por isso, acho que em python o melhor para o este caso é criar uma classe em tempo de execução, deixando minha vó um pouco mais alegre, conforme exemplo abaixo:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;class ServicoFake(object):&lt;br /&gt;  def __init__(self):&lt;br /&gt;    self.filme_visualizado = None&lt;br /&gt;    self.quantidade_visualizacoes = 0&lt;br /&gt;  def notificar_visualizacao(self,filme):&lt;br /&gt;    self.filme_visualizado = filme&lt;br /&gt;    self.quantidade_visualizacoes += 1&lt;br /&gt;&lt;br /&gt;servico_fake = ServicoFake()&lt;br /&gt;&lt;br /&gt;filme = Filme()&lt;br /&gt;filme.servico = servico_fake&lt;br /&gt;filme.visualizar()&lt;br /&gt;&lt;br /&gt;assert servico_fake.filme_visualizado is filme&lt;br /&gt;assert servico_fake.quantidade_visualizacoes == 1&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Em Java&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Uma forma "simples" de fazer em &lt;a href="http://java.sun.com/"&gt;Java&lt;/a&gt; é criando uma nova classe para herdar a original e sobrescrever somente o método desejado. Mas aí cairíamos no mesmo problema que discutimos sobre um possível construtor com código muito custoso na superclasse. E minha vó, que apesar de repetir muitas vezes as mesmas histórias, não gosta de ouvir as nossas repetidas.&lt;br /&gt;&lt;br /&gt;Uma alternativa que temos é extrair uma interface da classe original para que em nosso teste a gente possa implementar essa interface da maneira que quisermos. Ficaria mais ou menos assim:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;//ServicoExterno.java&lt;br /&gt;public interface ServicoExterno {&lt;br /&gt;  void notificarVisualizacao(Filme filme);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//ServicoExternoHTTP.java&lt;br /&gt;public class ServicoExternoHTTP implements ServicoExterno {&lt;br /&gt;  public ServicoExternoHTTP() {&lt;br /&gt;    //Faz um monte de coisas&lt;br /&gt;  }&lt;br /&gt;  public void notificarVisualizacao(Filme filme) {&lt;br /&gt;    //Faz mais coisas ainda&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Então em nosso teste a gente cria uma classe implementando a interface recém criada:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;public class ServicoExternoFake implements ServicoExterno {&lt;br /&gt;  public int quantidade_visualizacoes = 0;&lt;br /&gt;  public Filme filme_visualizado = null;&lt;br /&gt; &lt;br /&gt;  public void notificarVisualizacao(Filme filme) {&lt;br /&gt;    filme_visualizado = null;&lt;br /&gt;    quantidade_visualizacoes++;&lt;br /&gt;  } &lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;E depois, mesmo que neste ponto minha vó já esteje dormindo, a gente utiliza a classe fake criada no teste:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;ServicoExternoFake servicoFake = new ServicoExternoFake();&lt;br /&gt;  &lt;br /&gt;Filme filme = new Filme();&lt;br /&gt;filme.setServico(servicoFake);&lt;br /&gt;filme.visualizar();&lt;br /&gt; &lt;br /&gt;assertSame(filme,servicoFake.filme_visualizado);&lt;br /&gt;assertEquals(1,servicoFake.quantidade_visualizacoes);&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Parece que minha avó não gostou da história. Ela começou tão &lt;a href="http://infoblogs.com.br/frame/goframe.action?contentId=29088"&gt;dinâmica&lt;/a&gt; e foi ficando cadas vez mais devagar. Sem dúvida eu deveria ter contato ao contrário.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-5087112262762484179?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/5087112262762484179/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2010/03/objetos-fake-em-diversas-linguagens.html#comment-form' title='2 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/5087112262762484179'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/5087112262762484179'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2010/03/objetos-fake-em-diversas-linguagens.html' title='Objetos fake em diversas linguagens para divertir minha vó'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-9094755288144002331</id><published>2010-02-09T17:02:00.003-03:00</published><updated>2010-02-09T17:14:48.119-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='selenium'/><category scheme='http://www.blogger.com/atom/ns#' term='testes'/><title type='text'>Upload de arquivo com selenium server no firefox</title><content type='html'>Para fazer upload de arquivo com selenium é preciso alterar seu selenium-server.jar liberando permissão para que seja possível manipular campos do tipo file por javascript. Para fazer isso, extraia o &lt;span style="font-weight:bold;"&gt;selenium-server.jar&lt;/span&gt;:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;jar -xvf selenium-server.jar&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Crie um arquivo chamado &lt;span style="font-weight:bold;"&gt;user.js&lt;/span&gt; no diretório &lt;span style="font-weight:bold;"&gt;customProfileDirCUSTFF&lt;/span&gt; contendo um código semelhante com o exibido abaixo:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;user_pref("signed.applets.codebase_principal_support", true);&lt;br /&gt;&lt;br /&gt;user_pref("capability.principal.codebase.p0.granted", "UniversalFileRead");&lt;br /&gt;user_pref("capability.principal.codebase.p0.id", "http://localhost");&lt;br /&gt;user_pref("capability.principal.codebase.p0.subjectName", "");&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Repare que a liberação de acesso é feita por host. No caso do exemplo estou liberando apenas para o ambiente local. Se desejar liberar outros hosts baixa adicionar outros conforme o exemplo abaixo:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;user_pref("signed.applets.codebase_principal_support", true);&lt;br /&gt;&lt;br /&gt;user_pref("capability.principal.codebase.p0.granted", "UniversalFileRead");&lt;br /&gt;user_pref("capability.principal.codebase.p0.id", "http://localhost");&lt;br /&gt;user_pref("capability.principal.codebase.p0.subjectName", "");&lt;br /&gt;&lt;br /&gt;user_pref("capability.principal.codebase.p1.granted", "UniversalFileRead");&lt;br /&gt;user_pref("capability.principal.codebase.p1.id", "http://globo.com");&lt;br /&gt;user_pref("capability.principal.codebase.p1.subjectName", "");&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Feito isso, basta gerar novamente o jar e executá-lo.&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;jar cvfm selenium-server.jar ./META-INF/MANIFEST.MF -C ./ .&lt;br /&gt;java -jar selenium-server.jar&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Já será possivel preencher o path de qualquer arquivo no campo como se ele fosse apenas um campo de textos. Isso no Firefox é claro.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-9094755288144002331?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/9094755288144002331/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2010/02/upload-de-arquivo-com-selenium-server.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/9094755288144002331'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/9094755288144002331'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2010/02/upload-de-arquivo-com-selenium-server.html' title='Upload de arquivo com selenium server no firefox'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-6592361177784497921</id><published>2010-01-15T14:03:00.001-03:00</published><updated>2010-01-15T14:03:42.434-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Diferenças entre ruby e python: Que perigo</title><content type='html'>Em ruby:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;def a(z=[])&lt;br /&gt;  z.push 'a'&lt;br /&gt;end&lt;br /&gt;a   # retorna  ["a"]&lt;br /&gt;a   # retorna  ["a"]&lt;br /&gt;a   # retorna  ["a"]&lt;br /&gt;a   # retorna  ["a"]&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Em python:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;def a(z=[]):&lt;br /&gt;  z.append('a')&lt;br /&gt;  return z&lt;br /&gt;a()   # retorna ['a']&lt;br /&gt;a()   # retorna ['a','a']&lt;br /&gt;a()   # retorna ['a','a','a']&lt;br /&gt;a()   # retorna ['a','a','a','a']&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-6592361177784497921?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/6592361177784497921/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2010/01/diferencas-entre-ruby-e-python-que.html#comment-form' title='10 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/6592361177784497921'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/6592361177784497921'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2010/01/diferencas-entre-ruby-e-python-que.html' title='Diferenças entre ruby e python: Que perigo'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-1523376675335775806</id><published>2009-11-25T18:18:00.006-03:00</published><updated>2009-11-25T19:12:17.738-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='cucumber'/><category scheme='http://www.blogger.com/atom/ns#' term='watir'/><title type='text'>Utilizando asserts para testar layouts</title><content type='html'>Um dos problemas encontrados em nossos testes de aceitação é a impossibilidade de validar a aparência exata do resultado final de uma determinada ação do usuário. Conseguimos validar com o &lt;a href="http://watir.com/"&gt;watir&lt;/a&gt; se determinada div possui um texto, se determinado link está presente, mas nada impede que eles estejam escondidos, por trás de outro div, ou com letras da mesma cor do fundo. É sempre útil, mas não necessáriamente exato, sendo sempre um ponto de falha. Ainda mais em um sistema como o que estamos trabalhando em que o visual para o usuário tem uma grande importância.&lt;br /&gt;&lt;br /&gt;Eu e o &lt;a href="http://twitter.com/lquixada"&gt;quixadá&lt;/a&gt;, mestre do javascript e meu par de hoje, trabalhamos em uma correção de bug visual, e como costumamos trabalhar com &lt;a href="http://www.teachmetocode.com/screencasts/4"&gt;desenvolvimento outside-in&lt;/a&gt;, chegamos ao dilema de como criar um teste para garantir que a falha existia. A solução encontrada foi inserir asserts dentro do código javascript, tal qual a funcionalidade de &lt;a href="http://wiki.answers.com/Q/What_does_the_assert_keyword_do_in_Java"&gt;asserts do java&lt;/a&gt;. O código da função assert ficou parecido com o mostrado abaixo:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;function assert(mensagem,valorDesejado,valorRecebido) {&lt;br /&gt; if( valorDesejado != valorRecebido ) {&lt;br /&gt;   var html = '&amp;lt;div class="warning"&amp;gt;' + mensagem + '&amp;lt;/div&amp;gt;';&lt;br /&gt;   $('body').append(html);&lt;br /&gt;   throw mensagem;&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Em nosso caso, tinhamos que ter a certeza de que após um clique do usuário a barra de rolagem do elemento mantinha-se da mesma forma que anteriormente. Então o código ficou parecido com o mostrado abaixo:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;$.get( url, function ( responseHtml ) {&lt;br /&gt; var scrollAnterior = $('#opcoes').scrollTop();&lt;br /&gt; substituiOpcoes( responseHtml );&lt;br /&gt; assert( 'Deveria manter scroll igual', scrollAnterior, $('#opcoes').scrollTop() );&lt;br /&gt;});&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;No teste de aceitação verificamos então que o texto 'Deveria manter scroll igual' não deveria aparecer. O código do step do &lt;a href="http://cukes.info/"&gt;cucumber&lt;/a&gt; utilizando o &lt;a href="http://watir.com/"&gt;watir&lt;/a&gt; pode ser visto abaixo:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;Then /^a barra de rolagem deveria permanecer na mesma posição$/ do&lt;br /&gt; @browser.text.should_not include('Deveria manter scroll igual')&lt;br /&gt;end&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Com o teste pronto e falhando, aí sim corrigimos a função javascript &lt;b&gt;substituiOpcoes(html)&lt;/b&gt; de forma a manter o scroll anterior.&lt;br /&gt;&lt;br /&gt;Não é uma solução perfeita. Estamos pesquisando uma melhor, como pode ser vista no post &lt;a href="http://www.anselmoalves.com/2009/10/28/t-plan-robot/"&gt;Testes de aceitação automáticos para Flash com T-Plan Robot&lt;/a&gt; do Anselmo. Mas enquanto isso podemos evitar alguns pontos de falha visuais que costumávamos ter que testar manualmente. Dado que nosso sistema possui 100% de cobertura de testes unitários, somados a 152 cenários de teste de aceitação que abragem 1568 passos, acho que estamos indo por um bom caminho.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-1523376675335775806?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/1523376675335775806/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/11/utilizando-asserts-para-testar-layouts.html#comment-form' title='1 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/1523376675335775806'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/1523376675335775806'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/11/utilizando-asserts-para-testar-layouts.html' title='Utilizando asserts para testar layouts'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-2313894709322485838</id><published>2009-11-09T23:42:00.000-03:00</published><updated>2010-11-09T23:43:01.006-03:00</updated><title type='text'>Web Astrologia: Duvidas, criticas e sugestões</title><content type='html'>Utilize esse espaço para publicar duvidas, criticas e sugestões ao site &lt;a href="http://webastrologia.com.br"&gt;http://webastrologia.com.br&lt;/a&gt; Sua contribuição será muito bem vinda.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-2313894709322485838?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/2313894709322485838/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/11/web-astrologia-duvidas-criticas-e.html#comment-form' title='1 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/2313894709322485838'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/2313894709322485838'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/11/web-astrologia-duvidas-criticas-e.html' title='Web Astrologia: Duvidas, criticas e sugestões'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-1896918946376856227</id><published>2009-10-30T00:24:00.006-03:00</published><updated>2009-10-30T00:41:03.096-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='oauth'/><category scheme='http://www.blogger.com/atom/ns#' term='opensocial'/><title type='text'>API rest para OpenSocial do Orkut com ruby</title><content type='html'>A documentação da &lt;a href="http://code.google.com/apis/orkut/docs/rest/developers_guide_protocol.html"&gt;API rest do OpenSocial do Orkut&lt;/a&gt; detalha muito bem as opções e formatos de retorno disponíveis porém é um tanto vaga sobre como fazer a autenticação necessária para usá-la. Basicamente lá é explicado os parâmetros a serem enviados e que o protocolo é o &lt;a href="http://delicious.com/chapundifornio/oauth"&gt;OAuth&lt;/a&gt;. Então detalho aqui como obter por exemplo os dados de um usuário apartir desta API.&lt;br /&gt;&lt;br /&gt;Em primeiro lugar é preciso obter a consumer key e consumer secret de sua aplicação. Isso é feito gerando um token aqui: &lt;a href="https://www.google.com/gadgets/directory/verify"&gt;https://www.google.com/gadgets/directory/verify&lt;/a&gt;. Você deve colocar esse token dentro da tag &lt;span style="font-weight:bold;"&gt;content&lt;/span&gt; do xml descritor de sua aplicação e depois fazer a validação provando que é dono da aplicação. Com isso o Google irá lhe informar seu consumer key e consumer secret. Guarde eles com carinho.&lt;br /&gt;&lt;br /&gt;Depois, com a &lt;a href="http://oauth.rubyforge.org/"&gt;gem oauth&lt;/a&gt; instalada você deverá executar um código semelhante ao exibido abaixo, com a premissa de que as variaveis &lt;span style="font-weight:bold;"&gt;consumer_key&lt;/span&gt; e &lt;span style="font-weight:bold;"&gt;consumer_secret&lt;/span&gt; estão preenchidas com os correspondentes à sua aplicação. E que a variável &lt;span style="font-weight:bold;"&gt;id&lt;/span&gt; é o id do usuário do &lt;a href="http://www.orkut.com"&gt;Orkut&lt;/a&gt; que você está querendo conhecer melhor.&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;  consumer = OAuth::Consumer.new( &lt;br /&gt;    consumer_key, &lt;br /&gt;    consumer_secret, &lt;br /&gt;    :site =&gt; 'http://www.orkut.com',&lt;br /&gt;    :scheme =&gt; :query_string,&lt;br /&gt;    :http_method =&gt; :get &lt;br /&gt;  )&lt;br /&gt;    &lt;br /&gt;  request = consumer.create_signed_request(:get, &lt;br /&gt;    "/social/rest/people/#{id}/@self?xoauth_requestor_id=#{id}")  &lt;br /&gt;  res = Net::HTTP.start('www.orkut.com', 80) do&lt;br /&gt;    |h| h.request(request) &lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  puts res.body&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-1896918946376856227?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/1896918946376856227/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/10/api-rest-para-opensocial-do-orkut-com.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/1896918946376856227'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/1896918946376856227'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/10/api-rest-para-opensocial-do-orkut-com.html' title='API rest para OpenSocial do Orkut com ruby'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-2543278992691201507</id><published>2009-08-01T03:03:00.003-03:00</published><updated>2009-08-01T03:30:25.909-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><title type='text'>TralhaController notificando obervadores por pattern</title><content type='html'>TralhaController é uma biblioteca javascript que permite que aplicações web que utilizam requisições assíncronas ofereçam URLs para cada contexto de navegação do usuário. Para saber mais sobre ela basta ler o artigo &lt;a href="http://www.brunocarvalho.com/08/05/2009/mantendo-contexto-usando-ajax/"&gt;Mantendo contexto usando ajax&lt;/a&gt; que o Bruno Carvalho escreveu recentemente.&lt;br /&gt;&lt;br /&gt;Porém uma coisa me incomodava no uso dessa classe. Para cada observador registrado eu precisava dentro do método update verificar se ele precisava agir de acordo com a URL. Isso acaba por tornar o código mais confuso pois obrigava-nos a misturar validações e lógica de negócio. Veja um exemplo abaixo:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;var observadorBusca = { update:function(url) {&lt;br /&gt;  if( url.indexOf("#busca=")!=-1 ) {&lt;br /&gt;    //Executa a busca&lt;br /&gt;  }&lt;br /&gt;} };&lt;br /&gt;var observadorLink = { update:function(url) {&lt;br /&gt;  if( url.indexOf("#link=")!=-1 ) {&lt;br /&gt;    //Executa o link clicado&lt;br /&gt;  }&lt;br /&gt;} };&lt;br /&gt;TralhaController.addObserver(observadorBusca);&lt;br /&gt;TralhaController.addObserver(observadorLink);&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Para tentar melhorar isso, pus a mão na massa e alterei o &lt;a href="http://github.com/timotta/TralhaController/tree/master"&gt;TralhaController&lt;/a&gt; permitindo registrar observadores que só fossem notificados diante de URLs que tivessem o padrão desejado. Utilizando então a última versão da biblioteca o código acima poderia então ser alterado para ficar da seguinte forma:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;var observadorBusca = { update:function(url) { /* Executa a busca */ } };&lt;br /&gt;var observadorLink = { update:function(url) { /* Executa o link clicado */ } };&lt;br /&gt;&lt;br /&gt;TralhaController.addObserver("\\#busca\=.*", observadorBusca);&lt;br /&gt;TralhaController.addObserver("\\#link\=.*", observadorLink);&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Bem mais simples certo? É claro que há aqueles que não são muito fãs de expressões regulares e vão achar a primeira forma melhor. Para esses não há problema pois a forma antiga continua funcionando. Observadores registrados sem padrões continuam sendo notificados a toda e qualquer modificação de URL.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-2543278992691201507?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/2543278992691201507/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/08/tralhacontroller-notificando.html#comment-form' title='1 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/2543278992691201507'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/2543278992691201507'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/08/tralhacontroller-notificando.html' title='TralhaController notificando obervadores por pattern'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-4846244546767576324</id><published>2009-07-31T00:02:00.006-03:00</published><updated>2009-07-31T12:01:23.720-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Roda: Eu também já recriei</title><content type='html'>Uma das discussões mais quentes do momento, e também uma das mais antigas, é sobre a mania que nós desenvolvedores temos de reinventar a roda. Eu mesmo já reinventei diversas vezes e hoje olho para trás e vejo quanto tempo poderia ter poupado ao usar alguma ferramenta já pronta, ou apenas ter adaptado uma que não se encaixava perfeitamente aos meus requisitos. &lt;br /&gt;&lt;br /&gt;Há claro excessões, em que seu requisito é tão mínimo que reimplementar uma solução é mais rápido do que aprender uma já existente no mercado. Como foi o caso de um &lt;a href="http://m.video.globo.com/"&gt;sistema&lt;/a&gt; que estávamos desenvolvendo em &lt;a href="http://www.php.net"&gt;PHP&lt;/a&gt; e precisávamos de um framework apenas para separar a lógica da renderização de templates por tema. Validamos rapidamente diversos &lt;a href="http://www.phpframeworks.com"&gt;frameworks MVC&lt;/a&gt; e percebemos que era mais fácil criar em meia hora a seguinte classe:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;class Renderizador {&lt;br /&gt;    function Renderizador($tema=null) {&lt;br /&gt;     $this-&gt;_dir = dirname(__FILE__) . "/templates/" . $this-&gt;tema;&lt;br /&gt;    }&lt;br /&gt;    public function put($nome,$obj) {&lt;br /&gt;     $this-&gt;$nome = $obj;&lt;br /&gt;    }&lt;br /&gt;    public function show($template) {&lt;br /&gt;     include(  $this-&gt;_dir . "/" . $template . ".php");&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;A utilização dela era mais simples ainda. No exemplo abaixo as variáveis inseridas no renderizador ficam disponiveis para o template listagem.php através de $this, que nada mais é do que um arquivo php comum.&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;$renderizador = new Renderizador('cinza');&lt;br /&gt;$renderizador-&gt;put('lista',[1,2,3,4]);&lt;br /&gt;$renderizador-&gt;show('listagem');&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Simples, sem precisar aprender nenhum framework ou linguagem de marcação nova. Em duas horas o time já estava trabalhando com a lógica separada da apresentação. Contudo essa é a exceção.&lt;br /&gt;&lt;br /&gt;Em geral já recriei diversas rodas desnecessariamente desperdiçando muito tempo. Framework de mapeamento objeto relacional, CMS, blog e até mesmo o TimerTask do Java são alguns dos exemplos. Esse último me rendeu uma longa discussão com o &lt;a href="http://fragmental.com.br"&gt;Phillip Calçado&lt;/a&gt; na época em que ele trabalhava &lt;a href="http://video.globo.com"&gt;aqui&lt;/a&gt;. Felizmente ele foi persistente em me fazer desistir daquela implementação.&lt;br /&gt;&lt;br /&gt;De qualquer forma, o importante é a lição aprendida. Hoje tento usar ao máximo ferramentas já disponíveis, e quando alguma não me atende perfeitamente, ao invés de começá-la do zero eu a altero e contribuo, como foi o caso de uma recente &lt;a href="/2009/05/padroes-de-urls-com-expressoes.html"&gt;alteração que precisei fazer no fakeweb&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Outros membros da equipe também já vem de longe com esta mesma filosofia e estão constantemente contribuindo como &lt;a href="http://gcirne.wordpress.com/"&gt;Guilherme Cirne&lt;/a&gt; ao contribuir com o WillPaginate, o &lt;a href="http://www.anselmoalves.com/"&gt;Anselmo&lt;/a&gt; também contribuindo com o Fakeweb e o &lt;a href="http://www.brunocarvalho.com/"&gt;Bruno Carvalho&lt;/a&gt; contribuindo com o &lt;a href="http://github.com/timotta/TralhaController/tree/master"&gt;TralhaController&lt;/a&gt; e o ExceptionNotifier.&lt;br /&gt;&lt;br /&gt;O importante é aproveitar aquilo que a comunidade já lhe oferece pronto e testado. Economiza tempo seu, e do próximo desenvolvedor, que dará manutenção a algo que é padrão, com muita discussão em fóruns e muitas soluções documentadas.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-4846244546767576324?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/4846244546767576324/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/07/roda-eu-tambem-ja-recriei.html#comment-form' title='2 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/4846244546767576324'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/4846244546767576324'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/07/roda-eu-tambem-ja-recriei.html' title='Roda: Eu também já recriei'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-3299275015450942414</id><published>2009-07-18T02:16:00.003-03:00</published><updated>2009-07-18T19:29:39.363-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>Dica rápida de charset para projetos rails com mysql</title><content type='html'>Se você está trabalhando em um projeto ruby on rails com banco mysql, e o charset definido tanto no servidor como no database.yml é UTF-8, tome o cuidado para que inserções no banco por conta de terceiros também seja feito neste charset.&lt;br /&gt;&lt;br /&gt;Um simples insert feito na mão utilizando o cliente padrão do mysql pode te atrapalhar bastante. E o erro ficará bem difícil de encontrar pois este mesmo client exibirá o texto corretamente e só o rails entregará o dado incorreto, fazendo você acreditar que o problema está no ruby.&lt;br /&gt;&lt;br /&gt;Para evitar isso, sempre que fizer inserts na mão e de massa de dados execute como primeira linha o seguinte comando:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;charset utf8;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-3299275015450942414?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/3299275015450942414/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/07/dica-rapida-de-charset-para-projetos.html#comment-form' title='2 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3299275015450942414'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3299275015450942414'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/07/dica-rapida-de-charset-para-projetos.html' title='Dica rápida de charset para projetos rails com mysql'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-5478063970312347025</id><published>2009-07-09T04:04:00.004-03:00</published><updated>2009-07-09T04:38:38.154-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='globo.com'/><title type='text'>Globo Vídeos: Quem viu este vídeo também assistiu</title><content type='html'>É com muito orgulho que anuncio que acaba de sair para degustação uma nova funcionalidade do &lt;a href="http://video.globo.com"&gt;Globo Vídeos&lt;/a&gt;. Foi uma alteração que durou apenas um dia de desenvolvimento envolvendo o time inteiro e que ficará no ar por um tempo ainda em versão de teste. Trata-se de uma forma de ofertar vídeos baseada nas preferências de nossos usuários. Chamamos a funcionalidade de "Quem viu este vídeo também assistiu".&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_4HJqMW4KWVY/SlWcYtjrHgI/AAAAAAAAAMU/D7h20eH-XRk/s1600-h/paparazzo_menor.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 282px;" src="http://1.bp.blogspot.com/_4HJqMW4KWVY/SlWcYtjrHgI/AAAAAAAAAMU/D7h20eH-XRk/s320/paparazzo_menor.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5356359280187088386" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Como ainda está em versão "beta", para visualizar a alteração é preciso ter o addon &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/748"&gt;Grease Monkey&lt;/a&gt; instalado no seu &lt;a href="http://br.mozdev.org/firefox/download.html"&gt;Firefox&lt;/a&gt;, e instalar o &lt;a href="http://video.globo.com/Portal/videos/cda/js/globovideosrelacionadosdousuario.user.js"&gt;script que habilita a funcionalidade beta&lt;/a&gt;. Depois basta acessar o &lt;a href="http://video.globo.com"&gt;Globo Vídeos&lt;/a&gt; para poder aproveitar a nova facilidade disponível. E se possível, nos dê feedback dizendo o que achou e se encontrou algum problema na utilização. A opinião de vocês é muito importante para que a gente possa melhorar.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-5478063970312347025?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/5478063970312347025/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/07/globo-videos-quem-viu-este-video-tambem.html#comment-form' title='4 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/5478063970312347025'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/5478063970312347025'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/07/globo-videos-quem-viu-este-video-tambem.html' title='Globo Vídeos: Quem viu este vídeo também assistiu'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_4HJqMW4KWVY/SlWcYtjrHgI/AAAAAAAAAMU/D7h20eH-XRk/s72-c/paparazzo_menor.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-39006570214365387</id><published>2009-07-09T00:23:00.007-03:00</published><updated>2009-07-24T18:27:41.668-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='testes'/><title type='text'>Teste de aceitação automático com múltiplas configurações no rails</title><content type='html'>Dado que uma aplicação possua duas maneiras ou mais de funcionar de acordo com alguns parâmetros de inicialização, como proceder com os testes de aceitação automáticos sem ter que iniciar e parar o servidor entre cada cenário ou grupo de cenários? &lt;br /&gt;&lt;br /&gt;A solução simples, mas bem funcional que adotamos, foi ter uma &lt;a href="http://rake.rubyforge.org/"&gt;task rake&lt;/a&gt; para inicializar vários servidores no ambiente local, cada um com um environment e uma porta diferente. Assim, ao testar uma determinada funcionalidade com determinada configuração basta acessar o servidor naquela porta. &lt;br /&gt;&lt;br /&gt;A task rake que poderia até mesmo ser um shell script, ficou mais ou menos como mostrado abaixo. Créditos ao &lt;a href="http://pacman.blog.br"&gt;Tiago PacMan, mestre em shell,&lt;/a&gt; que fez a linha que finaliza os servidores.&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;&lt;br /&gt;desc 'Inicia servidores para teste de aceitação'&lt;br /&gt;task "server:test" do&lt;br /&gt;  system 'script/server -d -e test_conf_1 -p 3001'&lt;br /&gt;  sleep 1&lt;br /&gt;  system 'script/server -d -e test_conf_2 -p 3002'&lt;br /&gt;  sleep 1&lt;br /&gt;  system 'tail -f log/test.log'&lt;br /&gt;  system "ps aux | awk '/3001/{print $2}' | xargs kill -9"&lt;br /&gt;  sleep 1&lt;br /&gt;  system "ps aux | awk '/3002/{print $2}' | xargs kill -9"&lt;br /&gt;  sleep 1&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Repare que para este caso fizemos uma modificação no environments.rb para unificar os logs de todas os ambientes de teste. &lt;br /&gt;&lt;br /&gt;Já no ambiente de integração contínua, onde utilizamos apache com &lt;a href="http://www.modrails.com/documentation/Users%20guide.html"&gt;passenger&lt;/a&gt;, foi mais simples ainda, bastando definir para diferentes &lt;a href="http://httpd.apache.org/docs/2.0/vhosts/examples.html"&gt;VirtualHost&lt;/a&gt;s &lt;a href="http://www.modrails.com/documentation/Users%20guide.html"&gt;RailsEnv&lt;/a&gt;s diferentes.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-39006570214365387?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/39006570214365387/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/07/teste-de-aceitacao-automatico-com.html#comment-form' title='2 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/39006570214365387'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/39006570214365387'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/07/teste-de-aceitacao-automatico-com.html' title='Teste de aceitação automático com múltiplas configurações no rails'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-7047583318463976989</id><published>2009-07-09T00:01:00.004-03:00</published><updated>2009-07-09T00:22:43.665-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='apache'/><category scheme='http://www.blogger.com/atom/ns#' term='tomcat'/><title type='text'>Resolvendo o "missing uri map" no mod_jk</title><content type='html'>Esses dias instalei o &lt;a href="http://releases.ubuntu.com/9.04/"&gt;Ubuntu 9&lt;/a&gt; em meu desktop e desde então tenho preparado o ambiente de desenvolvimento de diversas aplicações legadas. Uma delas necessita do apache conectando no &lt;a href="http://tomcat.apache.org/"&gt;tomcat&lt;/a&gt; pelo &lt;a href="http://tomcat.apache.org/connectors-doc/"&gt;mod_jk&lt;/a&gt;. Após fazer a instalação de ambos e ter configurado os pontos de montagem do módulo, tudo de acordo com minha instalação antiga, deparei-me no log com o seguinte erro:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;jk_translate::mod_jk.c (3038): missing uri map for tiago.motta:/MeuPathAqui&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Descobri em uma &lt;a href="http://ibot.rikers.org/%23tomcat/20080610.html.gz"&gt;grande thread de IRC&lt;/a&gt; que o problema ocorre quando o apache possui algum &lt;a href="http://httpd.apache.org/docs/2.0/vhosts/examples.html"&gt;VirtualHost&lt;/a&gt; configurado, e que para resolver isso basta habilitar a configuração "&lt;a href="http://tomcat.apache.org/connectors-doc/reference/apache.html"&gt;JkMountCopy On&lt;/a&gt;" no &lt;a href="http://httpd.apache.org/docs/2.0/vhosts/examples.html"&gt;VirtualHost&lt;/a&gt; correspondente.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-7047583318463976989?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/7047583318463976989/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/07/resolvendo-o-missing-uri-map-no-modjk.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/7047583318463976989'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/7047583318463976989'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/07/resolvendo-o-missing-uri-map-no-modjk.html' title='Resolvendo o &quot;missing uri map&quot; no mod_jk'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-1859574118930359413</id><published>2009-06-06T05:50:00.006-03:00</published><updated>2009-06-10T15:20:34.984-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='flash'/><title type='text'>DynamicStream garantindo compatibilidade com flash 9</title><content type='html'>Ao implementar o &lt;a href="http://www.adobe.com/devnet/flashmediaserver/articles/dynstream_advanced_pt1.html"&gt;DynamicStream&lt;/a&gt; em seu flash, teoricamente pararia de funcionar em clientes com a versão 9, pois algumas das classes e métodos necessários para seu uso só estão disponíveis apartir da versão 10.&lt;br /&gt;&lt;br /&gt;Para evitar isso a &lt;a href="http://www.adobe.com"&gt;Adobe&lt;/a&gt; disponibilizou um artigo mostrando &lt;a href="http://www.adobe.com/devnet/flashmediaserver/articles/dynstream_advanced_pt3.html"&gt;como integrar o DynamicStream em um player antigo&lt;/a&gt;, para que só utilize o recurso novo quando o plugin do cliente estiver na versão 10, garantindo assim a compatibilidade. Este artigo possui até mesmo um exemplo, que embora funcione sem problemas em flash 9, quando integrado em outros projetos não funciona.&lt;br /&gt;&lt;br /&gt;O problema é um bug na classe de referência que a &lt;a href="http://www.adobe.com"&gt;Adobe&lt;/a&gt; oferece em diversos de seus artigos sobre essa funcionalidade. O erro fica evidente quando utilizamos um &lt;a href="http://www.adobe.com/support/flashplayer/downloads.html"&gt;flash player debugger&lt;/a&gt; de versão 9 sobre o swf gerado, até mesmo os de exemplo da empresa.&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;verifyError: Error #1053: Illegal override of play2 in DynamicStream.&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Esse erro ocorre porque a classe DynamicStream de referência sobrepõe o método play2 de &lt;a href="http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/net/NetStream.html"&gt;NetStream&lt;/a&gt; apenas para anulá-lo. Com isso o plugin fica obrigado a verificar a existência do método play2 em NetStream e acaba dando o erro, pois tal método só está disponível apartir da versão 10.&lt;br /&gt;&lt;br /&gt;Removendo esse método da classe DynamicStream de referência, tudo passa a funcionar, pois o acesso aos recursos existentes nas versões maiores ficam restritos ao conteudo dos métodos, que só serão avaliados em tempo de execução.&lt;br /&gt;&lt;br /&gt;Não fosse o &lt;a href="http://www.brunofms.net/"&gt;Bruno FMS&lt;/a&gt; me dar a dica do flash player debugger, perderia mais um bom tempo testando diversos artíficios para tentar fazer o código rodar em flash 9.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-1859574118930359413?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/1859574118930359413/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/06/dynamicstream-garantindo.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/1859574118930359413'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/1859574118930359413'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/06/dynamicstream-garantindo.html' title='DynamicStream garantindo compatibilidade com flash 9'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-7846618734318326474</id><published>2009-06-02T20:21:00.005-03:00</published><updated>2009-06-02T20:40:57.497-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fms'/><category scheme='http://www.blogger.com/atom/ns#' term='iptables'/><category scheme='http://www.blogger.com/atom/ns#' term='flash'/><title type='text'>Iptable para testar fallback para rtmpt no flash</title><content type='html'>Para testar se o fallback do seu flash player está funcionando corretamente, e acessando o &lt;a href="http://en.wikipedia.org/wiki/Adobe_Flash_Media_Server"&gt;flash media server&lt;/a&gt; pela porta 80 com rtmpt, basta configurar seu &lt;a href="http://en.wikipedia.org/wiki/Iptables"&gt;iptables&lt;/a&gt; para rejeitar ou deletar os pacotes da porta 1935, que é a padrão do &lt;a href="http://en.wikipedia.org/wiki/Adobe_Flash_Media_Server"&gt;FMS&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;sudo iptables -A OUTPUT -p tcp --dport 1935 -j REJECT&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;ou&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;sudo iptables -A OUTPUT -p tcp --dport 1935 -j DROP&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Sem esquecer de ao finalizar seus testes limpar o &lt;a href="http://en.wikipedia.org/wiki/Iptables"&gt;iptables&lt;/a&gt; para continuar seus testes:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;sudo iptables -F&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-7846618734318326474?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/7846618734318326474/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/06/iptable-para-testar-fallback-para-rtmpt.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/7846618734318326474'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/7846618734318326474'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/06/iptable-para-testar-fallback-para-rtmpt.html' title='Iptable para testar fallback para rtmpt no flash'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-3987661367623942772</id><published>2009-05-11T18:32:00.006-03:00</published><updated>2009-05-11T18:59:03.596-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='github'/><category scheme='http://www.blogger.com/atom/ns#' term='fakeweb'/><title type='text'>Padrões de URLs com expressões regulares no Fakeweb</title><content type='html'>Ao usarmos o &lt;a href="http://github.com/chrisk/fakeweb/tree/master"&gt;Fakeweb&lt;/a&gt; aqui &lt;a href="http://video.globo.com"&gt;onde trabalho&lt;/a&gt; encontramos a necessidade de registrar grupos de URLs de acordo com um padrão. Inspirado no &lt;a href="http://www.anselmoalves.com/"&gt;Anselmo Alves&lt;/a&gt;, colega de minha equipe que corrigiu um bug nesta mesma gem, alterei-a de forma a permitir o registro de URLs utilizando regex. &lt;br /&gt;&lt;br /&gt;Com essa alteração, se por exemplo você quiser que todas as chamadas a um determinado host sejam respondidas com uma determinada string, basta registrar como o mostrado abaixo:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;FakeWeb.register_uri(:get, /programandosemcafeina\.blogspot\.com/, :string =&gt; "Meu blog")&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;O principal desafio no desenvolvimento dessa nova funcionalidade foi não quebrar compatibilidade com o uso anterior. Dessa forma, os registros de URLs absolutas passaram a ter uma prioridade maior independente da ordem em que forem registradas. Ou seja, os retornos de acordo com as expressões regulares só são executados se a URL absoluta solicitada não estiver registrada. Veja o exemplo abaixo:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;FakeWeb.register_uri(:get, "http://programandosemcafeina.blogspot.com/search/label/rails", :string =&gt; "Label Rails")&lt;br /&gt;FakeWeb.register_uri(:get, /programandosemcafeina\.blogspot\.com/, :string =&gt; "Meu blog")&lt;br /&gt;FakeWeb.register_uri(:get, "http://programandosemcafeina.blogspot.com/search/label/ruby", :string =&gt; "Label Ruby")&lt;br /&gt;&lt;br /&gt;Net::HTTP.get(URI.parse("http://programandosemcafeina.blogspot.com/search/label/rails"))&lt;br /&gt;=&gt; "Label Rails"&lt;br /&gt;&lt;br /&gt;Net::HTTP.get(URI.parse("http://programandosemcafeina.blogspot.com/search/label/ruby"))&lt;br /&gt;=&gt; "Label Ruby"&lt;br /&gt;&lt;br /&gt;Net::HTTP.get(URI.parse("http://programandosemcafeina.blogspot.com/search/label/rmagick"))&lt;br /&gt;=&gt; "Meu blog"&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Já solicitei ao &lt;a href="http://www.shiftcommathree.com/"&gt;Chris Kampmeier&lt;/a&gt; a integração do meu fork, mas enquanto ela não é feita, você pode utilizar a gem gerada diretamente do meu &lt;a href="http://github.com/timotta"&gt;repositório no Github&lt;/a&gt;. Basta intalar da seguinte forma:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;gem sources -a http://gems.github.com&lt;br /&gt;sudo gem install timotta-fakeweb&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-3987661367623942772?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/3987661367623942772/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/05/padroes-de-urls-com-expressoes.html#comment-form' title='1 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3987661367623942772'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3987661367623942772'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/05/padroes-de-urls-com-expressoes.html' title='Padrões de URLs com expressões regulares no Fakeweb'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-3129393615715588441</id><published>2009-05-04T19:19:00.003-03:00</published><updated>2009-05-04T19:33:38.445-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='httparty'/><title type='text'>Undefined method request_uri com HTTParty</title><content type='html'>Dica rápida para quem está utilizando &lt;a href="http://httparty.rubyforge.org/"&gt;HTTParty&lt;/a&gt;: Se por um acaso você se deparar com o seguinde erro:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;undefined method `request_uri' for #&amp;lt;URI::Generic:0xb03919c&amp;gt;&lt;br /&gt;/mnt/apps/filmes/vendor/gems/httparty-0.3.1/lib/httparty/request.rb:56:in `setup_raw_request'&lt;br /&gt;/mnt/apps/filmes/vendor/gems/httparty-0.3.1/lib/httparty/request.rb:39:in `perform'&lt;br /&gt;/mnt/apps/filmes/vendor/gems/httparty-0.3.1/lib/httparty.rb:153:in `perform_request'&lt;br /&gt;/mnt/apps/filmes/vendor/gems/httparty-0.3.1/lib/httparty.rb:119:in `get'&lt;br /&gt;/mnt/apps/filmes/app/models/Filme.rb:25:in 'com_tags'&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Não se desespere, &lt;a href="http://www.ruby-doc.org/core/classes/URI/Generic.html"&gt;URI:Generic&lt;/a&gt; realmente não possui o método request_uri. O problema é na incialização do &lt;a href="http://httparty.rubyforge.org/"&gt;HTTParty&lt;/a&gt;. Se base_uri estiver nulo qualquer requisição utilizando a classe obterá esse erro. Teste você mesmo algo como:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;class A&lt;br /&gt;  include HTTParty&lt;br /&gt;  base_uri nil&lt;br /&gt;  format :xml&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;A.get('/search/label/ruby')&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Ao corrigir a inicialização de base_uri o erro deixa de acontecer:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;class A&lt;br /&gt;  include HTTParty&lt;br /&gt;  base_uri 'http://programandosemcafeina.blogspot.com'&lt;br /&gt;  format :xml&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;A.get('/search/label/ruby')&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;O problema é passível de ocorrer principalmente se base_uri for configurável de acordo com o ambiente em que a aplicação estiver rodando.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-3129393615715588441?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/3129393615715588441/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/05/undefined-method-requesturi-com.html#comment-form' title='1 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3129393615715588441'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3129393615715588441'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/05/undefined-method-requesturi-com.html' title='Undefined method request_uri com HTTParty'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-8334545390881079724</id><published>2009-04-16T17:20:00.003-03:00</published><updated>2009-04-16T17:41:26.803-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='watir'/><category scheme='http://www.blogger.com/atom/ns#' term='testes'/><title type='text'>Esperando o resultado de uma chamada assíncrona no Watir</title><content type='html'>Em alguns casos quando uma determinada requisição assíncrona demora para retornar, o teste de aceitação implementado com Watir pode falhar. Principalmente quando os testes estão rodando em alguma ferramenta de integração contínua. &lt;br /&gt;&lt;br /&gt;Para evitar essa falha irritante, o usual é colocar alguns sleeps após as ações que disparam as chamadas ajax. Contudo, isso não garante que o teste não vá falhar, especialmente se o sleep for baixo. Em contrapartida se forem colocado muitos sleeps altos poderá haver uma demora muito grande para rodar todos os testes.&lt;br /&gt;&lt;br /&gt;Uma solução possível é esperar que um determinado elemento html seja inserido ou removido da página para então continuar executando os testes. Para isso implementei um método genérico que recebe um bloco de verificação. Veja o exemplo de uso dele:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;espera { @browser.text.include? 'Ajax retornado' }&lt;/pre&gt;&lt;/div&gt; &lt;br /&gt;O código do método é bem simples e está descrito abaixo. Ele recebe um bloco assumindo que quando este retornar verdadeiro significa que a espera deve terminar, caso contrário ele continuará esperando e verificando, com um timeout de 30 segundos.&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;def espera&lt;br /&gt;  Timeout::timeout(30) do&lt;br /&gt;    while not yield&lt;br /&gt;      sleep 1 &lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-8334545390881079724?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/8334545390881079724/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/04/esperando-o-resultado-de-uma-chamada.html#comment-form' title='4 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/8334545390881079724'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/8334545390881079724'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/04/esperando-o-resultado-de-uma-chamada.html' title='Esperando o resultado de uma chamada assíncrona no Watir'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-3109971901858543183</id><published>2009-03-16T13:50:00.001-03:00</published><updated>2009-03-16T20:07:15.586-03:00</updated><title type='text'>Nomes de métodos e variáveis devem ser no idioma do cliente</title><content type='html'>Recentemente Carlos Brando escreveu um excelente artigo em seu blog sobre &lt;a href="http://www.nomedojogo.com/2009/02/13/rails-way-3-nomes-de-metodos-e-variaveis-devem-ser-obvios/"&gt;nomes de métodos e variáveis&lt;/a&gt;. Dentre as diversas dicas, uma eu não concordei, comentei lá sobre isso, e o argumento de resposta não me foi convincente.&lt;br /&gt;&lt;br /&gt;O ponto de discordância dizia respeito a necessidade de evitar código bilingue. Segundo ele, já que a maioria das linguagens de programação são em inglês, nada mais correto do que escrever o nome das classes e metódos também em inglês. Para começar vou contar uma pequena historinha:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Experiência própria: Laboratório geológico&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Há um bom tempo atrás trabalhei em uma empresa que desenvolvia um sistema para um laboratório geológico. Um dia, um dos desenvolvedores do time ligou para tirar duvidas com o cliente e fez a seguinte pergunta "Quando um sample alcança um result no segundo stage em determinado job, ele pode parar os próximos stages ou deve seguir até a conclusão de todos". &lt;br /&gt;&lt;br /&gt;O rapaz na outra ponta ficou completamente confuso. Era novo no laboratório, conhecia toda cadeia de análises de amostras mas não conhecia os estranhos termos que a equipe de desenvolvimento vira e mexe falava. Sample, result e stage não eram palavras de domínio do laboratório, e sim os seus similares em português. Mas como o sistema estava sendo desenvolvido com os termos em inglês, constantemente os membros do time de desenvolvimento, eu incluso, os citava, atrapalhando a comunicação e o entendimento dos problemas. &lt;br /&gt;&lt;br /&gt;Eis então a pergunta: Será que um código que mistura termos em inglês e português causa mais problemas que os ruídos na comunicação com o cliente? Pode-se alegar que o código nem será mostrado ao usuário, mas na hora em que o desenvolvedor precisa se comunicar com o cliente, na mente dele ele não está trabalhando com amostras, ele está trabalhando com samples.&lt;br /&gt;&lt;br /&gt;Mas o mais interessante é que dentre o domínio do laboratório geológico, um item era em inglês, o Job. Eles falavam amostras, estágio, resultados, mas falavam Job. Isso então nos leva a uma conclusão.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;O idioma como ferramenta&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;A grande questão é que o idioma é apenas uma ferramenta para a comunicação. O primeiro item do manifesto ágil fala "Indivíduos e interação entre eles mais que processos e ferramentas". No caso, acredito que a interação entre os individuos, no caso a comunicação, é mais importante do que a ferramenta, que no caso é o idioma. Portanto eu prefiro priorizar a comunicação independente do idioma. Se meu cliente possuir termos em seu negócio em francês e outros em alemão, eu prefiro utilizá-los no meu sistema para que ao conversar com ele eu não me confunda com os termos usados na programação.&lt;br /&gt;&lt;br /&gt;Um contra-argumento muito usado é dizer que utilizar termos em português em uma linguagem de programação em inglês torna o código sujo. Mas será que isso é realmente verdade?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Código limpo depende do idioma?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Antes de discutir é preciso definir o que é um código limpo. Em minha concepção código limpo é aquele fácil de entender. As técnicas apresentadas pelo Carlos Brando são realmente importantes. Mas será que o seguinte código é complicado de entender?&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;amostras = Amostra.obter_amostras_com_resultado_positivo&lt;br /&gt;amostras.each do |amostra|&lt;br /&gt;  puts amostra.produto.sigla&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Será que o fato de utilizarmos inglês misturado com o português tornou mais dificil o seu entendimento? Eu não só creio que não, como acredito que por conta das expressões utilizadas pelo cliente serem idênticas ao do programa, o entendimento fica mais fácil ainda.&lt;br /&gt;&lt;br /&gt;Dizer o contrário é o mesmo que defender o uso de arcabouço e chamada de retorno ao invés de framework e callback, em trabalhos acadêmicos somente por uma questão de purismo idiomático. É nesse público que políticos como o deputado &lt;a href="http://arquivoetc.blogspot.com/2007/12/europa-decide-reduzir-poluio-de-veculos.html"&gt;Aldo Rebelo&lt;/a&gt; se mira, tentando aprovar &lt;a href="http://arquivoetc.blogspot.com/2007/12/europa-decide-reduzir-poluio-de-veculos.html"&gt;projetos de lei perigosos&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Mas e o mercado internacional?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Outro ponto levantado para defender o uso de termos diferentes dos do domínio do cliente é a possibilidade de venda do software ao exterior em algum remoto dia. Ao meu entender isso é um pouco de síndrome do mapa de calor. Um problema comum em empresas de desenvolvimento que não seguem o manifesto ágil. Este tipo de argumento futurista era muito utilizado para defender que aplicações fossem escritas em Java. Coisas do tipo "E se um dia quisermos colocar esse software numa geladeira".&lt;br /&gt;&lt;br /&gt;O interessante é que na resposta que recebi, foi citado que para não haver confusão nas conversas com o cliente poderíamos fazer as especificações em português. Mas oras, quando um software é vendido, as especificações vão juntas. Se for vendido para o exterior teríamos o mesmo problema. E pior ainda, as especificações, que dizem exatamente o que o sistema faz estariam em uma lingua diferente da do cliente.&lt;br /&gt;&lt;br /&gt;O fato é que a possibilidade de se vender um sistema ao exterior em algum futuro remoto não deve se tornar um empecilho na comunicação com o cliente local, que é quem vai gerar receita mais brevemente. Se um dia realmente a venda for uma possibilidade real faz-se um refactory. &lt;br /&gt;&lt;br /&gt;Só para constar, a desculpa para desenvolver o sistema exemplificado no ínicio do post em inglês foi exatamente esta. Hoje, 6 anos depois o foco da empresa mudou totalmente, nenhum outro sistema de laboratório foi vendido ou criado. Somente este continua em manutenção. Dos outros que desenvolvi em Java nenhum precisou trocar de sistema operacional ou mesmo ser instalado em geladeiras.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Enfim a conclusão&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;O que defendo não é programar em um idioma e não em outro. Defendo que deve-se programar o mais próximo da realidade. Se o seu cliente tiver todos os termos do seu negócio em inglês, ótimo, programe em inglês. Fora isso, vejo apenas dois outros motivos para basear sua programação no idioma britânico: O software que você está desenvolvendo é uma biblioteca ou framework open-source; Ou a maioria dos membros da sua equipe só fala inglês.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-3109971901858543183?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/3109971901858543183/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/03/nomes-de-metodos-e-variaveis-devem-ser.html#comment-form' title='21 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3109971901858543183'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3109971901858543183'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/03/nomes-de-metodos-e-variaveis-devem-ser.html' title='Nomes de métodos e variáveis devem ser no idioma do cliente'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>21</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-2649939497965472419</id><published>2009-03-12T21:54:00.011-03:00</published><updated>2009-04-27T18:13:36.163-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bash'/><category scheme='http://www.blogger.com/atom/ns#' term='git'/><title type='text'>Alias e functions para seu dia-a-dia com git</title><content type='html'>Embora o &lt;a href="http://www.akitaonrails.com/2008/4/3/micro-tutorial-de-git"&gt;Git&lt;/a&gt; seja uma poderosa ferramenta de controle de versão, a quantidade de passos que é necessário para executar alguns procedimentos básicos pode dificultar um pouco o dia-a-dia do desenvolvedor. Principalmente se o projeto em questão não requer todas as funcionalidades que o &lt;a href="http://www.akitaonrails.com/2008/4/3/micro-tutorial-de-git"&gt;git&lt;/a&gt; oferece para compartilhar código entre grandes equipes espalhadas. &lt;br /&gt;&lt;br /&gt;Felizmente é possível criar alguns &lt;a href="http://www.vivaolinux.com.br/dica/Criando-um-alias-(atalho)-no-bash-Slackware-10.2/"&gt;alias&lt;/a&gt; e &lt;a href="http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO-8.html"&gt;functions&lt;/a&gt; para facilitar o seu uso. Dessa forma podemos utilizar o &lt;a href="http://www.akitaonrails.com/2008/4/3/micro-tutorial-de-git"&gt;git&lt;/a&gt; de maneira mais simples, e quando houver a necessidade de usar um dos seus recursos avançados, basta não usar esses atalhos. Segue abaixo os que tenho utilizado:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;gitshazam&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Adiciona todos os arquivos criados, modificados, deletados e depois comita para o repositório local com a mensagem informada.&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;alias gitshazam='git add -u &amp;&amp; git add . &amp;&amp; git commit -m'&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Exemplo de uso:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;gitshazam 'Minha mensagem de commit'&lt;br /&gt;git push&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;gitcoleradodragao&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Sincroniza com o repositório remoto as informações dos branchs existentes e os exibe, mostrando também em que branch você está desenvolvendo atualmente.&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;alias gitcoleradodragao='git fetch &amp;&amp; git branch -a -v'&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;gitpodediamante&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Cria um branch local com o nome informado e o associa ao branch de mesmo nome no repositório remoto.&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;function gitpodediamante() { git checkout --track -b $1 origin/$1; }&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Exemplo de uso:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;gitpodediamante 'historia-22'&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;gitmeteorodepegasus&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Baixa as ultimas atualizações no master e no branch indicado no parâmetro, e posteriormente faz o merge do master dentro deste branch.&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;function gitmeteorodepegasus() { git checkout master;git pull;git checkout "$1"; git merge master; }&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Exemplo de uso:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;gitmeteorodepegasus 'historia-22'&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Agradecimentos especiais à galera da &lt;a href="http://video.globo.com"&gt;minha equipe&lt;/a&gt; que tem me ajudado bastante com o git e aguentado minhas reclamações.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-2649939497965472419?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/2649939497965472419/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/03/alias-e-functions-para-seu-dia-dia-com.html#comment-form' title='5 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/2649939497965472419'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/2649939497965472419'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/03/alias-e-functions-para-seu-dia-dia-com.html' title='Alias e functions para seu dia-a-dia com git'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-236134438507398054</id><published>2009-02-26T17:27:00.000-03:00</published><updated>2009-02-26T17:28:06.556-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='testes'/><category scheme='http://www.blogger.com/atom/ns#' term='duck typing'/><title type='text'>Duck typing e os testes de aceitação automáticos</title><content type='html'>Uma discussão frequente em minha equipe é sobre maneiras de garantir que nossos sistemas estejam funcionando adequadamente por meio de testes automáticos. Em nossos ultimos projetos temos conseguido alcançar 100% de cobertura de testes unitários, e nos sistemas legados temos aumentado a cobertura aos poucos. &lt;br /&gt;&lt;br /&gt;No entanto, estamos cientes de que nem 100% de cobertura de testes unitários garantem a ausência de bugs. Isso nos remete aos testes de aceitação automáticos, não só por causa do funcionamento das telas e fluxos de navegação, mas também dos possíveis problemas que um refactoring com um pouco menos de atenção pode causar em uma linguagem baseada em &lt;a href="http://en.wikipedia.org/wiki/Duck_typing"&gt;duck typing&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Darei um exemplo em &lt;a href="http://www.ruby-lang.org"&gt;ruby&lt;/a&gt; para ilustrar um bug que pode ser introduzido sem que seus testes unitários percebam. Digamos que você tenha as seguintes classes:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;class Filme &amp;lt; ActiveRecord::Base do&lt;br /&gt;  def alugar&lt;br /&gt;    self.estoque.remover self&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;class Estoque &amp;lt; ActiveRecord::Base do&lt;br /&gt;  def remover(filme)&lt;br /&gt;    # ...&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;No seu &lt;a href="http://rspec.info/"&gt;spec&lt;/a&gt; que verifica o método &lt;strong&gt;alugar&lt;/strong&gt; da classe &lt;strong&gt;Filme&lt;/strong&gt; você colocaria um &lt;a href="http://en.wikipedia.org/wiki/Mock_object"&gt;mock&lt;/a&gt; mais ou menos como mostrado abaixo:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;@estoque = mock_model(Estoque)&lt;br /&gt;@estoque.should_receive(:remover).with(@filme).once&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Um dia você percebe que o nome do método &lt;strong&gt;remover&lt;/strong&gt; não está explicando muito bem o significado, e resolve fazer um refactoring renomeando-o para &lt;strong&gt;remover_fita_do_filme&lt;/strong&gt;. &lt;br /&gt;&lt;br /&gt;Você altera os testes da classe &lt;strong&gt;Estoque&lt;/strong&gt;, renomeia o método e logo depois recebe uma ligação de uma empresa de telefonia te oferecendo serviços excepcionais que você nunca precisou. Puto da vida você desliga o telefone, roda os testes e todos passam! Serviço feito!&lt;br /&gt;&lt;br /&gt;Perceberam o problema? Sem um teste de aceitação automático ou no mínimo um teste manual, você não perceberia que a classe &lt;span style="font-weight:bold;"&gt;Filme&lt;/span&gt; continua referenciando o método antigo, &lt;strong&gt;remover&lt;/strong&gt;, da classe &lt;strong&gt;Estoque&lt;/strong&gt;. Esse tipo de problema não passaria em uma linguagem que não segue &lt;a href="http://en.wikipedia.org/wiki/Duck_typing"&gt;duck typing&lt;/a&gt;, pois a etapa de verificação de código da compilação serviria como uma espécie de teste unitário de tipos e nomes.&lt;br /&gt;&lt;br /&gt;Um problema mais evidente seria um método que recebe um objeto que precisa ter determinado método, ou seja implementar determinada interface. Veja o exemplo abaixo, que mostra que ao alugar um item, no caso um &lt;span style="font-weight:bold;"&gt;Filme&lt;/span&gt;, adiciona-se todas as &lt;span style="font-weight:bold;"&gt;tags&lt;/span&gt; dele às tags preferenciais do cliente:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;class Cliente &amp;lt; ActiveRecord::Base do&lt;br /&gt;  def alugar(item)&lt;br /&gt;    #...&lt;br /&gt;    adiciona_tags_preferenciais item.tags&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;class Filme &amp;lt; ActiveRecord::Base do&lt;br /&gt;  def tags&lt;br /&gt;    # ...&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Se mudarmos o nome do método &lt;span style="font-weight:bold;"&gt;tags&lt;/span&gt; em &lt;span style="font-weight:bold;"&gt;Filme&lt;/span&gt; e também corrigirmos o uso de dele no método &lt;span style="font-weight:bold;"&gt;alugar&lt;/span&gt; da classe &lt;span style="font-weight:bold;"&gt;Cliente&lt;/span&gt; tudo funcionará bem. Mas, como usamos &lt;a href="http://en.wikipedia.org/wiki/Duck_typing"&gt;duck typing&lt;/a&gt; não temos como garantir que outros objetos, digamos alugáveis, tenham sido alterados. Se por exemplo esta locadora também alugas livros, teríamos um erro evidente:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;class Cliente &amp;lt; ActiveRecord::Base do&lt;br /&gt;  def alugar(item)&lt;br /&gt;    #...&lt;br /&gt;    adiciona_tags_preferenciais item.tags_principais&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;class Filme &amp;lt; ActiveRecord::Base do&lt;br /&gt;  def tags_principais&lt;br /&gt;    # ...&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;class Livro &amp;lt; ActiveRecord::Base do&lt;br /&gt;  def tags&lt;br /&gt;    # ...&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Esse tipo de erro só seria pego num teste manual se lembrássemos de testar o aluguel de livros também. Por isso é tão importante o teste de aceitação automático em uma linguagem com duck typing. Em &lt;a href="http://java.sun.com"&gt;Java&lt;/a&gt; por exemplo, que a &lt;a href="http://java.sun.com/docs/books/tutorial/java/concepts/interface.html"&gt;interface&lt;/a&gt; precisa ser explícita, o erro seria pego no que poderíamos considerar o teste unitário que a compilação executa. As classes &lt;span style="font-weight:bold;"&gt;Filme&lt;/span&gt; e &lt;span style="font-weight:bold;"&gt;Livro&lt;/span&gt; implementariam a interface &lt;span style="font-weight:bold;"&gt;Alugavel&lt;/span&gt; por exemplo:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;public interface Alugavel {&lt;br /&gt;  List&lt;Tag&gt; tagsPrincipais();&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Mas é claro que o uso de uma liguagem menos dinâmica não remove a necessidade de testes de aceitação automáticos. O valor desse tipo de testes é independente da linguagem, e sua implementação é importantíssima para garantir fluxos de navegação e integração entre os diversos componentes do sistema.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-236134438507398054?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/236134438507398054/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/02/duck-typing-e-os-testes-de-aceitacao.html#comment-form' title='5 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/236134438507398054'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/236134438507398054'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/02/duck-typing-e-os-testes-de-aceitacao.html' title='Duck typing e os testes de aceitação automáticos'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-8413940232557766293</id><published>2009-02-19T18:01:00.005-03:00</published><updated>2009-02-19T18:17:56.091-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='github'/><title type='text'>Plugin do rails para copiar erros de um model para outro</title><content type='html'>Em um projeto pessoal precisei desenvolver uma maneira de copiar os erros de um model para outro. Como é uma funcionalidade que outrora já havia precisado, aproveitei para criar um plugin e disponibilizá-lo para quem mais tiver esse mesmo problema.&lt;br /&gt;&lt;br /&gt;O plugin está disponível no GitHub pelo endereço &lt;a href="http://github.com/timotta/copy_errors_from/tree/master"&gt;http://github.com/timotta/copy_errors_from/tree/master&lt;/a&gt;. Para instalar no seu projeto basta rodar a seguinte linha:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;script/plugin install git://github.com/timotta/copy_errors_from.git&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Após instalar todos os seus models terão o método &lt;span style="font-weight:bold;"&gt;copy_errors_from&lt;/span&gt;, que pode ser utilizado como mostrado abaixo:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;&amp;gt; filme = Filme.new :titulo =&gt; 'Corra que a polícia vem aí'&lt;br /&gt;&amp;gt; ator = Ator.new&lt;br /&gt;&amp;gt; filme.atores.push ator&lt;br /&gt;&amp;gt; filme.save #return false&lt;br /&gt;&amp;gt; filme.errors.entries #return []&lt;br /&gt;&amp;gt; ator.errors.entries #return [['nome','Não pode ser vazio']]&lt;br /&gt;&amp;gt; filme.copy_errors_from ator&lt;br /&gt;&amp;gt; filme.errors.entries #return [['ator_nome','Não pode ser vazio']]&lt;br /&gt;&amp;gt; filme.errors.on(:ator_nome) #return 'Não pode ser vazia'&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-8413940232557766293?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/8413940232557766293/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/02/plugin-do-rails-para-copiar-erros-de-um.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/8413940232557766293'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/8413940232557766293'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/02/plugin-do-rails-para-copiar-erros-de-um.html' title='Plugin do rails para copiar erros de um model para outro'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-6880616993936664624</id><published>2009-01-23T10:02:00.013-03:00</published><updated>2009-01-23T10:28:46.847-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='cache'/><title type='text'>Cuidado ao cachear named_scope no Rails</title><content type='html'>Deve-se tomar cuidado ao cachear o acesso a um &lt;a href="http://railscasts.com/episodes/108"&gt;named_scope&lt;/a&gt; pois ele tem um carregamento de forma preguiçosa, o famoso lazy load. Assim, o código abaixo gravaria no cache não os atores femininos, mas sim todos os atores.&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;class Filme&lt;br /&gt;    #...&lt;br /&gt;  def atores_femininos_cacheado&lt;br /&gt;    Rails.cache.fetch("#{self.id}:atores-principais") { self.atores.femininos }&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;class Ator&lt;br /&gt;  #...&lt;br /&gt;  named_scope :femininos, :conditions =&gt; { :sexo =&gt; 'F' }&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Se você observar o log, verá que o &lt;a href="http://rubyonrails.org/"&gt;rails&lt;/a&gt; executará a query sem as condições definidas na classe &lt;span style="font-weight:bold;"&gt;Ator&lt;/span&gt; para o named_scope &lt;span style="font-weight:bold;"&gt;:femininos&lt;/span&gt;. E que após ser cacheado, será essa a query que deixará de ser executada. &lt;br /&gt;&lt;br /&gt;A query para buscar atores femininos do &lt;a href="http://railscasts.com/episodes/108"&gt;named_scope&lt;/a&gt; só será realmente executada quando algum método do objeto retornado pelo método &lt;span style="font-weight:bold;"&gt;.atores.femininos&lt;/span&gt; for chamado. Veja no exemplo abaixo:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;@filme = Filme.find_by_id 10&lt;br /&gt;filme.atores_femininos_cacheado.last&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Ou seja, o cacheamento não está servindo ao propósito definido. Uma solução para isso é cachear diretamente o resultado de um find, como pode ser visto abaixo:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;class Filme&lt;br /&gt;    #...&lt;br /&gt;  def atores_femininos_cacheado&lt;br /&gt;    Rails.cache.fetch("#{self.id}:atores-principais") do&lt;br /&gt;      Ator.find_all_by_filme_id self, :conditions =&gt; { :sexo =&gt; 'F' }&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-6880616993936664624?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/6880616993936664624/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/01/cuidado-ao-cachear-namedscope-no-rails.html#comment-form' title='2 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/6880616993936664624'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/6880616993936664624'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/01/cuidado-ao-cachear-namedscope-no-rails.html' title='Cuidado ao cachear named_scope no Rails'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-2287945968429512849</id><published>2009-01-21T10:58:00.005-03:00</published><updated>2009-01-21T12:01:52.852-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='heroku'/><title type='text'>PGError com Rails no Heroku</title><content type='html'>Essa dica é para quem utiliza o &lt;a href="http://heroku.com"&gt;Heroku Garden&lt;/a&gt; para desenvolver suas aplicações &lt;a href="http://rubyonrails.org/"&gt;rails&lt;/a&gt;. Ao rodar os &lt;a href="http://rspec.info/"&gt;specs&lt;/a&gt; diretamente da interface web do &lt;a href="http://heroku.com"&gt;Heroku&lt;/a&gt; pode ser que um dia você se depare com o seguinte erro:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;PGError: ERROR: relation "filmes" does not exist&lt;br /&gt;: SELECT a.attname, format_type(a.atttypid, a.atttypmod),&lt;br /&gt;d.adsrc, a.attnotnull&lt;br /&gt;FROM pg_attribute a LEFT JOIN pg_attrdef d&lt;br /&gt;ON a.attrelid = d.adrelid AND a.attnum = d.adnum&lt;br /&gt;WHERE a.attrelid = 'filmes'::regclass&lt;br /&gt;AND a.attnum &gt; 0 AND NOT a.attisdropped&lt;br /&gt;ORDER BY a.attnum&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;O erro acontece porque o link para rodar as &lt;a href="http://www.caironoleto.com/2008/09/23/traducao-rails-database-migrations-parte-i/"&gt;migrations&lt;/a&gt;, que aparece na interface do &lt;a href="http://heroku.com"&gt;heroku&lt;/a&gt;, não as executa no ambiente de testes. Portanto, é preciso abrir a tela do rake e executar as seguintes linhas antes de rodar seus &lt;a href="http://rspec.info/"&gt;specs&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;db:migrate&lt;br /&gt;db:test:prepare&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;UPDATE&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Descobri outro problema do &lt;a href="http://heroku.com"&gt;Heroku&lt;/a&gt;. O schema.rb é criado e alterado no diretório &lt;span style="font-weight:bold;"&gt;tmp&lt;/span&gt;. Dessa forma para que o PGError não dê novamente em outra atualização do banco, você precisa copiá-lo para o diretório &lt;span style="font-weight:bold;"&gt;db&lt;/span&gt; e então no console do rake rodar:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;db:test:clone&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-2287945968429512849?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/2287945968429512849/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/01/pgerror-com-rails-no-heroku.html#comment-form' title='1 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/2287945968429512849'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/2287945968429512849'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2009/01/pgerror-com-rails-no-heroku.html' title='PGError com Rails no Heroku'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-2256019094173055016</id><published>2008-10-09T09:09:00.003-03:00</published><updated>2008-10-09T09:29:26.255-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='captcha'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='rmagick'/><title type='text'>Imagem de fundo no captcha gerado pelo simple_captcha</title><content type='html'>O &lt;a href="http://expressica.com/simple_captcha/"&gt;simple_captcha&lt;/a&gt;, plugin de geração de captcha para &lt;a href="http://www.rubyonrails.org/"&gt;rails&lt;/a&gt;, não possui opção para colocar um imagem de fundo na imagem gerada. Mas fazer isso não é nenhum bicho de sete cabeças, basta alterar algumas partes chaves do plugin utilizando um pouco de &lt;a href="http://www.imagemagick.org/RMagick/doc/"&gt;rmagick&lt;/a&gt;. Vou mostrar aqui mais ou menos como fiz isso em um recente projeto.&lt;br /&gt;&lt;br /&gt;No arquivo simple_captcha_image.rb basta alterar o método generate_simple_captcha_image para que na criação da imagem seja colocado o fundo desejado. Veja o exemplo de código abaixo:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;def generate_simple_captcha_image(options={})  #:nodoc&lt;br /&gt;&lt;br /&gt;  fundo = Magick::Image.read(url_da_imagem_de_fundo).first&lt;br /&gt;  preenchimento = Magick::TextureFill.new(fundo)&lt;br /&gt;      &lt;br /&gt;  @image = Magick::Image.new(197, 45, preenchimento) do &lt;br /&gt;    self.format = 'JPG'&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  # ...&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Repare que existe ali uma chamada ao método url_da_imagem_de_fundo que no caso é um método que pega o path de uma imagem aleatória. Dessa forma o captcha poderá ter vários fundos diferentes. Veja um exemplo de como pode ser a implementação desse método:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;  def url_da_imagem_de_fundo&lt;br /&gt;    "#{RAILS_ROOT}/vendor/plugins/simple_captcha/assets/imgs/picture_" + (rand(9)+1).to_s + ".jpg"&lt;br /&gt;  end&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Foi preciso também comentar a chamada ao método que aplica estilo e distorção à imagem, mas não sei bem se isso é necessário. Como fiz isso, precisei alterar o método append_simple_captcha_code que escreve o texto na imagem, de forma a utilizar diversos tamanhos, posições e fontes diferentes.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-2256019094173055016?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/2256019094173055016/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/10/imagem-de-fundo-no-captcha-gerado-pelo.html#comment-form' title='3 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/2256019094173055016'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/2256019094173055016'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/10/imagem-de-fundo-no-captcha-gerado-pelo.html' title='Imagem de fundo no captcha gerado pelo simple_captcha'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-5368985544199702574</id><published>2008-10-08T19:30:00.004-03:00</published><updated>2008-10-08T19:53:03.708-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>Validação no lado cliente com form_remote_tag</title><content type='html'>Para fazer validações de formulário no lado cliente basta informar ao método &lt;a href="http://api.rubyonrails.com/classes/ActionView/Helpers/FormTagHelper.html"&gt;form_tag&lt;/a&gt; ou ao &lt;a href="http://api.rubyonrails.com/classes/ActionView/Helpers/FormHelper.html"&gt;form_for&lt;/a&gt;, utilizando o  parâmetro &lt;span style="font-weight:bold;"&gt;:onsubmit&lt;/span&gt;, o código javascript que se deseja executar para validá-lo. Veja no exemplo:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;  &amp;lt;% form_tag "/entrar", :onsubmit =&gt; 'return valida(this)' do %&amp;gt;&lt;br /&gt;      &amp;lt;h1&amp;gt;ok&amp;lt;/h1&amp;gt;&lt;br /&gt;  &amp;lt;% end %&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Contudo para a tag &lt;a href="http://api.rubyonrails.org/classes/ActionView/Helpers/PrototypeHelper.html"&gt;form_remote_tag&lt;/a&gt; e &lt;a href="http://api.rubyonrails.org/classes/ActionView/Helpers/PrototypeHelper.html"&gt;form_remote_for&lt;/a&gt; não há a possibilidade de utilizar o parâmetro &lt;span style="font-weight:bold;"&gt;:onsubmit&lt;/span&gt;. Isso porque o formulário gerado pelo &lt;a href="http://www.rubyonrails.org/"&gt;rails&lt;/a&gt; já possui a ação onsubmit definida com o código que fará o acesso remoto. Para colocar então sua validação antes deste código deve-se usar o parâmetro &lt;span style="font-weight:bold;"&gt;:before&lt;/span&gt; como é mostrado no exemplo:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;  &amp;lt;% form_remote_tag :url =&gt; "/entrar", &lt;br /&gt;        :update =&gt; 'meu_div', &lt;br /&gt;        :before =&gt; 'if( !valida(this) ) return false' do %&amp;gt;&lt;br /&gt;      &amp;lt;h1&amp;gt;ok&amp;lt;/h1&amp;gt;&lt;br /&gt;  &amp;lt;% end %&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Repare que é importante que haja o "return false" no caso de não ter sido validado, caso contrário ele executará o restante do código colocado pelo rails no onsubmit.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-5368985544199702574?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/5368985544199702574/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/10/validacao-no-lado-cliente-com.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/5368985544199702574'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/5368985544199702574'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/10/validacao-no-lado-cliente-com.html' title='Validação no lado cliente com form_remote_tag'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-3790245567070504619</id><published>2008-10-02T22:48:00.004-03:00</published><updated>2008-10-02T23:29:11.429-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='ajax'/><title type='text'>Impedindo redeclaração de objetos javascript</title><content type='html'>Ao importar duas vezes um mesmo arquivo .js em uma página, as funções, protótipos e objetos são redeclarados. Se for necessário que um determinado objeto mantenha seu estado durante as iterações assíncronas dessa página, perde-se este estado na segunta importação do arquivo .js.&lt;br /&gt;&lt;br /&gt;Veja por exemplo o objeto abaixo, que nada mais é que um repositório de outros objetos. Tá ta ta, não é um exemplo que tenha tanta utilidade, mas vá lá, é só pra explicar o mecanismo.&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;var Repositorio = {&lt;br /&gt;    sequencia:0,&lt;br /&gt;    lista:[],&lt;br /&gt;    registrar:function(obj) {&lt;br /&gt;        var id = ++this.sequencia;&lt;br /&gt;        this.lista[ id ] = obj;&lt;br /&gt;        return id;&lt;br /&gt;    },   &lt;br /&gt;    recuperar:function(id) {&lt;br /&gt;        return this.lista[ id ];&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;Agora estando esse código num arquivo por exemplo &lt;span style="font-weight:bold;"&gt;repositorio.js&lt;/span&gt; teríamos o problema relatado com o código a seguir:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;&amp;lt;script src="repositorio.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&amp;lt;script&amp;gt;&lt;br /&gt;    Repositorio.registrar( new Object() );&lt;br /&gt;    Repositorio.registrar( new Object() );&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&amp;lt;script src="repositorio.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&amp;lt;script&amp;gt;&lt;br /&gt;    alert( Repositorio.recuperar( 1 ) );&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;/pre&gt;&lt;/div&gt;Para impedir essa redeclaração do objeto, e os possíveis erros que ela pode causar, basta tentar utilizá-lo dentro de um bloco try e fazer sua declaração dentro do bloco catch. Ou seja, ele só será declarado caso haja um erro na tentativa de usá-lo. Veja:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;try{ Repositorio.existo(); }&lt;br /&gt;catch(e) {&lt;br /&gt;    Repositorio = {&lt;br /&gt;        sequencia:0,&lt;br /&gt;        lista:[],&lt;br /&gt;        existo:function(){},&lt;br /&gt;        registrar:function(obj) {&lt;br /&gt;            var id = ++this.sequencia;&lt;br /&gt;            this.lista[ id ] = obj;&lt;br /&gt;            return id;&lt;br /&gt;        },   &lt;br /&gt;        recuperar:function(id) {&lt;br /&gt;            return this.lista[ id ];&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;Mas aí você poderia me dizer: Pô pra que tudo isso, basta não importar duas vezes o javascript. Acontece que se você está criando uma biblioteca para ser distribuída para diversos sites e blogs, você não deve subestimar a "criatividade" dos utilizadores dela. Então, o melhor é se precaver.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-3790245567070504619?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/3790245567070504619/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/10/impedindo-redeclarao-de-objetos.html#comment-form' title='2 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3790245567070504619'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3790245567070504619'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/10/impedindo-redeclarao-de-objetos.html' title='Impedindo redeclaração de objetos javascript'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-6894444020692217789</id><published>2008-09-18T19:58:00.006-03:00</published><updated>2008-09-27T16:05:01.529-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='thread'/><category scheme='http://www.blogger.com/atom/ns#' term='pattern'/><title type='text'>Cópia exclusiva de coleção</title><content type='html'>Por serem de díficil identificação, problemas de concorrência acabam indo pra produção sem sequer serem notado em ambiente de testes e desenvolvimento. Erros acabam ocorrendo de maneira intermitente, &lt;a href="/2007/05/load-alto-causado-por-um-hashmap.html"&gt;causando dores de cabeça&lt;/a&gt; e acessos de insanidade nos desenvolvedores. &lt;a href="http://martinfowler.com/"&gt;Martin Fowler&lt;/a&gt; em seu livro &lt;a href="http://martinfowler.com/eaaCatalog/"&gt;Patterns of Enterprise Application Architecture&lt;/a&gt; identifica algumas soluções para evitar problemas de concorrência relativos a integridade de dados. Contudo há um outro problema que não está catalogado neste livro: A concorrência de informações na memória.&lt;br /&gt;&lt;br /&gt;Ao guardar objetos em uma coleção que é compartilhada por outras threads, é necessário tomar providências para que não sejam lançadas exceções inesperadas. A medida básica é garantir que os pontos de acesso às coleções compartilhadas devem obter exclusividade sobre seu uso com a diretiva &lt;a href="http://www.janeg.ca/scjp/threads/synchronized.html"&gt;synchronized&lt;/a&gt;. Veja abaixo um exemplo de classe que torna o uso de uma lista à prova de erros de concorrência:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;public class ListaSegura {&lt;br /&gt;    private List lista = new ArrayList();&lt;br /&gt;    public void adiciona(Object objeto) {&lt;br /&gt;        synchronized (lista) {&lt;br /&gt;            lista.add(objeto);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    public void remove(Object objeto) {&lt;br /&gt;        synchronized (lista) {&lt;br /&gt;            lista.remove(objeto);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    public void listar() {&lt;br /&gt;        synchronized (lista) {&lt;br /&gt;            for (Object o : lista) {&lt;br /&gt;                System.out.println(o);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;É uma solução simples e que resolve em parte o problema. Contudo na maioria das vezes não podemos obter a exclusividade para iterar sobre uma coleção. No caso mostrado acima isso é possível pois estamos apenas imprimindo o objeto. Mas existem alguns casos em que obter essa exclusividade para a iteração nos traria alguns problemas. Esses casos estão descritos abaixo:&lt;br /&gt;&lt;br /&gt;1- &lt;span style="font-weight:bold;"&gt;Alteração da coleção&lt;/span&gt;: No caso de você iterar pela coleção para remover ou adicionar algum objeto a ela. A exclusão ou adição ocorreria no meio da iteração e com isso seria lançada uma exceção de concorrência.Um exemplo simples disso são classes que gerenciam cache e precisam periodicamente remover objetos expirados.&lt;br /&gt;&lt;br /&gt;2- &lt;span style="font-weight:bold;"&gt;Iteração prolongada&lt;/span&gt;: Quando a coleção possui objetos demais ou o processo executado durante a iteração é lento, tornando o tempo de exclusividade total muito longo. Isso faria com que o restante do sistema que precisasse utilizar essa coleção ficasse muito tempo aguardando pela exclusividade terminar. Um exemplo comum é a execução de métodos que acessem banco de dados dentro da iteração de um objeto exclusivo.&lt;br /&gt;&lt;br /&gt;Para resolver esses dois casos identifiquei o padrão de desenvolvimento &lt;span style="font-weight:bold;"&gt;Cópia Exclusiva de Coleção&lt;/span&gt;. A idéia é obter a exclusividade da lista somente para fazer uma cópia dos itens em outra coleção e então poder iterar sobre esta cópia sem muitas preocupações. Abaixo mostro um exemplo de uma classe Armario que possui muitas Coisas. Se alguma coisa for lixo, ela deve ser removida na execução do método removeLixo.&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;public class Armario {&lt;br /&gt;    private List&lt;Coisa&gt; coisas = new LinkedList&lt;Coisa&gt;();&lt;br /&gt;    public void removeLixo() {&lt;br /&gt;        List&lt;Coisa&gt; copia = lista();&lt;br /&gt;        for (Coisa coisa : copia) {&lt;br /&gt;            if (coisa.isLixo())&lt;br /&gt;                remove(coisa);&lt;br /&gt;        }&lt;br /&gt;    }    &lt;br /&gt;    public List&lt;Coisa&gt; lista() {&lt;br /&gt;        List&lt;Coisa&gt; copia = null;&lt;br /&gt;        synchronized (coisas) {&lt;br /&gt;            copia = new ArrayList&lt;Coisa&gt;(coisas.size());&lt;br /&gt;            copia.addAll(coisas);&lt;br /&gt;        }&lt;br /&gt;        return copia;&lt;br /&gt;    }&lt;br /&gt;    public void adiciona(Coisa coisa) {&lt;br /&gt;        synchronized (coisas) {&lt;br /&gt;            coisas.add(coisa);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    public void remove(Coisa coisa) {&lt;br /&gt;        synchronized (coisas) {&lt;br /&gt;            coisas.remove(coisa);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Com isso o tempo de exclusividade fica restrito ao tempo da cópia para a outra coleção, que ocorre no método lista. Dessa forma temos uma folga no tempo de iteração total e a possibilidade de rearranjar a coleção, adicionando ou removendo itens a ela. É importante ressaltar que o melhor jeito de evitar o calafrio de receber um &lt;a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/ConcurrentModificationException.html"&gt;java.util.ConcurrentModificationException&lt;/a&gt; é evitar a utilização de objetos compartilhados entre threads. Quando isso não é possível, o jeito é utilizar um padrão como &lt;span style="font-weight:bold;"&gt;Cópia Exclusiva de Coleção&lt;/span&gt;.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-6894444020692217789?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/6894444020692217789/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/09/cpia-exclusiva-de-coleo.html#comment-form' title='7 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/6894444020692217789'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/6894444020692217789'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/09/cpia-exclusiva-de-coleo.html' title='Cópia exclusiva de coleção'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-3995499054587030393</id><published>2008-09-16T20:48:00.005-03:00</published><updated>2008-09-17T01:16:24.108-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='ajax'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>On-demand javascript com PHP</title><content type='html'>A técnica de script on-demand serve para compartilhar dados entre domínios. A idéia dela é driblar as restrições da classe XMLHttpRequest, que só permite requisições no mesmo domínio, e a restrição do iframe que no máximo permite comunicação entre sub-domínios de um mesmo domínio.&lt;br /&gt;&lt;br /&gt;Basicamente consiste em adicionar um script à página quando uma informação é necessária. Esse script proverá a informação necessária chamando uma função de callback ao solicitador. Uma explicação mais detalhada sobre a técnica pode ser encontrada em &lt;a href="http://ajaxpatterns.org/On-Demand_Javascript"&gt;On-Demand_Javascript - AjaxPatterns&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Com &lt;a href="http://www.php.net"&gt;PHP&lt;/a&gt; fica ainda mais fácil desenvolver o script provedor de dados. Digamos por exemplo que você queira obter a lista de livros de uma biblioteca, seu javascript ficaria assim:&lt;div class="codigo"&gt;&lt;pre&gt;var Livros = {&lt;br /&gt;    listar:function() {&lt;br /&gt;        var s = document.createElement('script');&lt;br /&gt;        s.type = 'text/javascript';&lt;br /&gt;        s.src = 'listaDeLivros.php?callback=Livros.onload_listar';&lt;br /&gt;        document.getElementsByTagName('head')[0].appendChild(s);&lt;br /&gt;    }&lt;br /&gt;    onload_listar:function(html) {&lt;br /&gt;        document.getElementsByTagName('lista').innerHTML = html;&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;A função listar insere um javascript à página informando a ele a função javascript de callback. No PHP informaremos o html gerado chamando a função enviada no parâmetro callback. Mais ou menos como mostrado abaixo:&lt;div class="codigo"&gt;&lt;pre&gt;&amp;lt;?php echo $_REQUEST['callback']; ?&amp;gt;("&amp;lt;h1&amp;gt;Lista de livros&amp;lt;/h1&amp;gt;");&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Mas é claro que normalmente o html gerado não será de apenas uma linha. Além disso ele poderá ter diversos dados com aspas e outros caracteres que poderiam causar erros de javascript. Ficar se preocupando com esse tipo de coisa na diagramação do html é trabalhoso e passível de muitos erros.&lt;br /&gt;&lt;br /&gt;Para evitar isso podemos utilizar a função &lt;a href="http://www.php.net/manual/en/function.ob-start.php"&gt;ob_start&lt;/a&gt;, que faz com que tudo o que for escrito no output seja guardado em buffer e ao final remetido a uma função de callback do PHP. Nesta função então pode-se tratar o html e enfim escrever no real output para o cliente. Veja como ficaria:&lt;div class="codigo"&gt;&lt;pre&gt;&amp;lt;?php &lt;br /&gt;function captura_buffer($html) {&lt;br /&gt;    $html = addslashes($html);&lt;br /&gt;    $html = str_replace("\n", '\n', $html);&lt;br /&gt;    $html = str_replace("\r", '\r', $html);&lt;br /&gt;    return  $_REQUEST['callback'] . '("' . $html .  '");';&lt;br /&gt;}&lt;br /&gt;ob_start('captura_buffer');&lt;br /&gt;&lt;br /&gt;$livros = funcao_ficticia_para_obter_livros();&lt;br /&gt;&lt;br /&gt;foreach($livros as $livro) {&lt;br /&gt;?&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;p&amp;gt;&lt;br /&gt;&amp;lt;?php echo $livro-&gt;id ?&amp;gt; - &lt;br /&gt;&amp;lt;b&amp;gt;&amp;lt;?php echo $livro-&gt;titulo ?&amp;gt;&amp;lt;/b&amp;gt; - &lt;br /&gt;&amp;lt;?php echo $livro-&gt;autor ?&amp;gt;&lt;br /&gt;&amp;lt;/p&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;?php } ?&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-3995499054587030393?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/3995499054587030393/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/09/on-demand-javascript-com-php.html#comment-form' title='1 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3995499054587030393'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3995499054587030393'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/09/on-demand-javascript-com-php.html' title='On-demand javascript com PHP'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-3397048219956620991</id><published>2008-09-03T13:50:00.003-03:00</published><updated>2008-09-03T13:54:14.610-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='applet'/><category scheme='http://www.blogger.com/atom/ns#' term='timotta-api'/><title type='text'>KeyDown e KeyUp para applets java</title><content type='html'>Um grande problema da interface &lt;a href="http://java.sun.com/docs/books/tutorial/uiswing/events/keylistener.html"&gt;KeyListener&lt;/a&gt; do &lt;a href="http://java.sun.com"&gt;Java&lt;/a&gt; é não possuir uma maneira de saber se uma ou mais teclas estão pressionadas ao mesmo tempo, e quando elas realmente são soltas. Quando você mantém uma tecla pressionada os eventos &lt;a href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/event/KeyListener.html#keyPressed(java.awt.event.KeyEvent)"&gt;keyPressed&lt;/a&gt; e &lt;a href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/event/KeyListener.html#keyReleased(java.awt.event.KeyEvent)"&gt;keyReleased&lt;/a&gt; são ativados consecutivamente, e se você pressiona e segura outra tecla, o keyReleased da tecla anterior para de ser notificado, seguido por uma notificação consecutiva do keyPressed e keyReleased da nova tecla. Isso é facilmente demonstrado no &lt;a href="http://java.sun.com/applets/"&gt;applet&lt;/a&gt; abaixo:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;public class ComProblema extends Applet implements KeyListener {&lt;br /&gt;    @Override&lt;br /&gt;    public void init() {&lt;br /&gt;        addKeyListener(this);&lt;br /&gt;    }&lt;br /&gt;    public void keyPressed(KeyEvent e) {&lt;br /&gt;        System.out.println("Pressionou: " + e.getKeyCode());&lt;br /&gt;    }&lt;br /&gt;    public void keyReleased(KeyEvent e) {&lt;br /&gt;        System.out.println("Soltou: " + e.getKeyCode());&lt;br /&gt;    }&lt;br /&gt;    public void keyTyped(KeyEvent e) {}&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;Ao executarmos esse &lt;a href="http://java.sun.com/applets/"&gt;applet&lt;/a&gt; clicando e segurando a tecla &lt;span style="font-weight:bold;"&gt;"a"&lt;/span&gt; por alguns segundos, depois apertar e segurar a &lt;span style="font-weight:bold;"&gt;"w"&lt;/span&gt; por outros tantos e ao final soltar as duas, teremos o seguinte resultado:&lt;br /&gt;&lt;div class="codigo maximo"&gt;&lt;pre&gt;Pressionou: 65&lt;br /&gt;Soltou: 65&lt;br /&gt;Pressionou: 65&lt;br /&gt;Soltou: 65&lt;br /&gt;Pressionou: 65&lt;br /&gt;Soltou: 65&lt;br /&gt;Pressionou: 65&lt;br /&gt;Soltou: 65&lt;br /&gt;Pressionou: 65&lt;br /&gt;Soltou: 65&lt;br /&gt;Pressionou: 65&lt;br /&gt;Soltou: 65&lt;br /&gt;Pressionou: 65&lt;br /&gt;Pressionou: 87&lt;br /&gt;Soltou: 87&lt;br /&gt;Pressionou: 87&lt;br /&gt;Soltou: 87&lt;br /&gt;Pressionou: 87&lt;br /&gt;Soltou: 87&lt;br /&gt;Pressionou: 87&lt;br /&gt;Soltou: 87&lt;br /&gt;Pressionou: 87&lt;br /&gt;Soltou: 87&lt;br /&gt;Pressionou: 87&lt;br /&gt;Soltou: 87&lt;br /&gt;Pressionou: 87&lt;br /&gt;Soltou: 87&lt;br /&gt;Pressionou: 87&lt;br /&gt;Soltou: 65&lt;br /&gt;Soltou: 87&lt;/pre&gt;&lt;/div&gt;Encontrei em diversos fóruns pessoas com o mesmo problema e nenhuma solução. Então, para resolvê-lo implementei uma espécie de &lt;a href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/event/KeyAdapter.html"&gt;KeyAdapter&lt;/a&gt; para facilitar a notificação de quando uma tecla é apertada e quando ela é solta. O uso de exemplo dela ficaria como o mostrado abaixo:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;public class SemProblema extends Applet {&lt;br /&gt;    @Override&lt;br /&gt;    public void init() {&lt;br /&gt;        super.init();&lt;br /&gt;        addKeyListener(new CumulativeKeyAdapterImpl());&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;class CumulativeKeyAdapterImpl extends AcumuladorKeyAdapter {&lt;br /&gt;    @Override&lt;br /&gt;    public void keyDown(KeyEvent e) {&lt;br /&gt;        System.out.println("Down: " + e.getKeyCode());&lt;br /&gt;    }&lt;br /&gt;    @Override&lt;br /&gt;    public void keyUp(KeyEvent e) {&lt;br /&gt;        System.out.println("Up: " + e.getKeyCode());&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;O resultado fazendo a mesma combinação de teclas apertadas no teste anterior neste outro applet, seria como o mostrado a seguir:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;Down: 65&lt;br /&gt;Down: 87&lt;br /&gt;Up: 65&lt;br /&gt;Up: 87&lt;/pre&gt;&lt;/div&gt;Você também pode ver &lt;a href="http://www.geocities.com/chapundifornio/applet/applet.html"&gt;outros exemplos de applets&lt;/a&gt; utilizando o AcumuladorKeyAdapter &lt;a href="http://www.geocities.com/chapundifornio/applet/applet.html"&gt;aqui&lt;/a&gt;. Para utilizar esse recurso basta baixar os &lt;a href="/2007/06/download-da-timotta-api.html"&gt;fontes e binários da timotta-api&lt;/a&gt; e colocar o timotta-api-applet.jar no atributo &lt;span style="font-weight:bold;"&gt;archive&lt;/span&gt; da sua tag applet, como demonstrado no exemplo abaixo:&lt;div class="codigo"&gt;&lt;pre&gt;&amp;lt;applet code="br.com.timotta.applet.MostraTeclas" &lt;br /&gt;    archive="timotta-api-applet-exemplos.jar,timotta-api-applet.jar"&lt;br /&gt;    width=256 height=256 &amp;gt;&lt;br /&gt;&amp;lt;/applet&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-3397048219956620991?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/3397048219956620991/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/09/keydown-e-keyup-para-applets-java.html#comment-form' title='2 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3397048219956620991'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3397048219956620991'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/09/keydown-e-keyup-para-applets-java.html' title='KeyDown e KeyUp para applets java'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-1225092698424997678</id><published>2008-08-18T17:25:00.000-03:00</published><updated>2008-08-18T17:25:06.827-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>Passagem de array como parâmetro em subrotinas de Perl</title><content type='html'>Passar um array como parâmetro em uma subrotina de &lt;a href="http://perldoc.perl.org/"&gt;Perl&lt;/a&gt; pode ser muito complicado sem saber todos os conceitos da linguagem. Veja abaixo um exemplo de uma função recebendo arrays de cinco maneiras diferentes:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;sub faz_nada_util&lt;br /&gt;{&lt;br /&gt;        print "(" . $_[0] . "," . $_[1] . ")";&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;my @array_1 = (1,2); &lt;br /&gt;my @array_2 = [3,4];&lt;br /&gt;&lt;br /&gt;faz_nada_util @array_1;&lt;br /&gt;faz_nada_util \@array_1;&lt;br /&gt;faz_nada_util @array_2; &lt;br /&gt;faz_nada_util (5,6);&lt;br /&gt;faz_nada_util [7,8];&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;A saída será:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;(1,2)&lt;br /&gt;(ARRAY(0x81536b4),)&lt;br /&gt;(ARRAY(0x815360c),)&lt;br /&gt;(5,6)&lt;br /&gt;(ARRAY(0x8176f5c),)&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;O importante no exemplo é verificar que quando criamos um array utilizando colchetes &lt;span style="font-weight:bold;"&gt;[]&lt;/span&gt; estamos retornando na verdade não o array, mas a referência a este array. Dessa forma o primeiro item de &lt;span style="font-weight:bold;"&gt;@_&lt;/span&gt; seria a referência ao array. Isso é mostrado na terceira e na última linha do resultado. &lt;br /&gt;&lt;br /&gt;Quando criamos com o uso de parênteses &lt;span style="font-weight:bold;"&gt;()&lt;/span&gt; temos na váriavel o array em si. Então quando passamos esta lista à subrotina, os itens são copiados um a um no array &lt;span style="font-weight:bold;"&gt;@_&lt;/span&gt;. Isso é demonstrado na primeira e na quarta linha do resultado. Usando a contra-barra &lt;span style="font-weight:bold;"&gt;\&lt;/span&gt; indicamos no segundo print que queremos enviar a referência do array criado com parênteses.&lt;br /&gt;&lt;br /&gt;Mas e se você fizer uma função que receba como parâmetro um texto e um array como parâmetros? Você pode fazer da seguinte maneira:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;sub continua_fazendo_nada_util&lt;br /&gt;{&lt;br /&gt;        my $nome = shift;&lt;br /&gt;        print $nome . ": \n";&lt;br /&gt;        foreach $nome ( @_ ) {&lt;br /&gt;                print "- " . $nome . "\n";&lt;br /&gt;        }&lt;br /&gt;}&lt;br /&gt;continua_fazendo_nada_util("tiago", (1,2,3,4));&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;É como se a função recebesse como parâmetro um único grande array. Esse tipo de solução funciona bem quando o último parâmetro é o array, pois podemos dar &lt;span style="font-weight:bold;"&gt;shift&lt;/span&gt; dos parâmetros iniciais e iterar com os restantes. Num caso por exemplo em que a função deva receber dois arrays, podemos fazer a passagem de parâmetro pela referência.&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;sub nada_pra_fazer_aqui&lt;br /&gt;{&lt;br /&gt;        my @array_1 = @{$_[0]};&lt;br /&gt;        my @array_2 = @{$_[1]};&lt;br /&gt;&lt;br /&gt;        print "array1: \n";&lt;br /&gt;        foreach $a (@array_1) {&lt;br /&gt;                print "- $a\n";&lt;br /&gt;        }&lt;br /&gt;        print "array2: \n";&lt;br /&gt;        foreach $a (@array_2) {&lt;br /&gt;                print "- $a\n";&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;nada_pra_fazer_aqui [1,2,3], [4,5,6];&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-1225092698424997678?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/1225092698424997678/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/08/passagem-de-array-como-parmetro-em.html#comment-form' title='1 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/1225092698424997678'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/1225092698424997678'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/08/passagem-de-array-como-parmetro-em.html' title='Passagem de array como parâmetro em subrotinas de Perl'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-4443640497842629787</id><published>2008-08-14T01:07:00.000-03:00</published><updated>2009-08-14T01:08:15.606-03:00</updated><title type='text'>Web Democracia: críticas e sugestões</title><content type='html'>Utilizem o espaço dos comentários para postar suas críticas e sugestões&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-4443640497842629787?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/4443640497842629787/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/08/web-democracia-criticas-e-sugestoes.html#comment-form' title='93 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/4443640497842629787'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/4443640497842629787'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/08/web-democracia-criticas-e-sugestoes.html' title='Web Democracia: críticas e sugestões'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>93</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-8399524522626488641</id><published>2008-08-12T14:47:00.006-03:00</published><updated>2008-08-14T14:52:24.748-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rpm'/><title type='text'>Installed, but unpackaged files found em um RPM</title><content type='html'>Você pegou um projeto antigo, adicionou um diretório e um arquivo e alterou a sessão &lt;span style="font-weight:bold;"&gt;%install&lt;/span&gt; de seu &lt;a href="http://www.rpm.org/max-rpm/ch-rpm-inside.html"&gt;SPEC&lt;/a&gt; para instalar esse arquivo, mas ao rodar o &lt;a href="http://www.rpm.org/max-rpm-snapshot/ch-rpm-b-command.html"&gt;rpmbuild&lt;/a&gt; o seguinte erro é obtido:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;Checking for unpackaged file(s): /usr/lib/rpm/check-files /var/tmp/MeuPrograma-1.0.7-tiago&lt;br /&gt;error: Installed (but unpackaged) file(s) found:&lt;br /&gt;   /usr/local/MeuPrograma/etc/configuracao.properties&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;No caso o diretório novo é etc e o arquivo novo é configuracao.properties. A resolução desse problema é simples, basta adicionar o novo diretório à sessão &lt;span style="font-weight:bold;"&gt;%files&lt;/span&gt;, para que o rpmbuild o empacote, da seguinte forma:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;%files&lt;br /&gt;# ... outros arquivos aqui&lt;br /&gt;/usr/local/MeuPrograma/etc&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-8399524522626488641?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/8399524522626488641/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/08/installed-but-unpackaged-files-em-um.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/8399524522626488641'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/8399524522626488641'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/08/installed-but-unpackaged-files-em-um.html' title='Installed, but unpackaged files found em um RPM'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-3005541990117608341</id><published>2008-06-17T16:16:00.007-03:00</published><updated>2008-08-09T22:30:27.027-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jdbc'/><category scheme='http://www.blogger.com/atom/ns#' term='oracle'/><title type='text'>Erro esotérico ao inserir clobs e blobs no oracle</title><content type='html'>A mensagem de erro do Oracle "table or view does not exist" é simples e direta. O problema acontece quando você está fazendo uma série de inserções em uma tabela, utilizando a mesma String de SQL para todas, e lá pela sexta inserção a mensagem de erro aparece:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;java.sql.SQLException: ORA-00942: table or view does not exist&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Até que você descubra que pode haver algum problema com o sexto registro da tabela, você perde um bom tempo tentando encontrar algum problema de lógica ou alguma desatenção sua. Até mesmo bufa de raiva achando que é algum bug do Oracle. E de certa forma é. &lt;br /&gt;&lt;br /&gt;No meu caso descobri que o erro acontecia quando inseria textos grandes demais em uma coluna clob.  Encontrei então um documento da oracle explicando &lt;a href="http://www.oracle.com/technology/sample_code/tech/java/codesnippet/jdbc/clob10g/handlingclobsinoraclejdbc10g.html"&gt;como gravar clobs no oracle utilizando JDBC&lt;/a&gt;. O macete é configurar o JDBC com o parâmetro &lt;span style="font-weight:bold;"&gt;SetBigStringTryClob&lt;/span&gt;, assim:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;Properties propriedades = new Properties();&lt;br /&gt;propriedades.put("user", "meuUsuario" );&lt;br /&gt;propriedades.put("password", "pensaQueVouTeContar?");&lt;br /&gt;propriedades.put("SetBigStringTryClob", "true");&lt;br /&gt;return DriverManager.getConnection("jdbc:oracle:thin:@meuserver.com:filmes", propriedades);&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Fazendo isso o estranho erro parou de acontecer. De certa forma acredito que este é um bug do Oracle, pois no mínimo a mensagem de erro deveria ser algo informando que o tamanho do valor excede o permitido na coluna. Ou até informando que não foi possivel inserir devido a erros no preenchimento da coluna tal.&lt;br /&gt;&lt;br /&gt;Contudo, essa não foi a unica vez que este erro aconteceu comigo. Ao inserir dados em um campo blob o maldito "table or view does not exist" deu as caras novamente. No caso eu errei o tipo de dado utilizando setString. Mas dessa vez eu já estava calejado, então bastou verificar que para inserir blobs bastaria fazer algo como o mostrado abaixo:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;byte[] arr = new byte[] {0,1};&lt;br /&gt;ByteArrayInputStream b = new ByteArrayInputStream(arr);&lt;br /&gt;statement.setBinaryStream(i, b, b.available() );&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Mas que fique bem claro que o fato de eu estar inserindo clobs e blobs no banco não quer dizer que eu ache isso uma boa prática e que eu recomende. Ao contrário, acredito que seja melhor na maioria das situações que esses dados fiquem no sistema de arquivos.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-3005541990117608341?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/3005541990117608341/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/06/erro-exotrico-ao-inserir-clobs-e-blobs.html#comment-form' title='2 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3005541990117608341'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3005541990117608341'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/06/erro-exotrico-ao-inserir-clobs-e-blobs.html' title='Erro esotérico ao inserir clobs e blobs no oracle'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-3291682693936805810</id><published>2008-06-07T15:08:00.002-03:00</published><updated>2008-06-07T15:28:27.016-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><title type='text'>Cálculo da variância com Ruby</title><content type='html'>No blog &lt;a href="http://kodumaro.blogspot.com/2008/06/variancia-com-linguagem-funcional.html"&gt;Kodumaro&lt;/a&gt; foi publicado uma comparação de cálculos estatísticos entre linguagens imperativas, como C, e linguagens funcionais, no caso o Lisp. Os cálculos utilizados como exemplificação foram os de &lt;a href="http://pt.wikipedia.org/wiki/Vari%C3%A2ncia"&gt;variância da população e variância da amostra&lt;/a&gt;. Como estou estudando ruby, me empolguei e resolvi fazer a versão desses cálculos nessa linguagem.&lt;br /&gt;&lt;br /&gt;Veja como o código ficou:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;class Array&lt;br /&gt;  def acumula()&lt;br /&gt;    acc = 0&lt;br /&gt;    each { |a| acc += yield(a) }&lt;br /&gt;    acc&lt;br /&gt;  end&lt;br /&gt;  def media()&lt;br /&gt;    total = acumula { |a| a }&lt;br /&gt;    total / length&lt;br /&gt;  end&lt;br /&gt;  def numerador_de_variancia()&lt;br /&gt;    m = media&lt;br /&gt;    acumula { |a| (a - media)**2 }&lt;br /&gt;  end&lt;br /&gt;  def variancia_populacional()&lt;br /&gt;    numerador_de_variancia / length&lt;br /&gt;  end&lt;br /&gt;  def variancia_da_amostra()&lt;br /&gt;    numerador_de_variancia / (length-1)&lt;br /&gt;  end&lt;br /&gt;end&lt;/pre&gt;&lt;/div&gt;O que fiz foi criar novos métodos na classe Array para fazer todos os cálculos necessários. O interessante é que não encontrei na documentação do Ruby nenhum método que iterasse sobre um array e retornasse um acumulo dos valores calculados por um bloco. Para suprir isso criei então o método &lt;span style="font-weight:bold;"&gt;acumula&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Apesar do código ter ficado um pouco mais verboso que o &lt;a href="http://kodumaro.blogspot.com/2008/06/variancia-com-linguagem-funcional.html"&gt;código em lisp exibido no Kodumaro&lt;/a&gt;, o uso ficou mais simples. Como são novos métodos, para obter a variância da amostra em um array basta por exemplo fazer &lt;span style="font-weight:bold;"&gt;[2,4,6].variancia_da_amostra&lt;/span&gt;. Veja abaixo um exemplo de uso desses métodos:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;array = [2,4,6]&lt;br /&gt;puts "Media: #{array.media}"&lt;br /&gt;puts "Numerador de variancia: #{array.numerador_de_variancia}"&lt;br /&gt;puts "Variancia populacional: #{array.variancia_populacional}"&lt;br /&gt;puts "Variancia da amostra: #{array.variancia_da_amostra}"&lt;/pre&gt;&lt;/div&gt;Mas é importante ressaltar o perigo dessa alteração de classe. Veja que não faço nenhum tratamento sobre o tipo de dado do array. No caso, estou assumindo que todo array que vá utilizar esses métodos tenham sempre todos os indices preenchidos por numeros.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-3291682693936805810?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/3291682693936805810/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/06/clculo-da-varincia-com-ruby.html#comment-form' title='3 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3291682693936805810'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3291682693936805810'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/06/clculo-da-varincia-com-ruby.html' title='Cálculo da variância com Ruby'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-3697188291498412892</id><published>2008-06-07T04:22:00.001-03:00</published><updated>2008-06-07T04:25:03.389-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scrum'/><title type='text'>EDS: Excell Driven Scrum</title><content type='html'>Para quem não sabe, EDS é o Excell Driven Scrum. Ou vocês achavam que siglas com a palavra mágica Driven serviam somente para Development? Estavam enganados. O EDS ocorre quando o P.O. elabora previamente um planejamento, sprint a sprint, do que deve ser feito pelo time para um longo período de tempo. Este planejamento é então gravado em um arquivo excell read-only.&lt;br /&gt;&lt;br /&gt;Pelo pouco que entendo de Scrum, esse padrão é uma maneira muito equivocada de se pôr em prática um ambiente de desenvolvimento ágil. Isso porque a definição dos sprints deveria ser feita com a particiação do time. E mesmo se esse tal planejamento anual fosse feito com o time, ainda sim eu consideraria errado, pois é impossivel prever com muita antecedência fatores ambientais que fatalmente mudam as necessidades e problemas que precisam ser resolvidos.&lt;br /&gt;&lt;br /&gt;Além disso, o fato de termos escopos definidos para cada sprint, faz o sprint planning 1 ficar engessado. O time perde a liberdade de sugerir histórias prioritárias que garantam a qualidade do sistema. Isso acaba causando um desgaste durante a reunião, o time não entendendo como o P.O. pode ignorar os possiveis problemas da aplicação e os riscos que eles podem causar, e o P.O. sem entender porque o time não aceita seguir o planejamento anual.&lt;br /&gt;&lt;br /&gt;Acontece que muitas vezes o uso deste padrão não é culpa do P.O., é uma parte da cultura anterior à adoção do Scrum que ainda não foi possivel mudar. Contudo, não se pode fechar os olhos para este problema. Para acabar com o EDS, é preciso que o P.O. brigue corajosamente contra ele. O time também precisa bater o pé o máximo possivel para mudar esta cultura. E a briga não é apenas para mudar a cabeça de quem está acima hierarquicamente, é também para mudar suas próprias mentes.&lt;br /&gt;&lt;br /&gt;Portanto, apesar de ter pouca experiência com scrum, acredito que utilizar o padrão EDS não é uma boa alternativa de implantação dele. Pela minha vivência de menos de um ano e a participação em apenas um curso, me parece que um dos principais benefícios que o Scrum traz é a liberdade que o time tem para identificar problemas e resolve-los para garantir a qualidade do produto. Com o EDS isso não é possivel.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-3697188291498412892?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/3697188291498412892/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/06/eds-excell-driven-scrum.html#comment-form' title='4 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3697188291498412892'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3697188291498412892'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/06/eds-excell-driven-scrum.html' title='EDS: Excell Driven Scrum'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-4245245029543693736</id><published>2008-06-06T19:45:00.002-03:00</published><updated>2008-06-06T19:51:19.017-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='flash'/><title type='text'>Como obter a URL do SWF</title><content type='html'>Essa aqui é básica, mas eu procurei bastante no Google e não encontrei. Acabei só achando no help do próprio flash. Caso você precise obter no seu aplicativo flash qual é o caminho completo do seu SWF, basta utilizar a propriedade loaderURL do objeto loaderInfo contido no seu MovieClip. Veja abaixo:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;package {&lt;br /&gt;    public class Main extends MovieClip {&lt;br /&gt;        function Main() {   &lt;br /&gt;            trace( "URL do SWF: " + this.loaderInfo.loaderURL );&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-4245245029543693736?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/4245245029543693736/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/06/como-obter-url-do-swf.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/4245245029543693736'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/4245245029543693736'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/06/como-obter-url-do-swf.html' title='Como obter a URL do SWF'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-7354934625708165490</id><published>2008-05-15T18:02:00.003-03:00</published><updated>2008-05-15T18:52:42.717-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='flash'/><title type='text'>FullScreen do flash não funciona em players abaixo de 9.0.28, e agora?</title><content type='html'>Desde a versão 9.0.28 do flash é possível definir seu swf para rodar em tela cheia de maneira bem simples. Também é possivel definir um listener para ser notificado quando o modo de exibição do flash é alterado de normal para fullscreen e vice-versa. Contudo, se você colocar essas funcionalidades, seu swf não rodará &lt;a href="http://programandosemcafeina.blogspot.com/2008/05/como-instalar-o-flash-9016-para-testar.html"&gt;em versões anteriores a 9.028&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;O jeito então é alterar seu ActionScript para só setar a o fullscreen quando a versão for compatível. Veja abaixo como isso pode ser feito:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;import flash.display.StageDisplayState;&lt;br /&gt;&lt;br /&gt;public class Main extends MovieClip {&lt;br /&gt;&lt;br /&gt;    //...&lt;br /&gt;&lt;br /&gt;    function suportaTelaCheia():Boolean {&lt;br /&gt;        //Verifica versao &gt;= que 9.0.28&lt;br /&gt;    }&lt;br /&gt;    function onBotaoTelaCheiaClique(event:MouseEvent):void {&lt;br /&gt;        if( suportaTelaCheia() ) {&lt;br /&gt;            stage.displayState = StageDisplayState.FULL_SCREEN;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    function onBotaSaiTelaCheiaClique(event:MouseEvent):void {&lt;br /&gt;        if( suportaTelaCheia() ) {&lt;br /&gt;            stage.displayState = StageDisplayState.NORMAL;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    //...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;Apesar da classe &lt;span style="font-weight:bold;"&gt;flash.display.StageDisplayState&lt;/span&gt; não existir no player com &lt;a href="http://programandosemcafeina.blogspot.com/2008/05/como-instalar-o-flash-9016-para-testar.html"&gt;versão menor que 9.0.28&lt;/a&gt;, a importação dela não trará erro, pois o flash só executa a importação de uma classe quando faz uso dela. Portanto a &lt;a href="http://programandosemcafeina.blogspot.com/2008/01/checar-verso-do-flash-de-dentro-do-swf.html"&gt;seleção da versão feita com o método &lt;span style="font-weight:bold;"&gt;suportaTelaCheia&lt;/span&gt;&lt;/a&gt; impede seu uso e consequente importação.&lt;br /&gt;&lt;br /&gt;O problema maior é quando precisamos definir um listener para sabermos quando o swf entrou em modo fullscreen e quando saiu. Normalmente isso é necessário quando precisamos redimensionar, ou adicionar elementos ao stage. No caso o código, sem o tratamento de versão, ou seja, o que daria erro nas &lt;a href="http://programandosemcafeina.blogspot.com/2008/05/como-instalar-o-flash-9016-para-testar.html"&gt;versões de flash anteriores a 9.0.28&lt;/a&gt; ficaria assim:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;import flash.events.FullScreenEvent;&lt;br /&gt;&lt;br /&gt;public class Main extends MovieClip {&lt;br /&gt;&lt;br /&gt;    //...&lt;br /&gt;&lt;br /&gt;    private function onStage(event:Event):void {&lt;br /&gt;        stage.addEventListener( FullScreenEvent.FULL_SCREEN, redrawFullScreen );&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    //...&lt;br /&gt;&lt;br /&gt;    function redrawFullScreen(event:FullScreenEvent):void {&lt;br /&gt;        if( event.fullScreen ) {&lt;br /&gt;            //Posicionar elementos para tela cheia&lt;br /&gt;        } else {&lt;br /&gt;            //Posicionar elementos para tamanho normal&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    //....&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;O grande problema disso é que o uso da classe FullScreenEvent está na definição do método &lt;span style="font-weight:bold;"&gt;redrawFullScreen&lt;/span&gt; e portanto não é possivel defini-lo somente quando for uma versão ou outra. O jeito então então é mudar a assinatura do método, para ao invés de receber um &lt;span style="font-weight:bold;"&gt;FullScreenEvent&lt;/span&gt; receber um &lt;span style="font-weight:bold;"&gt;Event&lt;/span&gt;. Com essa correção o código ficaria assim:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;import flash.events.FullScreenEvent;&lt;br /&gt;&lt;br /&gt;public class Main extends MovieClip {&lt;br /&gt;&lt;br /&gt;    //...&lt;br /&gt;&lt;br /&gt;    function suportaTelaCheia():Boolean {&lt;br /&gt;        //Verifica versao &gt;= que 9.0.28&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private function onStage(event:Event):void {&lt;br /&gt;        if( suportaTelaCheia() ) {&lt;br /&gt;            stage.addEventListener( FullScreenEvent.FULL_SCREEN, redrawFullScreen );&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    //...&lt;br /&gt;&lt;br /&gt;    function redrawFullScreen(event:Event):void {&lt;br /&gt;        if( event['fullScreen'] ) {&lt;br /&gt;            //Posicionar elementos para tela cheia&lt;br /&gt;        } else {&lt;br /&gt;            //Posicionar elementos para tamanho normal&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    //....&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;Repare que além de ter mudado a assinatura do método, mudei também o uso do objeto &lt;span style="font-weight:bold;"&gt;event&lt;/span&gt;. Isso foi feito porque a classe &lt;span style="font-weight:bold;"&gt;Event&lt;/span&gt; não possui o atributo fullScreen declarado, então utilizar &lt;span style="font-weight:bold;"&gt;event.fullScreen&lt;/span&gt; daria erro de compilação. Mas como sabemos que ali há este atributo, podemos utilizá-lo usando a notação &lt;span style="font-weight:bold;"&gt;event['fullScreen']&lt;/span&gt;.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-7354934625708165490?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/7354934625708165490/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/05/fullscreen-do-flash-no-funciona-em.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/7354934625708165490'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/7354934625708165490'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/05/fullscreen-do-flash-no-funciona-em.html' title='FullScreen do flash não funciona em players abaixo de 9.0.28, e agora?'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-4031716905728410373</id><published>2008-05-14T18:24:00.005-03:00</published><updated>2008-05-14T19:19:56.933-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='flash'/><title type='text'>Como instalar o flash 9.0.16 para testar seu swf</title><content type='html'>É muito importante que ao criar um swf complexo, como um &lt;a href="http://video.globo.com/Videos/Player/Entretenimento/0,,GIM827283-7822-EXFAVORITOS,00.html"&gt;player de vídeo&lt;/a&gt; por exemplo, você execute testes em todas as versões de flash player que você deseja que ele suporte. Para isso a &lt;a href="http://www.adobe.com"&gt;Adobe&lt;/a&gt; oferece em seu site diversas versões antigas do flash player para download. Contudo, se você está no Windows XP e deseja intalar a versão 9.0.16, mas já possui uma versão mais nova instalada, é preciso  seguir os seguintes passos. &lt;br /&gt;&lt;br /&gt;Primeiro é preciso desinstalar a versão atual do flash player executando o programa &lt;a href="http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=tn_14157"&gt;uninstall_flash_player.exe&lt;/a&gt; encontrado na página &lt;a href="http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=tn_14157"&gt;How to uninstall the Adobe Flash Player plug-in and ActiveX control&lt;/a&gt;. Depois de executar isso é preciso acessar o &lt;a href="http://support.microsoft.com/kb/256986"&gt;regedit&lt;/a&gt; e remover a seguinte chave: HKEY_LOCAL_MACHINE\SOFTWARE\Macromedia\FlashPlayer.&lt;br /&gt;&lt;br /&gt;Feito isso, reinicie o computador. Ok ok, paciência, estamos lidando com Windows portanto todo cuidado é pouco. Mãos a obra: Iniciar &gt; Reiniciar (Heim?).&lt;br /&gt;&lt;br /&gt;Após reiniciado basta instalar a versão 9.0.16 que pode ser baixada em &lt;a href="http://kb.adobe.com/selfservice/viewContent.do?externalId=tn_14266&amp;sliceId=1"&gt;Archived Flash Players available for testing purposes&lt;/a&gt;. Nesse link podem ser encontradas diversas outras versões do flash player e é bem possível que o escrito aqui sirva para instalar essas outras versões.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-4031716905728410373?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/4031716905728410373/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/05/como-instalar-o-flash-9016-para-testar.html#comment-form' title='2 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/4031716905728410373'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/4031716905728410373'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/05/como-instalar-o-flash-9016-para-testar.html' title='Como instalar o flash 9.0.16 para testar seu swf'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-8026661383311945914</id><published>2008-05-06T00:51:00.004-03:00</published><updated>2008-05-06T11:26:53.676-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>Undefined method *_path no uso do form_for</title><content type='html'>Essa é uma dica boba para aqueles que estão começando em ruby on rails como eu. Fazendo aqui um exemplos simples de cadastro de filmes recebi o seguinte erro:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;NoMethodError in Filme#incluir&lt;br /&gt;&lt;br /&gt;Showing filme/incluir.html.erb where line #5 raised:&lt;br /&gt;&lt;br /&gt;undefined method `filmes_path' for #&amp;lt;ActionView::Base:0xb76f144c&amp;gt;&lt;br /&gt;&lt;br /&gt;Extracted source (around line #5):&lt;br /&gt;&lt;br /&gt;2: &lt;br /&gt;3: &amp;lt;%= error_messages_for :filme %&amp;gt;&lt;br /&gt;4: &lt;br /&gt;5: &amp;lt;% form_for @filme do |f| %&amp;gt;&lt;br /&gt;6:   &amp;lt;p&amp;gt;&lt;br /&gt;7:     &amp;lt;b&amp;gt;T&amp;iacute;tulo&amp;lt;/b&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;8:     &amp;lt;%= f.text_field :titulo %&amp;gt;&lt;/pre&gt;&lt;/div&gt;Pelo que eu entendi até agora, isso acontece porque eu não estou utilizando os métodos padrões do ruby on rails. Se eu estiver falando besteira por favor me corrijam. O fato é que resolvi isso definindo a ação que o form deverá executar, o código da view então ficaria da seguinte forma:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;&amp;lt;h1&amp;gt;Incluir novo filme&amp;lt;/h1&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;%= error_messages_for :filme %&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;% form_for @filme, :url =&amp;gt; { :action =&amp;gt; 'salvar' } do |f| %&amp;gt;&lt;br /&gt;  &amp;lt;p&amp;gt;&lt;br /&gt;    &amp;lt;b&amp;gt;T&amp;iacute;tulo&amp;lt;/b&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;    &amp;lt;%= f.text_field :titulo %&amp;gt;&lt;br /&gt;  &amp;lt;/p&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;p&amp;gt;&lt;br /&gt;    &amp;lt;b&amp;gt;Resumo&amp;lt;/b&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;    &amp;lt;%= f.text_area :resumo %&amp;gt;&lt;br /&gt;  &amp;lt;/p&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;p&amp;gt;&lt;br /&gt;    &amp;lt;%= f.submit "Incluir" %&amp;gt;&lt;br /&gt;  &amp;lt;/p&amp;gt;&lt;br /&gt;&amp;lt;% end %&amp;gt;&lt;/pre&gt;&lt;/div&gt;No caso o controlador possui o método salvar.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-8026661383311945914?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/8026661383311945914/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/05/undefined-method-path-no-uso-do-formfor.html#comment-form' title='9 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/8026661383311945914'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/8026661383311945914'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/05/undefined-method-path-no-uso-do-formfor.html' title='Undefined method *_path no uso do form_for'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-6643460029092915903</id><published>2008-05-01T01:05:00.003-03:00</published><updated>2008-05-01T01:49:35.135-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scrum'/><title type='text'>Dependência entre tarefas no Scrum</title><content type='html'>Um problema recorrente nas reuniões diárias do &lt;a href="http://video.globo.com"&gt;nosso projeto&lt;/a&gt; aqui na &lt;a href="http://www.globo.com"&gt;globo.com&lt;/a&gt; é a dependência entre tarefas. Sempre ocorrem interrupções entre o fluxo de "O que eu fiz" e "O que farei" para questionar a algum colega do time se determinada tarefa já foi concluída. No caso afirmativo ele pode então pegar a tarefa que era dependente. Isso acontece frequentemente, e quando ocorre o fluxo desanda e a atenção se dispersa. Começam então algumas conversas paralelas que acabam atrapalhando o andamento da reunião.&lt;br /&gt;&lt;br /&gt;O ideal, é claro, é que não houvessem tarefas dependentes entre si em uma história. Mas isso é muito díficil, e segundo minha breve experiência com Scrum, e nesse ponto ela se resume a apenas alguns poucos projetos web, me parece ser impossivel. Como exemplo posso mostrar três tarefas que foram presentes na maioria das histórias dos projetos que participei e que são dependentes entre si:&lt;br /&gt;&lt;br /&gt;1- Criar design&lt;br /&gt;2- Criar html e css estático&lt;br /&gt;3- Implementar camada de visualização&lt;br /&gt;&lt;br /&gt;A tarefa 2 depende da tarefa 1, e a tarefa 3 depende da tarefa 2. É claro que seria possível paralelizar a tarefa 3 com a 2, fazendo com que a implementação da camada de visualização fosse apenas de um html tosco. Mas mesmo assim seria necessária uma quarta tarefa para fazer o merge do html bonito com a camada de visualização, e esta nova tarefa seria enfim dependente da tarefa 2.&lt;br /&gt;&lt;br /&gt;Ou seja, não vejo como paralelizar todas elas. E cabe aqui uma brecha para colaboração dos leitores no caso de terem encontrado alternativas, ficaria feliz de conhecê-las. De qualquer forma, existem outras situações específicas de cada empresa e de cada time que elevam mais ainda o número de dependência entre as tarefas. &lt;br /&gt;&lt;br /&gt;Eis então que presenciei em &lt;a href="http://faustaodancadagalera.globolog.com.br/"&gt;outro time&lt;/a&gt; uma boa adaptação da reunião diária. Ao invés de cada membro do time responder as três perguntas, um em seguida do outro, os membros dizem primeiro o que fizeram, uma após o outro, depois dizem o que pretendem fazer, um atrás do outro, e ao final o que os impede, também da mesma forma.&lt;br /&gt;&lt;br /&gt;Com isso o time resolveu de maneira simples um problema que parece não ter solução, sem sacrificar a reunião diária, pois todos saem dela sabendo o que fazer, o que já está pronto e quais são os impedimentos do projeto. Cumprindo exatamente o propósito dela.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-6643460029092915903?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/6643460029092915903/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/05/dependncia-entre-tarefas-no-scrum.html#comment-form' title='8 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/6643460029092915903'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/6643460029092915903'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/05/dependncia-entre-tarefas-no-scrum.html' title='Dependência entre tarefas no Scrum'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-4310250260374778834</id><published>2008-04-21T01:38:00.004-03:00</published><updated>2008-05-06T11:53:47.586-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='comet'/><title type='text'>Demora do Firefox para começar a receber os eventos de um Comet</title><content type='html'>Quando abrimos um HTTP Stream, ou &lt;a href="http://en.wikipedia.org/wiki/Comet_(programming)"&gt;Comet&lt;/a&gt; como é chamado hoje em dia, no &lt;a href="http://www.mozilla.com/en-US/firefox/"&gt;Firefox&lt;/a&gt;, demora um tempo até que os dados comecem a ficar disponíveis. Ao que parece ele espera haja 1kb de dados já recebidos para começar a deixá-los disponíveis para uso. Contudo, há uma forma de habilitar o processamento dos eventos do Comet no &lt;a href="http://www.mozilla.com/en-US/firefox/"&gt;Firefox&lt;/a&gt; diretamente sem esperar esse 1kb inicial. Basta definir o content type do retorno como &lt;span style="font-weight:bold;"&gt;multipart/x-mixed-replace&lt;/span&gt;. Em &lt;a href="http://java.sun.com"&gt;Java&lt;/a&gt; seria algo como o mostrado abaixo:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;response.setContentType("multipart/x-mixed-replace");&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-4310250260374778834?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/4310250260374778834/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/04/demora-do-firefox-para-comear-receber-o.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/4310250260374778834'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/4310250260374778834'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/04/demora-do-firefox-para-comear-receber-o.html' title='Demora do Firefox para começar a receber os eventos de um Comet'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-8491241110177099807</id><published>2008-04-19T23:02:00.004-03:00</published><updated>2008-04-19T23:15:52.565-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='comet'/><category scheme='http://www.blogger.com/atom/ns#' term='tomcat'/><title type='text'>Ativando o conector NIO no Tomcat 6</title><content type='html'>Para utilizar o &lt;a href="http://tomcat.apache.org/tomcat-6.0-doc/api/org/apache/catalina/CometProcessor.html"&gt;CometProcessor&lt;/a&gt; do &lt;a href="http://tomcat.apache.org/download-60.cgi"&gt;Tomcat 6&lt;/a&gt; é preciso configurar o servidor para utilizar o NIO Connector ao invés do conector http padrão. Caso contrário, ao acessar o seu &lt;a href="http://tomcat.apache.org/tomcat-6.0-doc/api/org/apache/catalina/CometProcessor.html"&gt;CometProcessor &lt;/a&gt; você receberá o seguinte erro:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;message HTTP method GET is not supported by this URL&lt;br /&gt;&lt;br /&gt;description The specified HTTP method is not allowed for the requested resource (HTTP method GET is not supported by this URL).&lt;/pre&gt;&lt;/div&gt;Para habilitar então este conector basta alterar no arquivo server.xml o atributo protocol do conector, deixando-a mais ou menos da seguinte maneira:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;    &amp;lt;Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol" &lt;br /&gt;               connectionTimeout="20000" &lt;br /&gt;               redirectPort="8443" /&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-8491241110177099807?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/8491241110177099807/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/04/ativando-o-conector-nio-no-tomcat-6.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/8491241110177099807'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/8491241110177099807'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/04/ativando-o-conector-nio-no-tomcat-6.html' title='Ativando o conector NIO no Tomcat 6'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-1623181518944489046</id><published>2008-04-15T13:17:00.003-03:00</published><updated>2008-04-15T13:39:19.444-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='globo.com'/><category scheme='http://www.blogger.com/atom/ns#' term='fisl'/><title type='text'>Globo.com no Fisl 9.0</title><content type='html'>Do dia 17 a 19 de Abril estarei em &lt;a href="http://www.portoalegre.tur.br/"&gt;Porto Alegre&lt;/a&gt; participando do &lt;a href="http://www.fisl.org.br"&gt;Fisl 9.0&lt;/a&gt; junto com &lt;a href="http://gc.blog.br/"&gt;Guilherme Chapiewski&lt;/a&gt;, &lt;a href="http://peczenyj.blogspot.com/"&gt;Tiago Peczenyj&lt;/a&gt; e mais um monte de gente da &lt;a href="http://www.globo.com"&gt;Globo.com&lt;/a&gt;. Estaremos lá para conversar um pouco sobre os problemas e soluções de um dos maiores portais da internet brasileira e todo nosso esforço para basearmos essas soluções em software livre. Especificamente estarei lá para conversar sobre o portal &lt;a href="http://video.globo.com"&gt;Globo Vídeos&lt;/a&gt; e as nossas  ultimas pesquisas com software livre. Quem quiser me encontrar para bater um bom papo nerd é só me procurar no stand da &lt;a href="http://www.globo.com"&gt;Globo.com&lt;/a&gt;. É claro que não estarei o tempo inteiro lá, pois assistirei diversas palestras, no entanto farei o possível para ficar o máximo de tempo lá. Além disso criei um &lt;a href="http://www.8p.com.br/grupo/fisl9"&gt;grupo de fotos no 8P&lt;/a&gt; para publicarmos as fotos do evento. Assim, se você quiser participar do &lt;a href="http://www.8p.com.br/grupo/fisl9"&gt;grupo de fotos do Fisl 9.0&lt;/a&gt; é só se associar e publicar suas fotos. Por hora é só, vejo vocês lá!&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-1623181518944489046?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/1623181518944489046/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/04/globocom-no-fisl-90.html#comment-form' title='2 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/1623181518944489046'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/1623181518944489046'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/04/globocom-no-fisl-90.html' title='Globo.com no Fisl 9.0'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-5855853004892254336</id><published>2008-04-14T21:54:00.004-03:00</published><updated>2008-04-15T00:45:26.790-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bash'/><title type='text'>Bash para redimensionar imagens</title><content type='html'>Toda segunda-feira é a mesma história, depois de um final de semana de festas e diversão, todo mundo fica solicitando, porque não dizer exigindo as fotos tiradas durante os eventos. O problema é que fotos de câmeras digitais costumam ser grandes, tornando dificil a transferencia para os amigos.&lt;br /&gt;&lt;br /&gt;O &lt;a href="http://www.caiomoritz.com"&gt;Caio Moritz&lt;/a&gt; postou em seu blog uma dica de &lt;a href="http://www.caiomoritz.com/2008/03/19/redimensionando-imagens-pela-linha-de-comando/"&gt;como redimensionar imagens pela linha de comando&lt;/a&gt; usando o comando mogrify. O problema é que o bash que ele criou redimensiona as imagens sem manter as originais. Então, tomei a liberdade de incrementá-lo um pouco, devido às minhas necessitades. Segue abaixo como ficou:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;#!/bin/sh&lt;br /&gt;if [ -z $2 ] &lt;br /&gt;then &lt;br /&gt;    echo "Uso: resizepics.sh diretorioFonte diretorioDestino";&lt;br /&gt;    exit;&lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;echo "Criando diretorio $2";&lt;br /&gt;mkdir "$2";&lt;br /&gt;&lt;br /&gt;echo "Copiando arquivos de $1 para $2";&lt;br /&gt;cp "$1/"*.jpg "$2";&lt;br /&gt;&lt;br /&gt;echo "Entrando no diretorio $2";&lt;br /&gt;cd "$2"&lt;br /&gt;&lt;br /&gt;echo "Redimensionando imagens";&lt;br /&gt;for img in *.jpg; &lt;br /&gt;do &lt;br /&gt; echo "$img"; &lt;br /&gt; mogrify -resize 800x600 "$img"; &lt;br /&gt;done&lt;/pre&gt;&lt;/div&gt;Pronto, salve com o nome de &lt;span style="font-weight:bold;"&gt;resizepics.sh&lt;/span&gt;. Para executá-lo é só informar como primeiro parâmetro o diretório onde estão as fotos, e no segundo o diretório onde serão colocadas as fotos redimensionadas. É claro que para isso funcionar você precisa instalar o pacote imagemagick conforme informado no &lt;a href="http://www.caiomoritz.com/2008/03/19/redimensionando-imagens-pela-linha-de-comando/"&gt;post do Caio&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-5855853004892254336?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/5855853004892254336/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/04/bash-para-redimensionar-imagens.html#comment-form' title='1 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/5855853004892254336'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/5855853004892254336'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/04/bash-para-redimensionar-imagens.html' title='Bash para redimensionar imagens'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-7701192092315339141</id><published>2008-04-04T16:04:00.003-03:00</published><updated>2008-04-04T16:14:33.571-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='firefox'/><category scheme='http://www.blogger.com/atom/ns#' term='extensions'/><category scheme='http://www.blogger.com/atom/ns#' term='xul'/><title type='text'>Brincando de extensões com o chrome do Firefox</title><content type='html'>Desenvolver &lt;a href="https://addons.mozilla.org/en-US/firefox/"&gt;extensões para o Firefox&lt;/a&gt; é algo muito divertido. Mas há certas questões que nos deixam de cabelo em pé. Basicamente por falta de exemplos e tutoriais simples. No caso, eu procurava duas coisas: Como acessar o elemento html que foi clicado com o botão direito do mouse, e como copiar um determinado trecho selecionado para a área de transferências (clipboard).&lt;br /&gt;&lt;br /&gt;Nessas horas então, nada melhor que analisar os códigos contidos no Chrome do Firefox. Descompactando os arquivos &lt;span style="font-weight:bold;"&gt;browser.jar&lt;/span&gt; e &lt;span style="font-weight:bold;"&gt;toolkit.jar&lt;/span&gt;, localizados no diretório &lt;span style="font-weight:bold;"&gt;${FIREFOX_HOME}/chrome&lt;/span&gt;, podemos verificar diversos bons exemplos. &lt;br /&gt;&lt;br /&gt;No arquivo &lt;span style="font-weight:bold;"&gt;browser.js&lt;/span&gt; do pacote &lt;span style="font-weight:bold;"&gt;browser.jar&lt;/span&gt; por exemplo, pude verificar que para acessar o elemento clicado com o botão direito basta utilizar a notação &lt;span style="font-weight:bold;"&gt;document.popupNode&lt;/span&gt;. O exemplo abaixo mostra de maneira simples um &lt;a href="http://developer.mozilla.org/en/docs/XUL"&gt;Xul&lt;/a&gt; e um &lt;a href="http://programandosemcafeina.blogspot.com/search/label/javascript"&gt;Js&lt;/a&gt; de um item no menu do botão direito cuja única função é exibir a tag html do elemento clicado com o botão direito.&lt;br /&gt;&lt;br /&gt;Xul:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;&amp;lt;popup id="contentAreaContextMenu"&amp;gt;&lt;br /&gt; &amp;lt;menuitem id="itemMenuExibirTag" label="Exibir tag" oncommand="exibirTag()"/&amp;gt;&lt;br /&gt;&amp;lt;/popup&amp;gt;&lt;/pre&gt;&lt;/div&gt; Js:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;function exibirTag() {&lt;br /&gt; alert( document.popupNode.tagName );&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;Já para implementar a função de copiar para a área de transferência, verifiquei no arquivo &lt;span style="font-weight:bold;"&gt;browser.xul&lt;/span&gt; do pacote &lt;span style="font-weight:bold;"&gt;browser.jar&lt;/span&gt; que os menus de copiar do próprio navegador chamavam a função &lt;span style="font-weight:bold;"&gt;goDoCommand&lt;/span&gt; com o parâmetro &lt;span style="font-weight:bold;"&gt;cmd_copy&lt;/span&gt;. Encontrei essa função no arquivo &lt;span style="font-weight:bold;"&gt;globalOverlay.js&lt;/span&gt; do pacote &lt;span style="font-weight:bold;"&gt;toolkit.jar&lt;/span&gt;. Havia nele um comentário informando que a função era apenas para manter compatibilidade com versões antigas, e realmente a única coisa que ela fazia era repassar a função para o método &lt;span style="font-weight:bold;"&gt;doCommand&lt;/span&gt; da classe &lt;span style="font-weight:bold;"&gt;CommandUpdater&lt;/span&gt;. Abaixo mostro um exemplo de item que seleciona todos os itens de uma página e copia o conteúdo para o clipboard:&lt;br /&gt;&lt;br /&gt;Xul:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;&amp;lt;popup id="contentAreaContextMenu"&amp;gt;&lt;br /&gt; &amp;lt;menuitem id="itemMenuCopiarPagina" label="Copiar pagina" oncommand="copiarPagina()"/&amp;gt;&lt;br /&gt;&amp;lt;/popup&amp;gt;&lt;/pre&gt;&lt;/div&gt; Js:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;function copiarPagina() {&lt;br /&gt; CommandUpdater.doCommand('cmd_selectAll');&lt;br /&gt; CommandUpdater.doCommand('cmd_copy');&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-7701192092315339141?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/7701192092315339141/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/04/brincando-de-extenses-com-o-chrome-do.html#comment-form' title='2 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/7701192092315339141'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/7701192092315339141'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/04/brincando-de-extenses-com-o-chrome-do.html' title='Brincando de extensões com o chrome do Firefox'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-1073377102039560270</id><published>2008-04-03T19:14:00.003-03:00</published><updated>2008-04-03T19:32:32.956-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Definindo atributos somente leitura em uma classe PHP</title><content type='html'>O modificador &lt;span style="font-weight:bold;"&gt;private&lt;/span&gt; em &lt;a href="http://www.php.net"&gt;PHP&lt;/a&gt;, assim como em quase todas as linguagens, impede que membros de uma classe sejam acessados para leitura e escrita. Mas e se você deseja que os atributos de uma classe sejam privados para escrita mas não para leitura? Uma solução simples seria criar métodos &lt;span style="font-weight:bold;"&gt;get&lt;/span&gt; para cada atributo que se deseja liberar a leitura. Mas essa solução é muito verbosa, e como costumo dizer, se você deseja fazer algo desse tipo, melhor então usar &lt;a href="http://java.sun.com"&gt;Java&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;O &lt;a href="http://www.php.net"&gt;PHP&lt;/a&gt; possui o que no site está descrito como &lt;a href="http://www.php.net/manual/pt_BR/language.oop5.magic.php"&gt;métodos mágicos&lt;/a&gt;. No caso para liberarmos a leitura de atributos privados de uma classe, utilizaríamos o método mágico &lt;span style="font-weight:bold;"&gt;__get&lt;/span&gt;. Esse método é chamado sempre que um atributo da classe é acessado. Veja abaixo como ficaria a implementação desse método de forma a liberar todos os atributos da classe:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;class Pessoa {&lt;br /&gt;    &lt;br /&gt;    private $nome;&lt;br /&gt;    private $idade;&lt;br /&gt;    &lt;br /&gt;    function Pessoa($nome,$idade) {&lt;br /&gt;        $this-&gt;nome = $nome;&lt;br /&gt;        $this-&gt;idade = $idade;&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    function __get($atributo) {&lt;br /&gt;        echo "&amp;lt;br/&amp;gt;Leu atributo $atributo"; &lt;br /&gt;        return $this-&gt;$atributo;&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;Repare que interessante a sintaxe do corpo do método &lt;span style="font-weight:bold;"&gt;__get&lt;/span&gt;. Eu retorno um atributo do objeto atual que contenha o nome enviado por parâmetro. Para verificarmos o funcionamento dele podemos testar com o seguinte código:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;$eu = new Pessoa("Tiago",25);&lt;br /&gt;echo "&amp;lt;br/&amp;gt;" . $eu-&gt;nome . " tem " . $eu-&gt;idade . " anos";&lt;br /&gt;echo "&amp;lt;br/&amp;gt;Atributo nao existente: " . $eu-&gt;atributoNaoExistente;&lt;/pre&gt;&lt;/div&gt;A saída esperada é:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;Leu atributo nome&lt;br /&gt;Leu atributo idade&lt;br /&gt;Tiago tem 25 anos&lt;br /&gt;Leu atributo atributoNaoExistente&lt;br /&gt;Atributo nao existente:&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-1073377102039560270?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/1073377102039560270/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/04/definindo-atributos-somente-leitura-em.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/1073377102039560270'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/1073377102039560270'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/04/definindo-atributos-somente-leitura-em.html' title='Definindo atributos somente leitura em uma classe PHP'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-6418762954364011140</id><published>2008-04-01T13:36:00.008-03:00</published><updated>2008-04-01T17:24:42.065-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='testes'/><category scheme='http://www.blogger.com/atom/ns#' term='phpunit'/><title type='text'>Mock de classe no PHPUnit executa o construtor</title><content type='html'>O mock de classes no &lt;a href="http://www.phpunit.de/"&gt;PHPUnit&lt;/a&gt; não funciona muito bem, o ideal, claro é mocar interfaces, mas quando isso não é possível é preciso estar ciente de que o construtor da classe mocada será executado e portanto o teste ficará um tanto falho. &lt;br /&gt;&lt;br /&gt;É possivel identificar facilmente esse problema com o código abaixo. No caso mocarei a classe &lt;span style="font-weight:bold;"&gt;ClasseUtilizada&lt;/span&gt; para ser utilizada na classe &lt;span style="font-weight:bold;"&gt;ClasseASerTestada&lt;/span&gt;. Ao executar o teste é possível verifique que o &lt;span style="font-weight:bold;"&gt;echo&lt;/span&gt; dentro do construtor será executado erroneamente.&lt;br /&gt;&lt;div class="codigo maximo"&gt;&lt;pre&gt;class ClasseUtilizada {   &lt;br /&gt;    function ClasseUtilizada() {&lt;br /&gt;        echo "\n[passou no construtor de ClasseUtilizada]\n";&lt;br /&gt;    }&lt;br /&gt;    public function fazAlgumaCoisa() {&lt;br /&gt;        return "uhuuu";&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;class ClasseASerTestada {&lt;br /&gt;    function ClasseASerTestada($in) {&lt;br /&gt;        echo "\n[" . $in-&gt;fazAlgumaCoisa() . "]\n";&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;class MeuTeste extends PHPUnit_Framework_TestCase {&lt;br /&gt;    /**&lt;br /&gt;     * @Test&lt;br /&gt;     */&lt;br /&gt;    public function teste_com_classe() {&lt;br /&gt;        $mock = $this-&gt;getMock("ClasseUtilizada");&lt;br /&gt;        $mock-&gt;expects($this-&gt;once())&lt;br /&gt;                 -&gt;method('fazAlgumaCoisa')&lt;br /&gt;                 -&gt;will($this-&gt;returnValue(2));&lt;br /&gt;        new ClasseASerTestada($mock);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;Uma forma de contornar isso é se aproveitar das características dinâmicas do &lt;a href="http://www.php.net"&gt;PHP&lt;/a&gt; para simplesmente criar um Mock de uma classe que não existe. Mas pra isso é preciso informar no método getMock não só o nome de uma classe inexistente, como também a lista de métodos que a classe teria. Veja como ficaria abaixo:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;    /**&lt;br /&gt;     * @Test&lt;br /&gt;     */&lt;br /&gt;    public function teste_com_classe_inexistente_indicando_metodos() {&lt;br /&gt;        $mock = $this-&gt;getMock("ClasseUtilizadaMock", Array('fazAlgumaCoisa'));&lt;br /&gt;        $mock-&gt;expects($this-&gt;once())&lt;br /&gt;             -&gt;method('fazAlgumaCoisa')&lt;br /&gt;             -&gt;will($this-&gt;returnValue(4));&lt;br /&gt;        new ClasseASerTestada($mock);&lt;br /&gt;    }&lt;/pre&gt;&lt;/div&gt;Se por um acaso você não indicar o segundo parâmetro, que é um array com os métodos que a classe possui, um erro parecido com este será exibido: "Fatal error: Call to undefined method Mock_ClasseUtilizadaMock_4322e2fd :: fazAlgumaCoisa()".&lt;br /&gt;&lt;br /&gt;O problema dessa solução é o caso em que a classe a ser utilizada possui verificação de tipo no recebimento dos parâmetros. Como a classe mock criada dinamicamente não é do tipo esperado, ocorrerá um erro. Veja um exemplo de como isso aconteceria abaixo:&lt;br /&gt;&lt;div class="codigo maximo"&gt;&lt;pre&gt;class ClasseUtilizada {   &lt;br /&gt;    function ClasseUtilizada() {&lt;br /&gt;        echo "\n----&gt;passou no construtor de ClasseUtilizada&lt;-----\n";&lt;br /&gt;    }&lt;br /&gt;    public function fazAlgumaCoisa() {&lt;br /&gt;        return "uhuuu";&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;class ClasseASerTestadaComVerificacaoDeTipo {&lt;br /&gt;    function ClasseASerTestada(ClasseUtilizada $in) {&lt;br /&gt;        echo "\n[" . $in-&gt;fazAlgumaCoisa() . "]\n";&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;class MeuTeste extends PHPUnit_Framework_TestCase {&lt;br /&gt;    /**&lt;br /&gt;     * @Test&lt;br /&gt;     */&lt;br /&gt;    public function teste_com_classe_inexistente_na_classe_com_verificacao_de_tipo() {&lt;br /&gt;        $mock = $this-&gt;getMock("ClasseUtilizadaMock", Array('fazAlgumaCoisa'));&lt;br /&gt;        $mock-&gt;expects($this-&gt;once())&lt;br /&gt;            -&gt;method('fazAlgumaCoisa')&lt;br /&gt;            -&gt;will($this-&gt;returnValue(5));&lt;br /&gt;        new ClasseASerTestadaComVerificacaoDeTipo($mock);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;Nesse caso o erro exibido será "Argument 1 passed to ClasseASerTestadaComVerificacaoDeParametro :: ClasseASerTestadaComVerificacaoDeParametro() must be an instance of ClasseUtilizada"&lt;br /&gt;&lt;br /&gt;Para esses casos não tem jeito, é preciso alterar as classes para que exista uma interface a ser mocada. Mas eu sugiro que não utilize a verificação de tipo nos parâmetros, caso contrário, seria melhor deixar de lado uma linguagem dinâmica como &lt;a href="http://www.php.net"&gt;PHP&lt;/a&gt; e usar uma mais estática como  &lt;a href="http://java.sun.com"&gt;Java&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-6418762954364011140?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/6418762954364011140/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/04/mock-de-classe-no-phpunit-executa-o.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/6418762954364011140'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/6418762954364011140'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/04/mock-de-classe-no-phpunit-executa-o.html' title='Mock de classe no PHPUnit executa o construtor'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-687281599848456412</id><published>2008-03-19T23:41:00.007-03:00</published><updated>2008-03-20T00:10:10.210-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='firefox'/><title type='text'>GreaseMonkey para feed do InfoBlogs no GoogleReader</title><content type='html'>Quem assina o feed do &lt;a href="http://www.infoblogs.com.br"&gt;InfoBlogs&lt;/a&gt; deve ter o mesmo problema que eu. O link deste feed não é para o post em si, ele é para uma cópia do post na página do próprio &lt;a href="http://www.infoblogs.com.br"&gt;InfoBlogs&lt;/a&gt;. Particularmente acho isso irritante, pois só costumo entrar no blog do autor quando o post é interessante e desejo comentá-lo. Nesse caso, preciso aguardar o carregamento (que é bem lento) da página do &lt;a href="http://www.infoblogs.com.br"&gt;InfoBlogs&lt;/a&gt; para só depois poder clicar e ver o post original, onde eu posso comentar.&lt;br /&gt;&lt;br /&gt;Para resolver esse problema implementei um script no &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/748"&gt;GreaseMonkey&lt;/a&gt; para remontar a página do GoogleReader com os links corretos. Para quem não sabe o &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/748"&gt;GreaseMonkey&lt;/a&gt; é uma extensão do &lt;a href="http://www.mozilla-europe.org/en/products/firefox/"&gt;Firefox, o melhor navegador existente na atualidade&lt;/a&gt;, que permite ao usuário configurar scripts para serem rodados ao término do carregamento de uma determinada página. Eu falei brevemente dele no post em que falo sobre as &lt;a href="http://programandosemcafeina.blogspot.com/2007/04/extenses-do-firefox-para.html"&gt;extensões do Firefox essenciais para um desenvolvedor web&lt;/a&gt;, e agora com este exemplo ficará mais fácil entender como ele funciona.&lt;br /&gt;&lt;br /&gt;O código do script encontra-se abaixo. O que ele faz é verificar a cada 3 segundos se houve alguma alteração de estado no &lt;a href="http://www.google.com/reader"&gt;GoogleReader&lt;/a&gt;. Para então mudar os links caso haja necessidade. Só há uma ressalva, ele não funciona para o modo de visualização de lista, só a visão expandida. O motivo é que não gosto da visualização em lista e portanto não me interessei em testar nela :P&lt;br /&gt;&lt;div class="codigo maximo"&gt;&lt;pre&gt;// ==UserScript==&lt;br /&gt;// @name           GoogleReaderGM&lt;br /&gt;// @namespace      http://www.google.com/reader&lt;br /&gt;// @include        http://www.google.com/reader/*&lt;br /&gt;// ==/UserScript==&lt;br /&gt;&lt;br /&gt;function GoogleReaderGM() {}&lt;br /&gt;GoogleReaderGM.prototype = {&lt;br /&gt;      isChanged: function() {&lt;br /&gt;              var e = document.getElementById("entries");&lt;br /&gt;              var ds = e.getElementsByTagName("div");&lt;br /&gt;              if( this._oldUrl != window.location.href ) {&lt;br /&gt;                      this._oldUrl = window.location.href;&lt;br /&gt;                      this._oldQtds = ds.length;&lt;br /&gt;                      return true;&lt;br /&gt;              }&lt;br /&gt;              if( this._oldQtds != ds.length ) {&lt;br /&gt;                      this._oldQtds = ds.length;&lt;br /&gt;                      return true;&lt;br /&gt;              }&lt;br /&gt;              return false;&lt;br /&gt;      },&lt;br /&gt;      mudaLinksInfoBlogs: function() {&lt;br /&gt;              if( this.isChanged() ) {&lt;br /&gt;                      var e = document.getElementById("entries");&lt;br /&gt;                      var as = e.getElementsByTagName("a");&lt;br /&gt;                      for( i=0; i&amp;lt;as.length; i++ ) {&lt;br /&gt;                              var a = as[i];&lt;br /&gt;                              if( a.className=="entry-title-link" &amp;&amp; a.href.indexOf("www.infoblogs.com.br/view.action")!=-1 ) {&lt;br /&gt;                                      a.href = a.href.replace("view","redir/go");&lt;br /&gt;                              }&lt;br /&gt;                      }&lt;br /&gt;              }&lt;br /&gt;      }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;var gr = new GoogleReaderGM();&lt;br /&gt;&lt;br /&gt;setInterval(function() { gr.mudaLinksInfoBlogs(); }, 3000);&lt;/pre&gt;&lt;/div&gt;Como pode ser visto eu defino como um comentário JavaScript em quais páginas esse script será executado. No caso, todas aquelas abaixo de http://www.google.com/reader. &lt;br /&gt;&lt;br /&gt;O interessante é que se você quiser pode utilizar esse código como template para executar outras funcionalidades no &lt;a href="http://www.google.com/reader"&gt;GoogleReader&lt;/a&gt;. Basta adicionar mais alguma função, e utilizando &lt;b&gt;isChanged&lt;/b&gt; você fica sabendo se alguma entrada nova apareceu na área que exibe as entradas dos feeds. Isso é claro se você utilizar a visão expandida (Expanded view), pois o método &lt;b&gt;isChanged&lt;/b&gt; só foi feito pra ela.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-687281599848456412?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/687281599848456412/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/03/greasemonkey-para-feed-do-infoblogs-no.html#comment-form' title='5 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/687281599848456412'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/687281599848456412'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/03/greasemonkey-para-feed-do-infoblogs-no.html' title='GreaseMonkey para feed do InfoBlogs no GoogleReader'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-8927935335115136193</id><published>2008-03-13T11:18:00.008-03:00</published><updated>2008-03-13T14:11:34.417-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><title type='text'>Escapando textos em funções de javascript executadas em links</title><content type='html'>Para determinar a execução de uma função javascript no clique de um link temos duas possibilidades. Ou utilizamos o próprio atributo href da tag &lt;b&gt;a&lt;/b&gt; com o protocolo &lt;b&gt;javascript:&lt;/b&gt; ou adicionamos o atributo onclick fazendo a chamada à função.&lt;br /&gt;&lt;br /&gt;Contudo o comportamento de ambos não é totalmente igual. Principalmente quando chamamos funções que passam como argumento algum texto escapado com a função &lt;b&gt;escape&lt;/b&gt;. Normalmente usa-se esse escape para evitar error de javascript devido ao uso de aspas simples e duplas nos textos.&lt;br /&gt;&lt;br /&gt;Veja abaixo dois exemplos de cógigo com o erro por não usar escape. O primeiro com aspas simples, que faz com que o javascript ache que o texto terminou antes do que deveria e o segundo com aspas duplas fazendo o navegador acreditar que o atributo terminou:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;var textoComAspaSimples = "Quero um copo d'água";&lt;br /&gt;var s = '&amp;lt;a href="javascript:alert(\'' + textoComAspaSimples+ '\')"&amp;gt;Clique aqui&amp;lt;/a&amp;gt;';&lt;br /&gt;document.writeln(s);&lt;br /&gt;&lt;br /&gt;var textoComAspaDupla = 'Você está com "sede"?';&lt;br /&gt;var s = '&amp;lt;a href="javascript:void(0)" onclick="alert(\'' + textoComAspaDupla+ '\')"&amp;gt;Clique aqui&amp;lt;/a&amp;gt;';&lt;br /&gt;document.writeln(s);&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;Para evitar esses erros uma boa idéia é utilizar a função escape. No entanto isso não funciona direito por href. Neste atributo o navegador interpreta o código escapado causando o mesmo erro. Isso pode ser observado no exemplo a seguir, no primeiro link gerado o erro acontece, no segundo não:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;var textoComAspaSimples = "Quero um copo d'água";&lt;br /&gt;var s = '&amp;lt;a href="javascript:alert(\'' + escape(textoComAspaSimples) + '\')"&amp;gt;Clique aqui href&amp;lt;/a&amp;gt;&amp;lt;br/&amp;gt;';&lt;br /&gt;s += '&amp;lt;a href="javascript:void(0)" onclick="alert(\'' + escape(textoComAspaSimples) + '\')"&amp;gt;Clique aqui onclick&amp;lt;/a&amp;gt;';&lt;br /&gt;document.writeln(s);&lt;/pre&gt;&lt;/div&gt;O problema é que com essa solução a função que recebe este parâmetro precisa executar uma transformação reversa utilizando a função &lt;b&gt;unescape&lt;/b&gt;. Caso contrário veremos algo parecido com o que o segundo link do exemplo acima mostra: Quero%20um%20copo%20d%27%C3%A1gua.&lt;br /&gt;&lt;br /&gt;Implementando a transformação reversa na função que recebe teríamos algo como o mostrado abaixo. Uma nova função que recebe o parâmetro escapado e o transforma para utilizá-lo:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;function escapedAlert(s) {&lt;br /&gt; alert( unescape(s) );&lt;br /&gt;}&lt;br /&gt;var textoTodoErrado = "Sentes \"sede\"?\nQuero um copo d'água";&lt;br /&gt;var s = '&amp;lt;a href="javascript:void(0)" onclick="escapedAlert(\'' + escape(textoTodoErrado) + '\')"&amp;gt;Clique aqui onclick&amp;lt;/a&amp;gt;';&lt;br /&gt;document.writeln(s);&lt;/pre&gt;&lt;/div&gt;Mas isso ainda não é o ideal pois continuaríamos sem poder utilizar a função dentro do atributo href, e além disso precisaríamos sempre preparar as funções para receber parâmetros escapados. Uma outra alternativa é criar nossa própria função de escape. Como é mostrado no exemplo abaixo:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;function escapeForLink(s) {&lt;br /&gt; if(s) {&lt;br /&gt;  var r = s.split("\n").join("\\n");&lt;br /&gt;  r = r.split("\r").join("\\r");&lt;br /&gt;  r = r.split("'").join("\\u0027");&lt;br /&gt;  r = r.split('"').join("\\u0022");&lt;br /&gt;  return r;&lt;br /&gt; }&lt;br /&gt; return s;&lt;br /&gt;}&lt;br /&gt;var textoTodoErrado = "Sentes \"sede\"?\nQuero um copo d'água";&lt;br /&gt;var s = '&amp;lt;a href="javascript:alert(\'' + escapeForLink(textoTodoErrado) + '\')"&amp;gt;Clique aqui href&amp;lt;/a&amp;gt;&amp;lt;br/&amp;gt;';&lt;br /&gt;s += '&amp;lt;a href="javascript:void(0)" onclick="alert(\'' + escapeForLink(textoTodoErrado) + '\')"&amp;gt;Clique aqui onclick&amp;lt;/a&amp;gt;';&lt;br /&gt;document.writeln(s);&lt;/pre&gt;&lt;/div&gt;Com essa função os problemas de escape para passagem de parâmetro de texto em funções executadas apartir de links fica resolvido.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-8927935335115136193?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/8927935335115136193/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/03/escapando-textos-em-funes-de-javascript.html#comment-form' title='1 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/8927935335115136193'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/8927935335115136193'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/03/escapando-textos-em-funes-de-javascript.html' title='Escapando textos em funções de javascript executadas em links'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-2673781927895581740</id><published>2008-02-27T23:25:00.000-03:00</published><updated>2008-02-27T23:27:12.896-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='vraptor'/><title type='text'>Component do VRaptor só pode ter um construtor público</title><content type='html'>Nem tudo são flores com o framework MVC VRaptor. Uma classe anotada com Component só pode ter um construtor público. Senão o seguinte erro é exibido na inicialização do container web:&lt;div class="codigo"&gt;&lt;pre&gt;org.vraptor.config.ConfigException: cannot load classes&lt;br /&gt;...&lt;br /&gt;Caused by: org.vraptor.component.InvalidComponentException: &lt;br /&gt;com.globo.ekzameno.vraptor.QuestionarioLogic component has 2 accessible constructors. &lt;br /&gt;This is not desirable as it may reflect optional arguments and create complex attributes.&lt;/pre&gt;&lt;/div&gt;Ok, ok, isso não é o fim do mundo para o VRaptor, mas me atrapalhou um pouco com os testes unitários, pois eu pretendia fazer injeção de dependência por construtor. Como esse framework não me permite isso, terei que fazer por método set. Ou seja, ao invés de fazer assim:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;public TiagoLogic() {&lt;br /&gt; this(new RepositorioImpl());&lt;br /&gt;}&lt;br /&gt;public TiagoLogic(Repositorio repositorio) {&lt;br /&gt; this.repositorio = repositorio;&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;Terei que fazer assim:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;public TiagoLogic() {&lt;br /&gt; setRepositorio(new RepositorioImpl());&lt;br /&gt;}&lt;br /&gt;public void setRepositorio(Repositorio repositorio) {&lt;br /&gt; this.repositorio = repositorio;&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;Não é um grande problema, só questão de preferência mesmo. Prefiro por construtor.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-2673781927895581740?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/2673781927895581740/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/02/component-do-vraptor-s-pode-ter-um.html#comment-form' title='4 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/2673781927895581740'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/2673781927895581740'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/02/component-do-vraptor-s-pode-ter-um.html' title='Component do VRaptor só pode ter um construtor público'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-6463230026721270617</id><published>2008-02-27T22:22:00.004-03:00</published><updated>2008-03-13T16:29:30.820-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='vraptor'/><title type='text'>NoClassDefFoundError de Expectations no VRaptor 2</title><content type='html'>&lt;hr/&gt;Atenção, não consegui mais replicar o problema descrito neste post. Provavelmente a necessidade da classe não era do VRaptor.&lt;hr/&gt;&lt;br /&gt;&lt;br /&gt;Comecei a utilizar o &lt;a href="http://www.vraptor.com.br/"&gt;VRaptor&lt;/a&gt; em um projeto pessoal e na primeira tentativa obtive um erro que foi simples corrigir, mas que achei pertinente postar aqui porque não encontrei no google ninguém com o mesmo problema e porque a documentação deste framework não informava nada sobre.&lt;br /&gt;&lt;br /&gt;O erro que obtive foi o seguinte:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;javax.servlet.ServletException: Servlet.init() for servlet vraptor2 threw exception&lt;/pre&gt;&lt;/div&gt;Cuja causa foi:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;java.lang.NoClassDefFoundError: org/jmock/Expectations&lt;/pre&gt;&lt;/div&gt;Para resolver isso bastou incluir no classpath da aplicação o jar do &lt;a href="http://www.jmock.org"&gt;JMock&lt;/a&gt;. Esse jar não é incluso no blank-project oferecido no site do &lt;a href="http://www.vraptor.com.br"&gt;VRaptor&lt;/a&gt;, por isso acho que outras pessoas poderão passar pelo mesmo problema.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-6463230026721270617?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/6463230026721270617/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/02/noclassdeffounderror-de-expectations-no.html#comment-form' title='4 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/6463230026721270617'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/6463230026721270617'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/02/noclassdeffounderror-de-expectations-no.html' title='NoClassDefFoundError de Expectations no VRaptor 2'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-3451664670638442044</id><published>2008-02-26T14:47:00.005-03:00</published><updated>2008-02-26T20:14:16.722-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><title type='text'>Problema do gets com uso de parametros na linha de comando em ruby</title><content type='html'>Brincando um pouco com ruby me deparei com um problema que parecia um pouco exotérico. Utilizando a função &lt;b&gt;gets&lt;/b&gt; num script que recebe parâmetros por linha de comando, o seguinte erro era exibido:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;tiago@tiago-laptop:~/Projetos/testeRuby$ ruby Pessoa.rb Tiago&lt;br /&gt;Pessoa.rb:14:in `gets': No such file or directory - Tiago (Errno::ENOENT)&lt;/pre&gt;&lt;/div&gt;Esse mesmo erro não ocorria quando não era enviado nenhum argumento na linha de comando. Pesquisando sobre o assunto no Google, &lt;a href="http://groups.google.com/group/ruby-talk-google/browse_thread/thread/298fc833a82932b7"&gt;encontrei uma discussão&lt;/a&gt; que clareou minhas idéias. O Ruby herda o comportamento da função &lt;b&gt;gets&lt;/b&gt; do Perl, ou seja, se houver um parâmetro na linha de comando, essa função assume que este parâmetro é o path de um arquivo e portanto tenta abrí-lo.&lt;br /&gt;&lt;br /&gt;Para resolver essa questão basta deixar claro que se deseja que a função gets recupere apenas o texto digitado. Na verdade ao invés de usarmos &lt;b&gt;gets&lt;/b&gt; puro devemos usar o do &lt;b&gt;STDIN&lt;/b&gt; da seguinte maneira &lt;b&gt;STDIN.gets&lt;/b&gt;.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-3451664670638442044?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/3451664670638442044/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/02/problema-do-gets-com-uso-de-parametros.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3451664670638442044'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3451664670638442044'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/02/problema-do-gets-com-uso-de-parametros.html' title='Problema do gets com uso de parametros na linha de comando em ruby'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-6219338702582688545</id><published>2008-01-30T04:55:00.000-03:00</published><updated>2008-01-30T21:20:11.825-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='globo.com'/><title type='text'>Tela cheia em flash no Globo Vídeos Player</title><content type='html'>Subiu agora pouco uma nova versão do &lt;a href="http://video.globo.com"&gt;Globo Vídeos&lt;/a&gt; contendo diversas melhorias e algumas correções pontuais. A maioria dessas alterações poucos irão perceber, pois são problemas que somente o controle de qualidade interno tem a paciência de procurar. Mais uma das melhorias sem duvida será muito bem notada, o botão de tela cheia.&lt;br /&gt;&lt;br /&gt;Na versão anterior, que usava &lt;a href="http://programandosemcafeina.blogspot.com/search/label/wmp"&gt;Windows Media Player&lt;/a&gt;, a funcionalidade de tela cheia existia para os usuários de Internet Explorer. Quando entrou o player em &lt;a href="http://programandosemcafeina.blogspot.com/search/label/flash"&gt;flash&lt;/a&gt; ela foi removida. Portanto muitos usuários reclamaram de sua remoção.&lt;br /&gt;&lt;br /&gt;Acontece que fazer o full screen no &lt;a href="http://programandosemcafeina.blogspot.com/search/label/flash"&gt;flash&lt;/a&gt; não é uma tarefa trivial devido as diversas versões de &lt;a href="http://programandosemcafeina.blogspot.com/search/label/flash"&gt;flash&lt;/a&gt; que teríamos que suportar. Assim preferimos implementar uma versão mínima do player antes do &lt;a href="http://video.globo.com/Videos/0,,GOD0-5633-programa-4412,00.html"&gt;Big Brother&lt;/a&gt;, para posteriormente implementar essas pequenas melhorias. Se tivessemos optado por lançar já com um player completo, talvez só pudéssemos entregá-lo ao final do &lt;a href="http://video.globo.com/Videos/0,,GOD0-5633-programa-4412,00.html"&gt;Big Brother&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Eu particularmente acredito que tomamos a decisão correta. Apesar de termos perdido por um mês a funcionalidade de tela cheia, milhares de usuários que não conseguiam ver vídeos, por causa da M que é o &lt;a href="http://programandosemcafeina.blogspot.com/search/label/wmp"&gt;Windows Media Player&lt;/a&gt;, agora conseguem. E melhor ainda, agora também têm o botãozinho de tela cheia.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-6219338702582688545?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/6219338702582688545/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/01/tela-cheia-em-flash-no-globo-vdeos.html#comment-form' title='2 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/6219338702582688545'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/6219338702582688545'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/01/tela-cheia-em-flash-no-globo-vdeos.html' title='Tela cheia em flash no Globo Vídeos Player'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-466034076489337503</id><published>2008-01-14T20:45:00.000-03:00</published><updated>2008-01-14T20:49:30.945-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='flash'/><title type='text'>Repassando parâmetros rest para funções que esperam parâmetros rest</title><content type='html'>No ActionScript 3 existe um parâmetro especial, comumente chamando de parâmetro rest, que permite que uma função receba "infinitos" parâmetros. O Java, apartir da versão 5, também possui essa facilidade, que é chamada de varargs. Veja abaixo o exemplo de uma função e o logo abaixo o uso dela: &lt;div class="codigo"&gt;&lt;pre&gt;//Funcao que mostra todos os parametros&lt;br /&gt;public function mostra(... numeros) {&lt;br /&gt;    if( numeros &amp;&amp; numeros.length &gt; 0 ) {&lt;br /&gt;        for each( var i in numeros ) {&lt;br /&gt;            trace(i);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;//Uso da funcao:&lt;br /&gt;mostra(1);&lt;br /&gt;mostra(1,2,3);&lt;br /&gt;mostra(1,2,3,5,8);&lt;/pre&gt;&lt;/div&gt;Apesar de ser um recurso muito bom, a forma como ela foi implementada no ActionScript nos traz um problema no momento em que precisamos repassar esses parâmetros para uma outra função que também receba parâmetros opcionais rest. &lt;br /&gt;&lt;br /&gt;Isso acontece porque quando a primeira função é chamada, ela é informada com os parâmetros separados por vírgula, mas ela os recebe como um array. Então se você enviar este array para o segundo método, ele será considerado apenas um parâmetro. No Java há um tratamento mais esperto, de forma que quando enviado um array o compilador não permite que mais nenhum parâmetro seja colocado na chamada à função.&lt;br /&gt;&lt;br /&gt;Para ilustrar melhor vamos pensar num cenário que mostrei no post passado sobre &lt;a href="/2008/01/como-testar-funes-que-fazem-chamadas.html"&gt;como testar funções que executem javascript&lt;/a&gt;. No caso era uma função proxy para isolarmos a execução de &lt;b&gt;ExternalInterface.call&lt;/b&gt;:&lt;div class="codigo"&gt;&lt;pre&gt;public function executarJavaScript(funcao:String, ... parametros) {&lt;br /&gt;    try {&lt;br /&gt;        return ExternalInterface.call(funcao,parametros);&lt;br /&gt;    } catch(erro:Error) {&lt;br /&gt;        return null;&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;No caso, a função &lt;b&gt;call&lt;/b&gt; de &lt;b&gt;ExternalInterface&lt;/b&gt; também espera receber parâmetros opcionais, mas como estamos mandando um array, ele achará que estamos mandando apenas dois parâmetros, um com o nome da função javascript e outro com um array. No Google é fácil encontrar uma solução POG e muito feia para isso. Nada mais é que uma cadeia megazord de ifs:&lt;div class="codigo"&gt;&lt;pre&gt;public function executarJavaScript(funcao:String, ... p) {&lt;br /&gt;    try {&lt;br /&gt;        if( p.length == 0 ) return ExternalInterface.call(funcao,null);&lt;br /&gt;        else if( p.length == 1 ) return ExternalInterface.call(funcao,p[0]);&lt;br /&gt;        else if( p.length == 2 ) return ExternalInterface.call(funcao,p[0],p[1]);&lt;br /&gt;        else if( p.length == 3 ) return ExternalInterface.call(funcao,p[0],p[1],p[2]);&lt;br /&gt;        else if( p.length == 4 ) return ExternalInterface.call(funcao,p[0],p[1],p[2],p[3]);&lt;br /&gt;    } catch(erro:Error) {&lt;br /&gt;        return null;&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;Ô coisa feia!&lt;br /&gt;&lt;br /&gt;A solução mais elegante para esse problema é utilizar os recurso de reflexão do ActionScript. Ou seja, vamos utilizar o método &lt;b&gt;apply&lt;/b&gt; da classe &lt;b&gt;Function&lt;/b&gt;, que aguarda os parâmetros como um array, exatamente como recebemos na função ao utilizar um parâmetro rest. Veja como ficaria:&lt;div class="codigo"&gt;&lt;pre&gt;public function executarJavaScript(funcao:String, ... parametros) {&lt;br /&gt;    try {&lt;br /&gt;        var f:Function = ExternalInterface.call;&lt;br /&gt;        var parametrosEnviar:Array = new Array(funcao);&lt;br /&gt;        parametrosEnviar = parametrosEnviar.concat(parametros);&lt;br /&gt;        return f.apply(null,parametrosEnviar);&lt;br /&gt;    } catch(erro:Error) {&lt;br /&gt;        return null;&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;No caso, concatenamos num array de parâmetros a serem enviados a função e os parâmetros rest. Ao enviá-los pela função apply eles são enviados como se fossem diversos parâmetros separados por vírgula e o método call os recebe corretamente como parâmetros rest.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-466034076489337503?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/466034076489337503/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/01/repassando-parmetros-rest-para-funes.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/466034076489337503'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/466034076489337503'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/01/repassando-parmetros-rest-para-funes.html' title='Repassando parâmetros rest para funções que esperam parâmetros rest'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-2341543879147492440</id><published>2008-01-13T18:47:00.000-03:00</published><updated>2008-01-13T18:52:47.894-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='flash'/><title type='text'>Como testar funções que fazem chamadas javascript no actionscript?</title><content type='html'>No ActionScript é possível chamar funções javascript utilizando a função &lt;b&gt;ExternalInterface.call&lt;/b&gt;. O problema é que essa função é estática, dificultando muito a criação de testes unitários para as funções que necessitam acessar esses recursos externos.&lt;br /&gt;&lt;br /&gt;Para fazer isso é preciso isolar a chamada ao javascript em uma função, e no teste unitário substituir essa função por uma outra que apenas a substitua por uma comportamento esperado de acordo com o teste. Vamos ver como fazer isso:&lt;br /&gt;&lt;br /&gt;Primeiro isolamos a chamada a &lt;b&gt;ExternalInterface.call&lt;/b&gt; em um função que serviria apenas como proxy. Em linguagens menos dinâmicas precisaríamos isolar essa função em uma outra classe pois não seria possível substitui-la posteriormente: &lt;div class="codigo"&gt;&lt;pre&gt;public function executarJavaScript(funcao:String,parametros:Object = null) {&lt;br /&gt;    try {&lt;br /&gt;        return ExternalInterface.call(funcao,parametros);&lt;br /&gt;    } catch(erro:Error) {&lt;br /&gt;        return null;&lt;br /&gt;    }&lt;br /&gt;}&lt;/div&gt;&lt;/pre&gt;A classe então que possui essa função precisa ser dinâmica, ou seja deve ter um modificador &lt;b&gt;dynamic&lt;/b&gt;, e possuir um atributo da classe &lt;b&gt;Function&lt;/b&gt; que recebe no construtor essa função que foi criada:&lt;div class="codigo"&gt;&lt;pre&gt;dynamic public class MinhaClasse {&lt;br /&gt;    public var js:Function;&lt;br /&gt;    function MinhaClasse() {&lt;br /&gt;        js = executarJavaScript;&lt;br /&gt;    }&lt;br /&gt;    public function executarJavaScript(funcao:String,parametros:Object = null) {&lt;br /&gt;        //Conteudo da função já exibido acima :P&lt;br /&gt;    }&lt;br /&gt;    //Outras funções&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;As demais funções da classe deverão usar então o atributo &lt;b&gt;js&lt;/b&gt; como uma função para executar o recurso externo desejado. Veja um exemplo que recupera do javascript a URL onde o swf está sendo exibido:&lt;div class="codigo"&gt;&lt;pre&gt;public function obterUrlAtual():String {&lt;br /&gt;    try {&lt;br /&gt;        var objJs = js("document.location.href.toString");&lt;br /&gt;        if( !objJs ) objJs = js("document.location.toString");&lt;br /&gt;        if( !objJs ) return null;&lt;br /&gt;        return new String(objJs);&lt;br /&gt;    } catch(e:Error) {&lt;br /&gt;        return null;&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;Para criar um teste unitário com o ASUnit para esta função de exemplo basta configurar de diversas formas o atributo &lt;b&gt;js&lt;/b&gt; da classe &lt;b&gt;MinhaClasse&lt;/b&gt; de acordo com o comportamento que você deseja testar. Um exemplo é exibido abaixo:&lt;div class="codigo"&gt;&lt;pre&gt;public function testSegundFuncaoJavaScriptEhChamada():void {&lt;br /&gt;    var m:MinhaClasse = new MinhaClasse();&lt;br /&gt;    m.js = function(funcao:String,parametros:Object = null) {&lt;br /&gt;        if(funcao=="document.location.toString") {&lt;br /&gt;            return "http://programandosemcafeina.blogspot.com";&lt;br /&gt;        } else {&lt;br /&gt;            return null;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    var s:String = m.obterUrlAtual();&lt;br /&gt;    assertEquals("Quando a primeira função retorna null a segunda função deve ser chamada","http://programandosemcafeina.blogspot.com",s);&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-2341543879147492440?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/2341543879147492440/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/01/como-testar-funes-que-fazem-chamadas.html#comment-form' title='1 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/2341543879147492440'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/2341543879147492440'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/01/como-testar-funes-que-fazem-chamadas.html' title='Como testar funções que fazem chamadas javascript no actionscript?'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-3660283706057776566</id><published>2008-01-07T14:29:00.001-03:00</published><updated>2008-01-07T16:22:36.399-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='flash'/><title type='text'>Checar versão do flash de dentro do SWF</title><content type='html'>Descobrir a versão do flash por javascript é moleza. Uma rápida busca no google lhe traz dezenas de script prontos pra usar. Mas verificar a versão do flash de dentro do SWF foi díficil de encontrar. Até é bem simples. Basta usar o atributo estático &lt;b&gt;version&lt;/b&gt; da classe &lt;b&gt;flash.system.Capabilities&lt;/b&gt;. Assim:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;trace("versao: " + Capabilities.version);&lt;/pre&gt;&lt;/div&gt;Simples, porém foi díficil encontrar. Como esse código só funciona no Action Script 3, fica parecendo que ele não tem muita utilidade. Mas acontece que existem pequenas diferenças entre as chamadas minor revisions que precisam ser verificadas no projeto em que estou trabalhando.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-3660283706057776566?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/3660283706057776566/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/01/checar-verso-do-flash-de-dentro-do-swf.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3660283706057776566'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3660283706057776566'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/01/checar-verso-do-flash-de-dentro-do-swf.html' title='Checar versão do flash de dentro do SWF'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-7039930848497605251</id><published>2008-01-02T18:16:00.001-03:00</published><updated>2008-01-02T18:17:40.226-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='apache'/><title type='text'>Redirecionar requisições do Apache para uma imagem</title><content type='html'>Para redirecionar todas as requisições do apache para uma determinada imagem basta utilizar o módulo &lt;b&gt;mod_rewrite&lt;/b&gt;. &lt;br /&gt;Se você ainda não utiliza este módulo do apache, é preciso descomentar no arquivo &lt;b&gt;httpd.conf&lt;/b&gt; o carregamento dele:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;LoadModule rewrite_module modules/mod_rewrite.so&lt;/pre&gt;&lt;/div&gt;Em seguida deve-se ativar o módulo e criar uma regra, também alterando o mesmo arquivo:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;RewriteEngine On&lt;br /&gt;RewriteRule   /*  /empty.gif&lt;/pre&gt;&lt;/div&gt;Neste caso o comando &lt;b&gt;RewriteRule&lt;/b&gt; informa que todas as requisições devem ser atendidas pelo recurso /empty.gif.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-7039930848497605251?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/7039930848497605251/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/01/redirecionar-requisies-do-apache-para.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/7039930848497605251'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/7039930848497605251'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2008/01/redirecionar-requisies-do-apache-para.html' title='Redirecionar requisições do Apache para uma imagem'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-4808436601976148418</id><published>2007-12-28T01:24:00.000-03:00</published><updated>2007-12-28T02:38:28.650-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>Problema com as bordas das janelas usando o Beryl</title><content type='html'>Esse sim foi um sufoco. O Beryl não queria funcionar de jeito nenhum no meu Ubuntu. Diversos probleminhas foram faceis após simples pesquisas no google. Mas o problema maior não tinha resposta alguma. Quando o beryl estava rodando as bordas das janelas não apareciam de jeito algum. &lt;br /&gt;&lt;br /&gt;A mensagem de erro do Beryl que me fez acordar para a razão do problema foi a seguinte:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;beryl: decoration: property ignored because version is 20070319 and decoration plugin version is 20061011&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Percebi que devia haver alguma incompatibilidade entre a versão do beryl e a do emerald ou coisa parecida. E realmente havia. O jeito foi achar uma denominador comum entre a versão do emerald, beryl e demais libs. Para fazer isso precisei fazer um downgrade da parada toda:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;sudo apt-get install beryl=0.1.9999.2~0beryl1 &lt;br /&gt;beryl-core=0.1.9999.2~0beryl1 &lt;br /&gt;beryl-manager=0.1.9999.2~0beryl1 &lt;br /&gt;beryl-plugins-data=0.1.9999.2~0beryl1 &lt;br /&gt;beryl-settings=0.1.9999.2~0beryl1 &lt;br /&gt;beryl-settings-bindings=0.1.9999.2~0beryl1 &lt;br /&gt;emerald=0.1.9999.2~0beryl1  &lt;br /&gt;libberyldecoration0=0.1.9999.2~0beryl1  &lt;br /&gt;libberylsettings0=0.1.9999.2~0beryl1  &lt;br /&gt;libemeraldengine0=0.1.9999.2~0beryl1 &lt;br /&gt;emerald-themes=0.1.9999.2~0beryl1  -V&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;O engraçado é que ontem de madrugada, depois que fiz o downgrade, não funcionou. Mas isso porque eu tinha configurado meu Beryl Manager para utilizar o Window Manager do Compiz, pois este funcionava bem. Contudo, com essa versão mais antiga do beryl, o Compiz parou de funcionar. Mas isso não tentarei consertar, ninguém precisa desse Compiz. Bastou então setar no Beryl Manager o Beryl como Windows Manager para tudo funcionar.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.8p.com.br/chapundifornio/flog/#a579-5317177"&gt;Olha a foto de como ficou meu cubo :)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Cada dia que passa me convenço mais da facilidade do Windows. Mas até que é divertido brincar com esse Ubuntu.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-4808436601976148418?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/4808436601976148418/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/12/problema-com-as-bordas-das-janelas.html#comment-form' title='3 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/4808436601976148418'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/4808436601976148418'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/12/problema-com-as-bordas-das-janelas.html' title='Problema com as bordas das janelas usando o Beryl'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-5308762849319939961</id><published>2007-12-19T04:55:00.000-03:00</published><updated>2007-12-19T05:09:56.585-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='globo.com'/><title type='text'>Globo Vídeos agora em Flash!</title><content type='html'>É com muito orgulho que anuncio que finalmente o Globo Vídeos entrou na era do Flash Vídeo. Os vídeos ao vivo, como da Globo News e as cameras do BBB continuarão em Windows Media Player, mas apartir de hoje a grande maioria dos vídeos on demand e íntegras ficarão disponíveis em flash.&lt;br /&gt;&lt;br /&gt;Veja alguns exemplos:&lt;br /&gt;&lt;a href="http://video.globo.com/Videos/Player/Noticias/0,,GIM766320-7823-DOMINO,00.html"&gt;Dominó é mania nacional em Cuba&lt;/a&gt;&lt;br /&gt;&lt;a href="http://video.globo.com/Videos/Player/Noticias/0,,GIM763798-7823-LENDA+OU+REALIDADE,00.html"&gt;Romulo e Reno: Lenda ou Realidade?&lt;/a&gt;&lt;br /&gt;&lt;a href="http://video.globo.com/Videos/Player/Noticias/0,,GIM763795-7823-FESTIVAL+DE+GELO,00.html"&gt;Festival de gelo na Bélgica&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-5308762849319939961?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/5308762849319939961/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/12/globo-vdeos-agora-em-flash.html#comment-form' title='1 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/5308762849319939961'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/5308762849319939961'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/12/globo-vdeos-agora-em-flash.html' title='Globo Vídeos agora em Flash!'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-5776141070174943313</id><published>2007-12-18T15:58:00.000-03:00</published><updated>2007-12-18T16:19:35.783-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><title type='text'>Como obter a largura e a altura de Divs liquidos</title><content type='html'>Obter os atributos width e height de Divs absolutos é muito simples. A coisa começa a ficar complicada quando seu Div não possui uma altura e uma largura fixa, se comportando de acordo com o restante da página. Encontrei em alguns blogs diversas dicas de como fazer isso. Alguns só funcionavam no Firefox, outros só no Internet Explorer, nenhum em ambos.&lt;br /&gt;&lt;br /&gt;Com essas informações então criei as funções getWidth e getHeight que retornam a largura e altura de um div, mesmo que está seja fluída:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo maximo"&gt;&lt;pre&gt;function getHeight(d) {&lt;br /&gt;    if( document.all ) {&lt;br /&gt;        return eval( "document.all." + d.id + ".offsetHeight" );&lt;br /&gt;    } else {&lt;br /&gt;        return parseInt( getStyle(d,"height") );&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function getWidth(d) {&lt;br /&gt;    if( document.all ) {&lt;br /&gt;        return eval( "document.all." + d.id + ".offsetWidth" );&lt;br /&gt;    } else {&lt;br /&gt;        return parseInt( getStyle(d,"width") );&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function getStyle(el, style) {&lt;br /&gt;    if(!document.getElementById) return;&lt;br /&gt;    if( window.getComputedStyle ) {&lt;br /&gt;        value = document.defaultView.getComputedStyle(el, "").getPropertyValue(style);&lt;br /&gt;    } else if(document.defaultView) {&lt;br /&gt;        value = document.defaultView.getComputedStyle(el, "").getPropertyValue(style);&lt;br /&gt;    } else if(el.currentStyle) {&lt;br /&gt;        value = el.currentStyle[style];&lt;br /&gt;    }&lt;br /&gt;    return value;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Para usá-las é só chamar enviando por parâmetro um objeto div. Veja o exemplo:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;var d = document.getElementById("meuDiv");&lt;br /&gt;alert( getWidth( d ) );&lt;br /&gt;alert( getHeight( d ) );&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Testei essas funções com o Firefox 2, IE 7 e o Safari 3.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-5776141070174943313?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/5776141070174943313/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/12/como-obter-largura-e-altura-de-divs.html#comment-form' title='1 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/5776141070174943313'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/5776141070174943313'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/12/como-obter-largura-e-altura-de-divs.html' title='Como obter a largura e a altura de Divs liquidos'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-4615360660822678595</id><published>2007-09-06T17:36:00.000-03:00</published><updated>2008-01-23T09:31:25.526-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='rhino'/><category scheme='http://www.blogger.com/atom/ns#' term='weblogic'/><title type='text'>Como utilizar uma versão nova do Rhino no Weblogic 8</title><content type='html'>Em recente post mostrei a vocês como descobri que o &lt;a href="http://programandosemcafeina.blogspot.com/2007/09/script-bash-para-encontrar-classes-em.html"&gt;weblogic.jar contém as classes do &lt;a href="http://www.mozilla.org/rhino/"&gt;Rhino&lt;/a&gt; numa versão antiga&lt;/a&gt;. Agora vou mostrar porque tentava descobrir isso e como resolvi a situação.&lt;br /&gt;&lt;br /&gt;Tudo começou quando ao subir nossa aplicação, que utiliza o js.jar do &lt;a href="http://www.mozilla.org/rhino/"&gt;Rhino&lt;/a&gt;, para o weblogic, começamos a obter a seguinte exceção.&lt;div class="codigo"&gt;&lt;pre&gt;java.lang.NoSuchMethodError: org.mozilla.javascript.Context.initStandardObjects()Lorg/mozilla/javascript/ScriptableObject;&lt;/pre&gt;&lt;/div&gt;Logo descobri a referida bizarrice do weblogic.jar. Começou então o nosso desafio: Como fazer a nossa aplicação utilizar as classes do Rhino contidas no js.jar que levávamos no WEB-INF/lib e não os do weblogic.jar?&lt;br /&gt;&lt;br /&gt;O &lt;a href="http://blog.fragmental.com.br/"&gt;Phillip Calçado, líder de nossa equipe&lt;/a&gt;, me deu uma ótima sugestão. Criar um Class Loader para carregar as classes do jar contido em nossa aplicação. Para fazer isso, extendi a classe &lt;b&gt;URLClassLoader&lt;/b&gt; e implementei o método loadClass(String). Veja como ficou o código:&lt;div class="codigo maximo"&gt;&lt;pre&gt;public class RhinoClassLoader extends URLClassLoader {&lt;br /&gt;    private static final String PREFIXO_JAVASCRIPT = "org.mozilla.javascript.";&lt;br /&gt;    private static final String PREFIXO_CLASSFILE = "org.mozilla.classfile.";&lt;br /&gt; &lt;br /&gt;    private Map classesCarregadas;&lt;br /&gt;&lt;br /&gt;    public RhinoClassLoader(URL url) {&lt;br /&gt;        super(new URL[]{ url }, RhinoClassLoader.class.getClassLoader() );&lt;br /&gt;        classesCarregadas = new HashMap();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Class loadClass(String name) throws ClassNotFoundException {&lt;br /&gt;        if( name!=null &amp;&amp; ( name.indexOf(PREFIXO_JAVASCRIPT)!=-1 || name.indexOf(PREFIXO_CLASSFILE)!=-1 ) ) {&lt;br /&gt;            Object classeCarregada = classesCarregadas.get(name);&lt;br /&gt;            if( classeCarregada != null ) {&lt;br /&gt;                return (Class) classeCarregada;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            Class classe = findClass(name);&lt;br /&gt;            classesCarregadas.put(name, classe);&lt;br /&gt;&lt;br /&gt;            return classe;&lt;br /&gt;        }&lt;br /&gt;        return super.loadClass(name);&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;Observe que o construtor recebe uma url, que deve ser o caminho pro arquivo jar do &lt;a href="http://www.mozilla.org/rhino/"&gt;Rhino&lt;/a&gt; e a repassa para o contrutor da superclasse. Já no método loadClass nós passamos a carregar somente as classes do Rhino através do método findClass da superclasse URLClassLoader. Este procurará essas classes no jar que informamos. Para as demais classes nós repassamos a chamada para o método loadClass default. Ou seja, somente as classes do Rhino estarão com o ClassLoader diferente.&lt;br /&gt;&lt;br /&gt;Outra coisa importante do código é o cache de classes anteriormente carregadas. Ele é extremamente necessário, senão pode ocorrer um erro indicando que duas classes com o mesmo nome foram carregadas.&lt;br /&gt;&lt;br /&gt;Para obter então a classe correta do &lt;a href="http://www.mozilla.org/rhino/"&gt;Rhino&lt;/a&gt;, basta utilizar o método &lt;b&gt;forName&lt;/b&gt; da classe &lt;b&gt;Class&lt;/b&gt; para recebê-la. Veja abaixo como ficaria:&lt;div class="codigo"&gt;&lt;pre&gt;URL url = new URL( "file:///path/para/o/WEB-INF/lib/js.jar" );&lt;br /&gt;RhinoClassLoader loader = new RhinoClassLoader( url );&lt;br /&gt;Class classeContext = Class.forName("org.mozilla.javascript.Context",true,loader);&lt;/pre&gt;&lt;/div&gt;Utilizando o objeto classeContext que representa a classe desejada, você pode então criar novas instancias utilizando a api de reflexão do Java. Mas tome cuidado para não causar um ClassCastException. Isso porque a classe representada em classeContext é diferente da classe de mesmo nome que você faria o cast, pois são de ClassLoaders diferentes. Logo, o seguinte cógido causaria a exceção:&lt;div class="codigo"&gt;&lt;pre&gt;Context contexto = (Context)classeContext.newInstance();&lt;/pre&gt;&lt;/div&gt;Ou seja, você só pode utilizar classes de outro ClassLoader via api de relection. Um exemplo de como seria utilizar um método de Context nesse caso é mostrado a seguir:&lt;div class="codigo"&gt;&lt;pre&gt;Object contexto = (Object)classeContext.newInstance();&lt;br /&gt;Method metodoDebug = classeContext.getMethod("isGeneratingDebug",null);&lt;br /&gt;Boolean gerandoDebug = (Boolean) metodoDebug.invoke( contexto, null );&lt;/pre&gt;&lt;/div&gt;Repare que no retorno da invocação do método refletido eu faço um cast. Neste caso eu posso, pois somente as classes dos pacotes org.mozilla.javascript e org.mozilla.classfile foram carregadas com outro ClassLoader.&lt;br /&gt;&lt;br /&gt;Esse trabalho todo porque o weblogic não sabe brincar. Custava colocar o jar do &lt;a href="http://www.mozilla.org/rhino/"&gt;Rhino&lt;/a&gt; separadinho? Tem certas coisas que não dá pra entender. Já falei isso antes, mas repetir nunca é demais. Cada dia que passa mais odeio o weblogic.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-4615360660822678595?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/4615360660822678595/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/09/como-utilizar-uma-verso-nova-do-rhino.html#comment-form' title='2 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/4615360660822678595'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/4615360660822678595'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/09/como-utilizar-uma-verso-nova-do-rhino.html' title='Como utilizar uma versão nova do Rhino no Weblogic 8'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-4787345781333471815</id><published>2007-09-03T20:17:00.001-03:00</published><updated>2007-09-03T20:28:16.104-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bash'/><category scheme='http://www.blogger.com/atom/ns#' term='weblogic'/><title type='text'>Script bash para encontrar classes em um diretório de jars</title><content type='html'>Scripts bash é uma das ferramentas mais uteis para desenvolvedores. Hoje por exemplo, para enfrentar um problema de classe duplicada no classpath da aplicação, fiz um rápido script para identificar todas as classes que seguem determinado padrão em um determinado diretório de jars. No caso, no WEB-INF/lib. Segue o script: &lt;div class="codigo"&gt;&lt;pre&gt;for i in $( ls | grep jar )&lt;br /&gt;do&lt;br /&gt;   echo "$i:"&lt;br /&gt;   jar -tvf $i | grep "javascript.Context"&lt;br /&gt;done;&lt;/pre&gt;&lt;/div&gt;No caso, estava procurando pela classe Context do Rhino, que logo descobri estava mesmo duplicada. Não no WEB-INF/lib da minha aplicação, mas dentro do weblogic.jar. Sim, por incrível que pareça o weblogic empacotou o Rhino inteiro em seu jar, com caminho de pacote e tudo, e ainda por cima numa versão bem antiga. Cada dia que passa mais odeio o weblogic.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-4787345781333471815?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/4787345781333471815/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/09/script-bash-para-encontrar-classes-em.html#comment-form' title='1 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/4787345781333471815'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/4787345781333471815'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/09/script-bash-para-encontrar-classes-em.html' title='Script bash para encontrar classes em um diretório de jars'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-5827178225176459127</id><published>2007-09-02T21:06:00.000-03:00</published><updated>2007-09-02T21:25:13.670-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='weblogic'/><title type='text'>JspException: Non-matching extension tags</title><content type='html'>Essa semana obtive a referida mensagem de erro ao colocar uma aplicação testada no tomcat para rodar no weblogic. A mensagem era mais ou menos a seguinte: &lt;div class="codigo"&gt;&lt;pre&gt;weblogic.servlet.jsp.JspException: (line 60): Non-matching extension tags //[ null; Line: 60]&lt;/pre&gt;&lt;/div&gt;Percebi que o erro ocorria na chamada a uma tag customizada de meu sistema que recebia em um atributo um parâmetro da requisição. O código era mais ou menos como o mostrado a seguir: &lt;div class="codigo"&gt;&lt;pre&gt;&amp;lt;minha:tagCustomizada nomeAtributo="&lt;%=request.getParameter("valorFornecido")%&gt;"/&amp;gt;&lt;/pre&gt;&lt;/div&gt;Ao pesquisar no google sobre o assunto, encontrei outras pessoas com o mesmo problema e uma sugestão para resolução dele. Trocar as aspas duplas por aspas simples no valor do atributo. Isso funcionou: &lt;div class="codigo"&gt;&lt;pre&gt;&amp;lt;minha:tagCustomizada nomeAtributo='&lt;%=request.getParameter("valorFornecido")%&gt;'/&amp;gt;&lt;/pre&gt;&lt;/div&gt;Parece até aceitável que o erro ocorra, pois pensando no padrão XML, a abertura do valor de atributos terminaria na próxima aspas dupla. Contudo, o JSP em questão não é um documento XML, de forma que teoricamente os scriptlets deveriam ser executados antes das tags customizadas. Tanto que no Tomcat não houve qualquer problema. Ao que tudo indica isso é um bug do Weblogic.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-5827178225176459127?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/5827178225176459127/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/09/jspexception-non-matching-extension.html#comment-form' title='1 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/5827178225176459127'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/5827178225176459127'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/09/jspexception-non-matching-extension.html' title='JspException: Non-matching extension tags'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-6979475041880070205</id><published>2007-08-22T19:44:00.001-03:00</published><updated>2008-04-01T17:25:30.431-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='selenium'/><category scheme='http://www.blogger.com/atom/ns#' term='testes'/><title type='text'>Selenium IDE aplicado a interfaces com Ajax</title><content type='html'>Fazer testes automatizados com o &lt;a href="http://www.openqa.org/selenium-ide/"&gt;Selenium IDE&lt;/a&gt; numa interface web que possui chamadas assíncronas pode se tornar um problemão. Um exemplo simples é de um link que buscaria através de HttpRequest um recurso no servidor:&lt;div class="codigo"&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;a href="javascript:void(buscarDados())"&amp;gt;Clique aqui&amp;lt;/a&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;Neste caso para testar o clique no link usando o selenium temos dois problemas. Se usarmos a função &lt;b&gt;click&lt;/b&gt; o teste seguinte poderá falhar caso o servidor demore alguns segundos para responder. E se usarmos a função &lt;b&gt;clickAndWait&lt;/b&gt; ocorrerá um timeout pois o selenium espera que a página inteira mude.&lt;br /&gt;&lt;br /&gt;Para resolver esse tipo de situação é que existem as funções iniciadas por &lt;b&gt;wait&lt;/b&gt;. Elas aguardam um bom período até que determinado elemento ou texto sejam impressos na página. Dessa forma podemos utilizar a função &lt;b&gt;click&lt;/b&gt; e posteriormente fazer aguardar por determinado recurso, o que indicaria que o servidor respondeu.&lt;br /&gt;&lt;br /&gt;No caso do nosso exemplo a função de callback do ajax, ao receber os dados do servidor imprime uma tag H1 do html com o texto "Dados do servidor". Dessa forma o teste ficaria na seguinte ordem:&lt;br /&gt;&lt;br /&gt;&lt;table border="1"&gt;&lt;tr&gt;&lt;td&gt;open&lt;/td&gt;&lt;td&gt;/teste.html&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;click&lt;/td&gt;&lt;td&gt;link=Clique aqui&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;waitForElementPresent&lt;/td&gt;&lt;td&gt;//h1&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;verifyText&lt;/td&gt;&lt;td&gt;//h1&lt;/td&gt;&lt;td&gt;Dados do servidor&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;Repare que após o clique no link, utilizamos a função &lt;b&gt;waitForElementPresent&lt;/b&gt; para aguardar que o html tenha inserido uma tag h1 em qualquer parte dele. Depois que este é impresso eu utilizo a função &lt;b&gt;verifyText&lt;/b&gt; para verificar se há alguma tag h1 inserida no html que possua o texto "Dados do servidor".&lt;br /&gt;&lt;br /&gt;A versão do &lt;a href="http://www.openqa.org/selenium-ide/"&gt;Selenium IDE&lt;/a&gt; utilizada é 0.8.7 e a versão do Firefox 2.0.0.1&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-6979475041880070205?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/6979475041880070205/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/08/selenium-ide-aplicado-interfaces-com.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/6979475041880070205'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/6979475041880070205'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/08/selenium-ide-aplicado-interfaces-com.html' title='Selenium IDE aplicado a interfaces com Ajax'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-4371074779297401923</id><published>2007-08-17T00:46:00.001-03:00</published><updated>2007-08-17T00:51:34.539-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><title type='text'>O atributo length de objetos e arrays em javascript</title><content type='html'>Eu não resisti e usei Javascript essa ultima semana inteira - É a minha confissão de hoje na reunião de javascriptólatras anônimos - Passei um perrengue danado por causa de suas peculiaridades. Tudo causado por falta de atenção a uma das pequenas regrinhas que essa linguagem viciante tem. Para evitar que outros passem pelo mesmo problema, compartilho aqui minha dor.&lt;br /&gt;&lt;br /&gt;Em primeiro lugar é preciso verificar que somente arrays têm o atributo length. Em segundo que arrays podem se comportar como objetos, e objetos podem se comportar como arrays, e que arrays são objetos. Entendeu? Bom, vamos aos exemplos pra ficar mais claro.&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt; var eu = { nome:"Tiago",idade:25 }; //Criando um objeto dinâmico com {}&lt;br /&gt; alert( eu["nome"] ) //Acessando como se fosse um mapa&lt;br /&gt; alert( eu.idade ) //Acessando como um atributo&lt;br /&gt; eu.sexo = "Sempre"; //Criando um novo atributo&lt;br /&gt; alert( eu["sexo"] ); &lt;br /&gt; eu["altura"] = 1.75; //Criando um novo atributo como mapa&lt;br /&gt; alert( eu.altura );  &lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;No exemplo acima eu crio um objeto dinamicamente utilizando chaves além de acessar e criar novos atributos, de duas formas possíveis: Como se fosse um mapa, utilizando o operado colchetes, e da maneira mais comum, que é utilizando o operador ponto. Vamos seguir então para o próximo exemplo:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt; var eu = []; //Criando um array com []&lt;br /&gt; eu.nome = "Tiago"; //Setando atributo em um array? Que coisa estranha&lt;br /&gt; eu["idade"] = 25 //Não era um array, por que está aceitando uma chave como se fosse um mapa?&lt;br /&gt; alert( eu["nome"] ) //Acessando como se fosse um mapa&lt;br /&gt; alert( eu.idade ) //Acessando como um atributo&lt;br /&gt; eu.sexo = "Sempre"; //Criando um novo atributo&lt;br /&gt; alert( eu["sexo"] ); &lt;br /&gt; eu["altura"] = 1.75; //Criando um novo atributo como mapa&lt;br /&gt; alert( eu.altura );  &lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;Neste exemplo criamos um array ao invés de um objeto, e qual não é nossa surpresa ao perceber que, exceto pela incialização, o comportamento dele fica exatamente igual ao de um objeto. Isso ocorre porque um array é um objeto. Até aí tudo parece maravilhoso. A coisa desanta quando você resolve usar o comportamento de array, num array tratado como objeto. Vejamos:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt; var eu = []; &lt;br /&gt; alert(eu.length); //Tamanho 0 oras, não coloquei nada aqui ainda&lt;br /&gt; eu["nome"] = "Tiago"; //Inserindo um atributo&lt;br /&gt; alert(eu.length); //Tamanho 0?? E o atributo que botei?&lt;br /&gt; eu.idade = 25; //Inserindo outro atributo&lt;br /&gt; alert(eu.length); //Tamanho 0 de novo????&lt;br /&gt; eu[0] = "primeiro indice"; //Adicionando elementos ao array&lt;br /&gt; eu[1] = "segundo indice"; &lt;br /&gt; alert(eu.length); //Ahhh agora sim&lt;br /&gt; eu.push("terceiro indice");&lt;br /&gt; eu.push("quarto indice");&lt;br /&gt; alert(eu.length); //Ahhh agora sim de novo&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;No exemplo acima podemos perceber o quão confuso começa a ficar. Basicamente o atributo length passa a só funcionar quando inserimos elementos no array. Ou seja, os atributos inseridos no array ficam fora do array. Isso fica mais evidente fazendo as iterações como as do exemplo abaixo:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt; //agora vamos iterar só pelos elementos do array&lt;br /&gt; for(i=0; i&lt;eu.length; i++)&lt;br /&gt; {&lt;br /&gt;     alert("elemento do array: " + eu[i]); &lt;br /&gt; }&lt;br /&gt; //agora vamos iterar sobre tudo que tiver aí&lt;br /&gt; for(i in eu)&lt;br /&gt; {&lt;br /&gt;     alert("atributo do objeto: " + eu[i]); &lt;br /&gt; }&lt;br /&gt; //De novo somente elementos do array&lt;br /&gt; while(eu.length&gt;0)&lt;br /&gt; {&lt;br /&gt;     alert("Remove elemento: " + eu.pop());&lt;br /&gt; }&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;Na primeira eu itero sobre números, logo somente sobre os elementos do array. No segundo eu itero sobre as chaves, sejam elas numéricas, sejam de texto. E finalmente no terceiro eu itero removendo os elementos do array. Mas ainda sim podem ocorrer algumas curiosidades que podem nos confundir, veja abaixo:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt; var eu = []; &lt;br /&gt; eu[0] = "primeiro indice"; &lt;br /&gt; eu[1] = "segundo indice"; &lt;br /&gt; eu["1"] = "substitui segundo indice"; //Isso aqui é o mesmo que eu[1]&lt;br /&gt; alert(eu[1]);  //Vai alertar a mesma coisa que o alerta de baixo&lt;br /&gt; alert(eu["1"]); &lt;br /&gt; eu["length"] = 0; //Vou sacanear o array agora&lt;br /&gt; alert(eu.length); //Zerei o tamanho&lt;br /&gt; alert(eu[4]); //Ih!!! Apagou tudo&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;Perceba que adicionar um elemento ao indice 1 como número é o mesmo que adicionar ao 1 como string. Além disso, veja que podemos alterar o valor do tamanho, atributo length, do array somente atribuindo um valor a ele. Isso pode causar grandes problemas, ainda mais se você estiver guardando dados digitados pelo usuário como se o array fosse um mapa. &lt;br /&gt;&lt;br /&gt;Um exemplo simples é um usuário preenchendo um formulário com campo de nome e valor, ao clicar em um botão esses dados são inseridos no array como se fosse um mapa. Digamos que ele preencha da seguinte maneira:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;Nome     | Valor: &lt;br /&gt;---------------------&lt;br /&gt;"país"   | "brasil"&lt;br /&gt;10       | "isso"&lt;br /&gt;7        | "hoje"&lt;br /&gt;"olha"   | "caso"&lt;br /&gt;"length" | 0&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;Neste caso quando ele digitar o nome 10, o array passará a ter 10 posições, mas no momento que ele digitar "length" com o valor 0, os dados que ele inseriu com nome 10 e 7 desaparecerão. Para debugar isso você terá que arrancar vários tufos de seu cabelo.&lt;br /&gt;&lt;br /&gt;E é aí finalmente que chegamos ao ponto crucial. Para evitar esse tipo de problema o ideal é deixar bem separado o que é array, inicializando com [] e o que é objeto inicializando com {}. Isso porque para objetos, um atributo com nome length não tem poder algum, exceto se você implementá-lo para que tenha.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-4371074779297401923?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/4371074779297401923/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/08/o-atributo-length-de-objetos-e-arrays.html#comment-form' title='2 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/4371074779297401923'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/4371074779297401923'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/08/o-atributo-length-de-objetos-e-arrays.html' title='O atributo length de objetos e arrays em javascript'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-2014349430682284912</id><published>2007-06-19T22:35:00.000-03:00</published><updated>2007-08-12T21:57:03.816-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jasperreport'/><title type='text'>Cuidado com SubReports aninhados</title><content type='html'>Com JasperReport é bom prestar atenção no uso de SubReports aninhados. O problema é com os parâmetros, pois o parâmetro de um relatório não persiste em seus SubReports. Parece pouca coisa, mas tive uma péssima experiência com isso. &lt;br /&gt;&lt;br /&gt;Um projeto em que estou trabalhando possui um relatório com um SubReport, e este SubReport possui inclui um outro SubReport. Para achar os arquivos jaspers dos SubReports utilizamos o default de concatenação do parâmetro ${SUB_DIRECTORY}. Acontece que ao definirmos na aplicação este parâmetro, o SubReport não recebe esta definição, de forma que ele acaba utilizando o default na inclusão de seu SubReport. Resultado: Funciona no ambiente de desenvolvimento, mas no ambiente de distribuição o bug acontece.&lt;br /&gt;&lt;br /&gt;Para resolver isso é preciso repassar o valor do parâmetro ${SUB_DIRECTORY} para os demais SubReports. Para fazer isso no IReport aperte com botão direito no local de inclusão do SubReport e aperte em properties. Na aba em que se configura o path do SubReport, logo abaixo deste campo existe uma caixa onde se pode definir os valores que devem ser repassados por parâmetro.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-2014349430682284912?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/2014349430682284912/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/06/cuidado-com-subreports-aninhados.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/2014349430682284912'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/2014349430682284912'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/06/cuidado-com-subreports-aninhados.html' title='Cuidado com SubReports aninhados'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-5709085320692930553</id><published>2007-06-13T18:22:00.001-03:00</published><updated>2007-06-13T18:24:02.620-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jasperreport'/><category scheme='http://www.blogger.com/atom/ns#' term='timotta-api'/><title type='text'>Classes de auxílio no uso de JasperReport e IReport</title><content type='html'>Popular um relatório jasper com uma coleção de beans é simples, o incoveniente é quando se deseja popular com uma coleção de beans que possuem instancias de outros beans como atributo. Para resolver essa sitação, criei dois DataSources do Jasper implementando algo parecido com a EL (Expression language) de forma a navegar seus atributos. São as classes ObjetoDataSource e ColecaoDataSource.&lt;br /&gt;&lt;br /&gt;Veja como essas classes funcionam:&lt;br /&gt;- &lt;a href="http://programandosemcafeina.blogspot.com/2007/06/classe-objetodatasource-para.html"&gt;Como funciona a classe ObjetoDataSource para JasperReport&lt;/a&gt;&lt;br /&gt;- &lt;a href="http://programandosemcafeina.blogspot.com/2007/06/classe-colecaodatasource-para.html"&gt;Como funciona a classe ColecaoDataSource para JasperReport&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Se você gostou, pode &lt;a href="http://programandosemcafeina.blogspot.com/2007/06/download-da-timotta-api.html"&gt;baixar o projeto timotta-api clicando aqui&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-5709085320692930553?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/5709085320692930553/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/06/classes-de-auxlio-no-uso-de.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/5709085320692930553'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/5709085320692930553'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/06/classes-de-auxlio-no-uso-de.html' title='Classes de auxílio no uso de JasperReport e IReport'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-857599790705534671</id><published>2007-06-13T18:21:00.001-03:00</published><updated>2007-06-13T19:10:09.767-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jasperreport'/><category scheme='http://www.blogger.com/atom/ns#' term='timotta-api'/><title type='text'>Classe ObjetoDataSource para JasperReport</title><content type='html'>Com a classe &lt;b&gt;ObjetoDataSource&lt;/b&gt; é possível acessar os atributos de qualquer objeto. Basta colocar os campos separados por ponto. Essa classe faz parte do &lt;a href="http://programandosemcafeina.blogspot.com/2007/06/classes-de-auxlio-no-uso-de.html"&gt;pacote de auxílio no uso de JasperReport e IReport&lt;/a&gt;. Mostro abaixo um exemplo simples. Primeiro vejamos as classes de dominio.&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;public class Pessoa&lt;br /&gt;{&lt;br /&gt; String nome;&lt;br /&gt; Date nascimento;&lt;br /&gt; Endereco endereco;&lt;br /&gt; List&amp;lt;String&amp;gt; telefones;&lt;br /&gt; // ... Gets e Sets omitidos ...&lt;br /&gt;}&lt;br /&gt;public class Endereco&lt;br /&gt;{&lt;br /&gt; String rua;&lt;br /&gt; short numero;&lt;br /&gt; // ... Gets e Sets omitidos ...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Agora digamos que a gente possui uma intância da classe Pessoa e desejamos imprimir todos seus campos em um relatório Jasper. Para fazer isso basta instanciar um novo ObjetoDataSource, como mostrado abaixo:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;Pessoa eu = loadPessoa();&lt;br /&gt;ObjetoDataSource dataSource = new ObjetoDataSource(eu);&lt;br /&gt;JasperReport compilado = JasperCompileManager.compileReport("/tmp/Relatorio.xml");&lt;br /&gt;JasperPrint preenchido = JasperFillManager.fillReport(compilado, new HashMap(), dataSource);&lt;br /&gt;JasperViewer.viewReport(preenchido);&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;No seu relatorio, para acessar os campos da Pessoa, basta declarar os campos da seguinte forma:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;&amp;lt;field name="nome" class="java.lang.String" /&amp;gt;&lt;br /&gt;&amp;lt;field name="nascimento" class="java.util.Date" /&amp;gt; &lt;br /&gt;&amp;lt;field name="telefones" class="java.lang.Object" /&amp;gt;&lt;br /&gt;&amp;lt;field name="endereco.rua" class="java.lang.String" /&amp;gt;&lt;br /&gt;&amp;lt;field name="endereco.numero" class="java.lang.Short" /&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Repare que o campo telefones é declarado como Object, porque a resposta dele não é um List, e sim um &lt;a href="http://programandosemcafeina.blogspot.com/2007/06/classe-colecaodatasource-para.html"&gt;ColecaoDataSource&lt;/a&gt;. Isso é feito para que se você possa iterar nele utilizando um SubReport. &lt;br /&gt;&lt;br /&gt;Neste exemplo, o relatório possuiria apenas uma pessoa, no caso de termos que exibir várias pessoas utiliza-se o &lt;a href="http://programandosemcafeina.blogspot.com/2007/06/classe-colecaodatasource-para.html"&gt;ColecaoDataSource&lt;/a&gt;. Ou poderiamos ter uma classe, por exemplo, Equipe que possui um atributo do tipo List&lt;Pessoa&gt;. Com essas classes as possibilidades são infinitas.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-857599790705534671?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/857599790705534671/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/06/classe-objetodatasource-para.html#comment-form' title='3 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/857599790705534671'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/857599790705534671'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/06/classe-objetodatasource-para.html' title='Classe ObjetoDataSource para JasperReport'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-3815582796293511482</id><published>2007-06-13T18:19:00.000-03:00</published><updated>2007-06-13T18:28:42.924-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jasperreport'/><category scheme='http://www.blogger.com/atom/ns#' term='timotta-api'/><title type='text'>Classe ColecaoDataSource para JasperReport</title><content type='html'>Com a classe &lt;b&gt;ColecaoDataSource&lt;/b&gt; é possível iterar e acessar os atributos dos objetos presentes em uma coleção. Funciona da mesma forma que a &lt;a href="http://programandosemcafeina.blogspot.com/2007/06/classe-objetodatasource-para.html"&gt;ObjetoDataSource&lt;/a&gt;, com a diferença de que por ser uma coleção, permite iterar entre uma coleção de objetos. Essa classe também faz parte do &lt;a href="http://programandosemcafeina.blogspot.com/2007/06/classes-de-auxlio-no-uso-de.html"&gt;pacote de auxílio no uso de JasperReport e IReport&lt;/a&gt;. Mostro abaixo um exemplo simples. Primeiro vejamos as classes de dominio:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;public class Pessoa&lt;br /&gt;{&lt;br /&gt; String nome;&lt;br /&gt; Date nascimento;&lt;br /&gt; Endereco endereco;&lt;br /&gt; List&amp;lt;String&amp;gt; telefones;&lt;br /&gt; // ... Gets e Sets omitidos ...&lt;br /&gt;}&lt;br /&gt;public class Endereco&lt;br /&gt;{&lt;br /&gt; String rua;&lt;br /&gt; short numero;&lt;br /&gt; // ... Gets e Sets omitidos ...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Agora vou preencher o relatorio com uma lista de pessoas:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;List&lt;Pessoa&gt; pessoas = loadPessoas();&lt;br /&gt;ColecaoDataSource dataSource = new ColecaoDataSource(eu);&lt;br /&gt;JasperReport compilado = JasperCompileManager.compileReport("/tmp/Relatorio.xml");&lt;br /&gt;JasperPrint preenchido = JasperFillManager.fillReport(compilado, new HashMap(), dataSource);&lt;br /&gt;JasperViewer.viewReport(preenchido);&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;No seu relatorio, para acessar os campos da Pessoa, basta declarar os campos da mesma forma como foi feita com a classe &lt;a href="http://programandosemcafeina.blogspot.com/2007/06/classe-objetodatasource-para.html"&gt;ObjetoDataSource&lt;/a&gt;. Viu como é fácil? &lt;br /&gt;&lt;br /&gt;Agora digamos que ao invés de List, queremos utilizar um Map. É a mesma coisa, só a declaração dos campos no relatório fica um pouco diferente. Isso porque passamos a iterar não por objetos Pessoa, mas por objetos Entry. Veja abaixo:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;Map&amp;lt;Integer,Pessoa&amp;gt; pessoas = loadPessoas();&lt;br /&gt;ColecaoDataSource dataSource = new ColecaoDataSource(eu);&lt;br /&gt;JasperReport compilado = JasperCompileManager.compileReport("/tmp/Relatorio.xml");&lt;br /&gt;JasperPrint preenchido = JasperFillManager.fillReport(compilado, new HashMap(), dataSource);&lt;br /&gt;JasperViewer.viewReport(preenchido);&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;No relatório então, os campos ficariam assim:&lt;br /&gt;No seu relatorio, para acessar os campos da Pessoa, basta declarar os campos da seguinte forma:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;field name="key" class="java.lang.Integer" /&amp;gt;&lt;br /&gt;&amp;lt;field name="value.nome" class="java.lang.String" /&amp;gt;&lt;br /&gt;&amp;lt;field name="value.nascimento" class="java.util.Date" /&amp;gt; &lt;br /&gt;&amp;lt;field name="value.telefones" class="java.lang.Object" /&amp;gt;&lt;br /&gt;&amp;lt;field name="value.endereco.rua" class="java.lang.String" /&amp;gt;&lt;br /&gt;&amp;lt;field name="value.endereco.numero" class="java.lang.Short" /&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Simples não é?&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-3815582796293511482?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/3815582796293511482/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/06/classe-colecaodatasource-para.html#comment-form' title='3 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3815582796293511482'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3815582796293511482'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/06/classe-colecaodatasource-para.html' title='Classe ColecaoDataSource para JasperReport'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-657980277673746918</id><published>2007-06-13T18:12:00.002-03:00</published><updated>2008-09-03T13:56:24.086-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='timotta-api'/><title type='text'>Download da timotta-api</title><content type='html'>Faça o download dos fontes, testes, exemplos e binários:&lt;br /&gt;&lt;br /&gt;Versão 0.0.2: &lt;a href="http://www.divshare.com/download/5301025-124"&gt;http://www.divshare.com/download/5301025-124&lt;/a&gt;&lt;br /&gt;Versão 0.0.1: &lt;a href="http://www.divshare.com/download/936101-637"&gt;http://www.divshare.com/download/936101-637&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Veja as funcionalidades disponíveis na timotta-api 0.0.2:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2008/09/keydown-e-keyup-para-applets-java.html"&gt;KeyDown e KeyUp para applets java&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/06/classes-de-auxlio-no-uso-de.html"&gt;Classes de auxílio no uso de JasperReport e IReport&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-657980277673746918?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/657980277673746918/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/06/download-da-timotta-api.html#comment-form' title='4 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/657980277673746918'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/657980277673746918'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/06/download-da-timotta-api.html' title='Download da timotta-api'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-8265700262671490957</id><published>2007-05-31T16:35:00.000-03:00</published><updated>2007-05-31T16:40:19.187-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gnuplot'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>Gerar imagem PNG com o GnuPlot</title><content type='html'>Ao instalar o GnuPlot no servidor, verifiquei que não conseguia gerar graficos no formato png com este programa. O erro que eu obtinha é o seguinte:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;gnuplot&gt; set terminal png&lt;br /&gt;                      ^&lt;br /&gt;         unknown or ambiguous terminal type; type just 'set terminal' for a list&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Observando o log da configuração, config.log, que é gerado assim que o comando ./configure é executado descobri qual o problema, faltava-me a libgd. Veja a linha do log mostrando o problema:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;configure:16121: result:   png terminal: no (requires libgd with png support)&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Sabendo disso, desinstalei o gnuplot, instalei a libgd e todas suas dependencias para finalmente reinstalar o gnuplot. Com isso, tudo funcionou perfeitamente.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-8265700262671490957?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/8265700262671490957/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/05/gerar-imagem-png-com-o-gnuplot.html#comment-form' title='1 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/8265700262671490957'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/8265700262671490957'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/05/gerar-imagem-png-com-o-gnuplot.html' title='Gerar imagem PNG com o GnuPlot'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-2201715772135207847</id><published>2007-05-30T17:19:00.000-03:00</published><updated>2007-05-30T17:22:15.916-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='thread'/><title type='text'>Load alto causado por um HashMap</title><content type='html'>Recentemente tivemos um problema em produção com nosso sistema. O load do servidor aumentava gradativamente chegando a um ponto em que era necessário reinicia-lo. Para quem não sabe, o load é um cálculo que indica quantas threads estão executando ou tentando executar em um determinado período. Ou seja, este problema de load alto poderia então ser causado por uma grande quantidade de acessos, no entanto, o load não baixava em periodos de pouco acesso, e até mesmo quando tirávamos o servidor do balanceamento.&lt;br /&gt;&lt;br /&gt;Para analisar as threads que estavam executando, lançamos mão de um Thread Dump em dois momentos, antes de tirarmos a máquina do balanceamento, e outra uma hora depois de tirá-la. No Java, para obter um Thread Dump basta executar o seguinte comando, onde o &lt;b&gt;processo_id&lt;/b&gt; é o pid do processo Java que queremos analisar.&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;kill -QUIT processo_id&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Com o resultado em mãos descobrimos que antes e depois de tirarmos o servidor do balanceamento havia uma thread executando (runnable) travada na linha 325 de um HashMap. Veja a descrição dessa thread abaixo:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;"ExecuteThread: '94' for queue: 'default'" daemon prio=5 tid=0x00b525a0 nid=0x2c runnable [0x63bfe000..0x63bffc28]&lt;br /&gt;at java.util.HashMap.get(HashMap.java:325)&lt;br /&gt;(...)&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Foi então que descobrimos o problemas. Procuramos no Google por esta linha e encontramos diversas pessoas com o mesmo problema e até mesmo notificações deste possível bug para Sun. Contudo, isto não é um bug. O que acontece é que a classe HashMap não é thread safe, nem mesmo para seus métodos put e get. Portanto, se diversas threads gravam e recuperam valores de um HashMap, pode ocorrer problemas como esse. De fato, se olharmos o código de HashMap, podemos perceber que o método get tem grandes possibilidades de entrar em loop infinito.&lt;br /&gt;&lt;br /&gt;A solução recomendade pela Sun é utilizar o wrapper sincronizado para mapas &lt;b&gt;Collections.synchronizedMap(new HashMap())&lt;/b&gt;, ou utilizar a classe &lt;b&gt;ConcurrentHashMap&lt;/b&gt; da api de concorrência incorporada ao Java 5, a java.util.concurrent. Para quem não pode utilizar o Java 5, pode-se utilizar a versão &lt;a href="http://dcl.mathcs.emory.edu/util/backport-util-concurrent/" target="_blank"&gt;back-port da java.util.concurrent&lt;/a&gt;. No nosso caso, utilizamos essa versão backport.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-2201715772135207847?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/2201715772135207847/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/05/load-alto-causado-por-um-hashmap.html#comment-form' title='1 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/2201715772135207847'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/2201715772135207847'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/05/load-alto-causado-por-um-hashmap.html' title='Load alto causado por um HashMap'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-8934957290730572312</id><published>2007-05-24T17:51:00.000-03:00</published><updated>2007-05-24T18:45:14.807-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jms'/><title type='text'>Configurando um tópico JMS no JBoss</title><content type='html'>Para configurar um tópico de Java Message Service no JBoss (a versão que estou utilizando é 4.0.5.GA) basta editar o arquivo %JBOSS_HOME%/server/default/deploy/jms/jbossmq-destinations-service.xml iserindo uma tag XML como mostrada abaixo:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;&amp;lt;mbean code="org.jboss.mq.server.jmx.Topic" &lt;br /&gt;        name="jboss.mq.destination:service=Topic,name=&lt;b&gt;nomeDoMeuTopico&lt;/b&gt;"&amp;gt;&lt;br /&gt;    &amp;lt;depends optional-attribute-name="DestinationManager"&amp;gt;jboss.mq:service=DestinationManager&amp;lt;/depends&amp;gt;&lt;br /&gt;    &amp;lt;depends optional-attribute-name="SecurityManager"&amp;gt;jboss.mq:service=SecurityManager&amp;lt;/depends&amp;gt;&lt;br /&gt;    &amp;lt;attribute name="SecurityConf"&amp;gt;&lt;br /&gt;        &amp;lt;security&amp;gt;&lt;br /&gt;            &amp;lt;role name="guest" read="true" write="true"/&amp;gt;&lt;br /&gt;            &amp;lt;role name="publisher" read="true" write="true" create="false"/&amp;gt;&lt;br /&gt;            &amp;lt;role name="durpublisher" read="true" write="true" create="true"/&amp;gt;&lt;br /&gt;        &amp;lt;/security&amp;gt;&lt;br /&gt;    &amp;lt;/attribute&amp;gt;&lt;br /&gt;&amp;lt;/mbean&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Não sei bem para que serve cada um dos parâmetros pois estou começando a estudar JMS agora.&lt;br /&gt;&lt;br /&gt;Depois de configurar um tópico eu crio um servlet de exemplo que lê um parâmetro e o escreve no tópico como uma mensagem de texto. Observer que o lookup que fazemos no InitialContext para pegar o tópico utiliza o mesmo nome do tópico que configuramos adicionado com o prefixo "topic/":&lt;br /&gt;&lt;div class="codigo maximo"&gt;&lt;pre&gt;@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException&lt;br /&gt;{&lt;br /&gt;    Context jndiContext = null;&lt;br /&gt;    TopicConnectionFactory topicConnectionFactory = null;&lt;br /&gt;    TopicConnection topicConnection = null;&lt;br /&gt;    TopicSession topicSession = null;&lt;br /&gt;    Topic topic = null;&lt;br /&gt;    TopicPublisher topicPublisher = null;&lt;br /&gt;&lt;br /&gt;    try&lt;br /&gt;    {&lt;br /&gt;        jndiContext = new InitialContext();&lt;br /&gt;&lt;br /&gt;        topicConnectionFactory = (TopicConnectionFactory) jndiContext.lookup("TopicConnectionFactory");&lt;br /&gt;        topic = (Topic) jndiContext.lookup("topic/nomeDoMeuTopico");&lt;br /&gt;&lt;br /&gt;        topicConnection = topicConnectionFactory.createTopicConnection();&lt;br /&gt;        topicSession = topicConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);&lt;br /&gt;        topicPublisher = topicSession.createPublisher(topic);&lt;br /&gt;&lt;br /&gt;        TextMessage mensagem = topicSession.createTextMessage();&lt;br /&gt;        mensagem.setText( request.getParameter("m") );&lt;br /&gt;        topicPublisher.publish(mensagem);&lt;br /&gt;    }&lt;br /&gt;    catch (NamingException e)&lt;br /&gt;    {&lt;br /&gt;        log.error("",e);&lt;br /&gt;    }&lt;br /&gt;    catch (JMSException e)&lt;br /&gt;    {&lt;br /&gt;        log.error("",e);&lt;br /&gt;    }&lt;br /&gt;    finally&lt;br /&gt;    {&lt;br /&gt;        if (topicPublisher != null) try { topicPublisher.close(); } catch (JMSException e) {}&lt;br /&gt;        if (topicConnection != null) try { topicConnection.close(); } catch (JMSException e) {}&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    response.getWriter().print("Mensagem publicada");&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Para ler as mensagens enviadas ao tópico é preciso definir um listener, implementando a interface MessageListener, como é mostrado no código abaixo:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;public class MensagemDeTextoListener implements MessageListener&lt;br /&gt;{&lt;br /&gt;    public void onMessage(Message mensagem)&lt;br /&gt;    {&lt;br /&gt;        TextMessage msg = null;&lt;br /&gt;&lt;br /&gt;        try&lt;br /&gt;        {&lt;br /&gt;            if (mensagem instanceof TextMessage)&lt;br /&gt;            {&lt;br /&gt;                msg = (TextMessage) mensagem;&lt;br /&gt;                System.out.println("Leu mensagem: " +  msg.getText());&lt;br /&gt;            }&lt;br /&gt;            else&lt;br /&gt;            {&lt;br /&gt;                System.out.println("Mensagem de tipo invalido: " + mensagem.getClass().getName());&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        catch (JMSException e)&lt;br /&gt;        {&lt;br /&gt;            log.error("",e);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Finalmente é preciso inscrever uma instancia deste listener como leitor do tópico. No exemplo abaixo é mostrado como inscrever um listener no tópico anteriormente definido, através do método contextInitializer de um ServletContextListener. &lt;br /&gt;&lt;div class="codigo maximo"&gt;&lt;pre&gt;private TopicConnection topicConnection;&lt;br /&gt;&lt;br /&gt;private void contextInitialized(ServletContextEvent evento)&lt;br /&gt;{&lt;br /&gt;    Context jndiContext = null;&lt;br /&gt;    TopicConnectionFactory topicConnectionFactory = null;&lt;br /&gt;    TopicSession topicSession = null;&lt;br /&gt;    Topic topic = null;&lt;br /&gt;    TopicSubscriber topicSubscriber = null;&lt;br /&gt;    MensagemDeTextoListener topicListener = null;&lt;br /&gt;    TextMessage message = null;&lt;br /&gt;&lt;br /&gt;    try&lt;br /&gt;    {&lt;br /&gt;        jndiContext = new InitialContext();&lt;br /&gt;        topicConnectionFactory = (TopicConnectionFactory)&lt;br /&gt;        jndiContext.lookup("TopicConnectionFactory");&lt;br /&gt;        topic = (Topic) jndiContext.lookup("topic/mensagemTopic");&lt;br /&gt;&lt;br /&gt;        topicConnection = topicConnectionFactory.createTopicConnection();&lt;br /&gt;        topicSession = topicConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);&lt;br /&gt;        topicSubscriber = topicSession.createSubscriber(topic);&lt;br /&gt;        topicListener = new MensagemDeTextoListener();&lt;br /&gt;        topicSubscriber.setMessageListener(topicListener);&lt;br /&gt;        topicConnection.start();&lt;br /&gt;    }&lt;br /&gt;    catch (NamingException e)&lt;br /&gt;    {&lt;br /&gt;        log.error("",e);&lt;br /&gt;    }&lt;br /&gt;    catch (JMSException e)&lt;br /&gt;    {&lt;br /&gt;        log.error("",e);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;private void finalizarSubscriber()&lt;br /&gt;{&lt;br /&gt;    try&lt;br /&gt;    {&lt;br /&gt;        topicConnection.close();&lt;br /&gt;    }&lt;br /&gt;    catch (JMSException e)&lt;br /&gt;    {&lt;br /&gt;        log.error("",e);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Pronto, aparentemente o exemplo funcionaria, mas não, quando inicializar seu JBoss, ocorrerá o seguinte erro: &lt;b&gt;javax.jms.IllegalStateException in JBOSS: This method is not applicable inside the application server. See the J2EE spec, e.g. J2EE1.4 Section 6.6&lt;/b&gt;. Esse erro se dará exatamente na linha em que você adiciona o listener das mensagens ao tópico.&lt;br /&gt;&lt;br /&gt;Ainda não entendi bem o porque isso acontece, mas achei a solução: Basta alterar o arquivo %JBOSS_HOME%/server/default/deploy/jms/jms-ds.xml adicionando o parâmetro Strict à connection-factory de jndi-name JmsXA. Veja abaixo:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;&amp;lt;tx-connection-factory&amp;gt;&lt;br /&gt;    &amp;lt;jndi-name&amp;gt;JmsXA&amp;lt;/jndi-name&amp;gt;&lt;br /&gt;    &amp;lt;xa-transaction/&amp;gt;&lt;br /&gt;    &amp;lt;rar-name&amp;gt;jms-ra.rar&amp;lt;/rar-name&amp;gt;&lt;br /&gt;    &amp;lt;connection-definition&amp;gt;org.jboss.resource.adapter.jms.JmsConnectionFactory&amp;lt;/connection-definition&amp;gt;&lt;br /&gt;    &amp;lt;config-property name="SessionDefaultType" type="java.lang.String"&amp;gt;javax.jms.Topic&amp;lt;/config-property&amp;gt;&lt;br /&gt;    &amp;lt;config-property name="JmsProviderAdapterJNDI" type="java.lang.String"&amp;gt;java:/DefaultJMSProvider&amp;lt;/config-property&amp;gt;&lt;br /&gt;    &lt;b&gt;&amp;lt;config-property name="Strict" type="java.lang.Boolean"&amp;gt;false&amp;lt;/config-property&amp;gt;&lt;/b&gt;&lt;br /&gt;    &amp;lt;max-pool-size&amp;gt;20&amp;lt;/max-pool-size&amp;gt;&lt;br /&gt;    &amp;lt;security-domain-and-application&amp;gt;JmsXARealm&amp;lt;/security-domain-and-application&amp;gt;&lt;br /&gt;&amp;lt;/tx-connection-factory&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-8934957290730572312?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/8934957290730572312/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/05/configurando-um-tpico-jms-no-jboss.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/8934957290730572312'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/8934957290730572312'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/05/configurando-um-tpico-jms-no-jboss.html' title='Configurando um tópico JMS no JBoss'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-2301065018699951848</id><published>2007-05-23T11:33:00.000-03:00</published><updated>2007-05-23T12:06:03.051-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='irc'/><title type='text'>Cliente de IRC em Java</title><content type='html'>Na verdade neste artigo não faremos um cliente de IRC, faremos um simple bot. Mas, a biblioteca que apresento também serve para fazer clientes de IRC, é a &lt;a href="http://www.jibble.org/pircbot.php"&gt;PircBot (Java IRC Bot Framework)&lt;/a&gt;. Para funcionar você pode utilizar um servidor de IRC já existente, ou instalar um na sua máquina. No caso eu preferi intalar um, e utilizei o &lt;a href="http://unrealircd.com/?page=main"&gt;UnrealIRCd&lt;/a&gt;, que possui versões para Windows e Linux. &lt;br /&gt;&lt;br /&gt;O &lt;a href="http://unrealircd.com/?page=main"&gt;UnrealIRCd é um bom servidor de IRC&lt;/a&gt; pois é fácil, na medida do possível, de configurar. Isso porque as mensagens de erro dele são bem explicativa, dizendo até mesmo em qual linha do arquivo de configuração há uma entrada incorreta ou faltando.&lt;br /&gt;&lt;br /&gt;Depois de escolher servidor criamos uma classe extendendo &lt;span style="font-weight:bold;"&gt;org.jibble.pircbot.PircBot&lt;/span&gt;, essa classe representará o usuário que criamos para se conectar no IRC. A idéia é fazer um usuário que desconecte do IRC quando receber uma mensagem "Sai vagabundo" de qualquer pessoa em qualquer sala.&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;public class BotVagabundo extends PircBot&lt;br /&gt;{&lt;br /&gt;    public BotVagabundo(String nome)&lt;br /&gt;    {&lt;br /&gt;        this.setName(nome);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override protected void onMessage(String channel, String sender, String login, String hostname, String message)&lt;br /&gt;    {&lt;br /&gt;        System.out.println("Recebi em [" + channel + "] do usuario [" + sender + "] a mensagem [" + message + "]");&lt;br /&gt;&lt;br /&gt;        if("Sai vagabundo".equals(message))&lt;br /&gt;        {&lt;br /&gt;            this.disconnect();&lt;br /&gt;            this.dispose();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;O método PircBot.setName(String) define o nick do usuário. O método onMessage, é acionado quando alguma mensagem é recebida, seja num dos canais em que ele se encontra ou em um chat privado. &lt;br /&gt;&lt;br /&gt;Agora vamos ver como se faz para conectar esse usuário no IRC:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;BotVagabundo bot = new BotVagabundo("Chapun");&lt;br /&gt;&lt;br /&gt;bot.setVerbose(true);&lt;br /&gt;bot.connect("irc.chapun.com");&lt;br /&gt;bot.joinChannel("#brasil");&lt;br /&gt;&lt;br /&gt;bot.sendMessage("#brasil", "Eu sou um bot");&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;No caso eu não só fiz o bot conectar no servidor irc.chapun.com (coloquei o meu servidor local com este host) como fiz ele entrar no canal #brasil e enviar uma mensagem informando que é um bot.&lt;br /&gt;&lt;br /&gt;Viu como é fácil?&lt;br /&gt;&lt;br /&gt;Além disso, o PircBot tem diversas outras opções, como pegar a lista de canais, lista de usuários em um canal, kikar um usuário... Vale o tempo perdido dar uma olhada no site dele.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-2301065018699951848?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/2301065018699951848/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/05/cliente-de-irc-em-java.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/2301065018699951848'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/2301065018699951848'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/05/cliente-de-irc-em-java.html' title='Cliente de IRC em Java'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-7831171986056941204</id><published>2007-05-16T20:20:00.001-03:00</published><updated>2008-04-01T17:23:45.356-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='junit'/><category scheme='http://www.blogger.com/atom/ns#' term='testes'/><title type='text'>Teste unitário em funções javascript utilizando o JUnit</title><content type='html'>Um dos grandes problemas do javascript é a falta de testabilidade. Vou mostrar aqui nesse artigo uma forma que encontrei de testar as funções de arquivos javascripts utilizando o JUnit. Ah! Claro, isso só é possível se você estiver utilizando o Java 6, que possui suporte a linguagens de script e já vem nativamente com suporte a javascript.&lt;br /&gt;&lt;br /&gt;Primeiro criamos o arquivo funcoes.js contendo uma função para validar um determinado formulário. O objetivo é retornar verdadeiro se todos os campos estiverem preenchidos e falso no caso contrário. O formulário html deve ter dois campos: nome e senha. Inicialmente deixaremos essa função sem nenhum código, pois criaremos os testes primeiro:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;function validaFormulario(f)&lt;br /&gt;{&lt;br /&gt;    return false;&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Em seguida criamos uma classe para realizar os testes unitários contendo o método testValidaFormulario e outras duas classes internas para servir de Mock do formulário html e dos campos do formulário, HtmlFormMock e HtmlFieldMock respectivamente. Segue o código:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;package junit.script;&lt;br /&gt;import junit.framework.TestCase;&lt;br /&gt;&lt;br /&gt;public class FuncoesTest extends TestCase&lt;br /&gt;{&lt;br /&gt;    public void testValidaFormulario() throws FileNotFoundException, ScriptException, NoSuchMethodException&lt;br /&gt;    {&lt;br /&gt;        //Aqui entrarão os testes&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    public class HtmlFormMock implements Serializable&lt;br /&gt;    {&lt;br /&gt;        public HtmlFieldMock nome = new HtmlFieldMock();&lt;br /&gt;        public HtmlFieldMock senha = new HtmlFieldMock();&lt;br /&gt;    }&lt;br /&gt;    public class HtmlFieldMock implements Serializable&lt;br /&gt;    {&lt;br /&gt;        public String name;&lt;br /&gt;        public String value;&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;No método testValidaFormulario da classe de testes começamos então importanto o script funcoes.js, e criando um Invocable para poder executar funções no javascript:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;ScriptEngineManager manager = new ScriptEngineManager();&lt;br /&gt;ScriptEngine engine = manager.getEngineByName("js");&lt;br /&gt;engine.eval( new FileReader( "C:/Projetos/testes/web/funcoes.js" ) );&lt;br /&gt;Invocable invocable = (Invocable) engine;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Em seguida preenche-se o Mock de formulário da seguinte forma:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;HtmlFormMock f1 = new HtmlFormMock();&lt;br /&gt;f1.nome.name = "nome";&lt;br /&gt;f1.nome.value = "Tiago";&lt;br /&gt;f1.senha.name = "senha";&lt;br /&gt;f1.senha.value = "123456";&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Tendo o invocador de funções de script (Invocable) e o objeto mock, basta alterar os atributos deste objetos para utilizá-lo como parâmetro do formulário por diversas vezes. Veja abaixo os testes que defini para esta função, repare que fiz dois testes para verificar se o campo "nome" e o campo "senha" existem no formulário, pois é possível que criem um formulário html sem o campos:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo maximo"&gt;&lt;pre&gt;//Todo formulario preenchido&lt;br /&gt;Boolean b = (Boolean) invocable.invokeFunction("validaFormulario", new Object[] { f1 } );&lt;br /&gt;assertTrue("Formulario todo preenchido deve retornar true", b.booleanValue());&lt;br /&gt;&lt;br /&gt;//Senha preenchida com nulo&lt;br /&gt;f1.senha.value = null;&lt;br /&gt;b = (Boolean) invocable.invokeFunction("validaFormulario", new Object[] { f1 } );&lt;br /&gt;assertFalse("Senha preenchida com nulo deve retornar falso", b.booleanValue());&lt;br /&gt;&lt;br /&gt;//Senha preenchida com vazio&lt;br /&gt;f1.senha.value = "";&lt;br /&gt;b = (Boolean) invocable.invokeFunction("validaFormulario", new Object[] { f1 } );&lt;br /&gt;assertFalse("Senha preenchida com vazio deve retornar falso", b.booleanValue());&lt;br /&gt;&lt;br /&gt;//Nome preenchido com nulo&lt;br /&gt;f1.senha.value = "outra";&lt;br /&gt;f1.nome.value = null;&lt;br /&gt;b = (Boolean) invocable.invokeFunction("validaFormulario", new Object[] { f1 } );&lt;br /&gt;assertFalse("Nome preenchido com nulo deve retornar falso", b.booleanValue());&lt;br /&gt;&lt;br /&gt;//Nome preenchido com vazio&lt;br /&gt;f1.nome.value = "";&lt;br /&gt;b = (Boolean) invocable.invokeFunction("validaFormulario", new Object[] { f1 } );&lt;br /&gt;assertFalse("Nome preenchido com vazio deve retornar falso", b.booleanValue());&lt;br /&gt;&lt;br /&gt;//Campo nome do formulario nao existir deve retornar falso&lt;br /&gt;f1.nome = null;&lt;br /&gt;b = (Boolean) invocable.invokeFunction("validaFormulario", new Object[] { f1 } );&lt;br /&gt;assertFalse("Campo nome do formulario nao existe entao deve retornar falso", b.booleanValue());&lt;br /&gt;&lt;br /&gt;//Campo senha nao pode ser nulo&lt;br /&gt;f1.nome = new HtmlFieldMock();&lt;br /&gt;f1.nome.name = "nome";&lt;br /&gt;f1.nome.value = "Tiago";&lt;br /&gt;f1.senha = null;&lt;br /&gt;b = (Boolean) invocable.invokeFunction("validaFormulario", new Object[] { f1 } );&lt;br /&gt;assertFalse("Campo senha do formulario nao existe entao deve retornar falso", b.booleanValue());&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Executando esse teste com o JUnit teremos uma falha logo no primeiro teste, pois o este espera que haja um retorno de true e nossa função javascript está sempre retornando false. Alteremos então a função javascript dando-lhe um código aceitável:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;function validaFormulario(f)&lt;br /&gt;{&lt;br /&gt; if( f.nome &amp;&amp; f.nome.value &amp;&amp; f.nome.value.length() &gt; 0 &amp;&amp;&lt;br /&gt;  f.senha &amp;&amp; f.senha.value &amp;&amp; f.senha.value.length() &gt; 0 )&lt;br /&gt; {&lt;br /&gt;  return true;&lt;br /&gt; }&lt;br /&gt; return false;&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Pronto, tendo a função javascript codificada, basta rodarmos o teste no JUnit novamente para verificar que finalmente a função está funcionando de acordo com o específicado. E o melhor, sempre que ocorrer uma alteração no código dela, saberemos se houve alguma quebra de contrato.&lt;br /&gt;&lt;br /&gt;Também é possível testar métodos de classes javascript. Basta utilizar o método invokeMethod da interface Invocable, ao invés de invokeFunction. Mas isso eu deixo pra outro artigo.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-7831171986056941204?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/7831171986056941204/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/05/teste-unitrio-em-funes-javascript.html#comment-form' title='5 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/7831171986056941204'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/7831171986056941204'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/05/teste-unitrio-em-funes-javascript.html' title='Teste unitário em funções javascript utilizando o JUnit'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-6134478463206841540</id><published>2007-05-10T12:12:00.001-03:00</published><updated>2007-05-10T15:48:59.399-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jmeter'/><title type='text'>Como utilizar o ForEach Controller do JMeter</title><content type='html'>A estrutura de repetição, &lt;b&gt;ForEach Controller&lt;/b&gt; do JMeter exige que existam váriaveis definidas com um padrão contendo um prefixo seguido de um número sequencial (Exemplo: x1, x2, x3, ou , n_1, n_2, n_3). Para definir um grande número de váriaveis sem grande esforço, pode-se utilizar um &lt;b&gt;CSV Data Set Config&lt;/b&gt; que define váriaveis apartir de um arquivo texto.&lt;br /&gt;&lt;br /&gt;No campo "Filename" do &lt;b&gt;CSV Data Set Config&lt;/b&gt; indica-se o path de um arquivo de texto contendo os valores separados por vírgula (Exemplo: 10,56,345,12). A seguir no campo "Variable names" define-se um nome de váriavel para cada valor definido no arquivo texto. Assim, se no arquivo texto tiver cinco valores, devem ser definidos cinco nomes de váriaveis (Exemplo: n_1,n_2,n_3,n_4,n_5).&lt;br /&gt;&lt;br /&gt;No &lt;b&gt;ForEach Controller&lt;/b&gt; deve-se indicar o prefixo da váriavel em "Input variable prefix". Se o padrão de váriaveis suas possuir um _ antes do número, pode-se marcar a opção "Add _ before number". O campo "Output variable name" recebe o valor da variável que receberá o valor de uma das variáveis a cada iteração. Ou seja, a primeira vez que executar o valor será de n_1, na segunda vez será de n_2 e assim por diante.&lt;br /&gt;&lt;br /&gt;Assim, os itens que estiverem dentro do &lt;b&gt;ForEach Controller&lt;/b&gt; passam a poder utilizar a váriavel definida em "Output variable name" utilizando um padrão parecido com de Expression Language do JSP 2.0: ${nomeVariavel}.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-6134478463206841540?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/6134478463206841540/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/05/como-utilizar-o-foreach-controller-do.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/6134478463206841540'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/6134478463206841540'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/05/como-utilizar-o-foreach-controller-do.html' title='Como utilizar o ForEach Controller do JMeter'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-8979433209313353172</id><published>2007-05-09T17:50:00.000-03:00</published><updated>2007-05-09T18:03:19.208-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tomcat'/><title type='text'>Número de threads no Tomcat</title><content type='html'>Para aumentar o número de threads que o seu Tomcat utiliza para atender requisições é só alterar o server.xml, localizado em &lt;span style="font-weight:bold;"&gt;TOMCAT_HOME/conf/server.xml&lt;/span&gt;. É necessário informar alguns novos atributos (ou alterar) na tag Connector que é utilizada para receber as requisições, ou seja aquela que possui o atributo "port" com valor igual a porta http. Veja abaixo:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;&amp;lt;Connector &lt;br /&gt;    className="org.apache.coyote.tomcat4.CoyoteConnector" port="80" &lt;br /&gt; ...&lt;br /&gt;    maxThreads="150"&lt;br /&gt;    minSpareThreads="25"&lt;br /&gt;    maxSpareThreads="75"&amp;gt;&lt;br /&gt; ...      &lt;br /&gt;&amp;lt;/Connector&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;O atributo "maxThreads" indica o máximo de threads que o tomcat irá abrir, o "minSpareThread" e "maxSpareThreads" são respectivamente o mínimo e o máximo de Threads que o Tomcat disponibilizará para servirem de steps.&lt;br /&gt;&lt;br /&gt;Essa configuração funcionou para a versão 4.1.31 do Tomcat, mas creio não deve ter mudado para novas versões.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-8979433209313353172?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/8979433209313353172/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/05/nmero-de-threads-no-tomcat.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/8979433209313353172'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/8979433209313353172'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/05/nmero-de-threads-no-tomcat.html' title='Número de threads no Tomcat'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-1278307723465728949</id><published>2007-05-07T18:05:00.000-03:00</published><updated>2007-05-07T18:21:23.636-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='firefox'/><category scheme='http://www.blogger.com/atom/ns#' term='busca'/><title type='text'>Busca com OpenSearch no Firefox e IE7</title><content type='html'>Recentemente fiz texto explicando &lt;a href="http://programandosemcafeina.blogspot.com/2007/04/busca-firefox.html"&gt;como disponibilizar a busca de seu site no Firefox&lt;/a&gt;. Semana retrasada aqui no trabalho utilizamos uma nova forma de disponibilizar a nossa busca nos navegadores dos usuário. Dessa vez, funciona tanto para Internet Explorer 7, quanto para o Firefox.&lt;br /&gt;&lt;br /&gt;Isso foi feito utilizando o padrão &lt;a href="http://www.opensearch.org"&gt;OpenSearch&lt;/a&gt;. E é bem similar ao padrão do artigo anterior, veja abaixo o código do XML que é preciso disponibilizar:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;br /&gt;&amp;lt;OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"&amp;gt;&lt;br /&gt;    &amp;lt;ShortName&amp;gt;Programando sem cafeina&amp;lt;/ShortName&amp;gt;&lt;br /&gt;    &amp;lt;Description&amp;gt;Blog sobre tecnologia principalmente para a area de desenvolvimento de sistemas&amp;lt;/Description&amp;gt;&lt;br /&gt;    &amp;lt;Url type="text/html" template="http://programandosemcafeina.blogspot.com/search/label/{searchTerms}"/&amp;gt;&lt;br /&gt;    &amp;lt;LongName&amp;gt;Programando sem cafeina - Blog sobre tecnologia principalmente para a area de desenvolvimento de sistemas&amp;lt;/LongName&amp;gt;&lt;br /&gt;    &amp;lt;Image height="64" width="64" type="image/png"&amp;gt;http://programandosemcafeina.blogspot.com/imagems/busca-openserach.png&amp;lt;/Image&amp;gt;&lt;br /&gt;    &amp;lt;Image height="16" width="16" type="image/png"&amp;gt;http://programandosemcafeina.blogspot.com/imagems/busca-openserach.png&amp;lt;/Image&amp;gt;&lt;br /&gt;    &amp;lt;Language&amp;gt;pt-br&amp;lt;/Language&amp;gt;&lt;br /&gt;    &amp;lt;OutputEncoding&amp;gt;UTF-8&amp;lt;/OutputEncoding&amp;gt;&lt;br /&gt;    &amp;lt;InputEncoding&amp;gt;UTF-8&amp;lt;/InputEncoding&amp;gt;&lt;br /&gt;&amp;lt;/OpenSearchDescription&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Esse XML tem apenas as opções básicas, para saber o restante das opções visite a página: &lt;a href="http://www.opensearch.org/Specifications/OpenSearch/1.1#OpenSearch_description_document"&gt;Descrição do documento OpenSearch&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Continuando então, para disponibilizar a busca não basta apenas colocar o XML lá no seu servidor, é preciso oferecer um link com um código javascript que adicione a busca na barra de ferramentas do navegador do usuário. Veja o exemplo abaixo de como fazer isso:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;&amp;lt;a href="javascript:void(window.external.AddSearchProvider('http://programandosemcafeina.blogspot.com/minhabusca.xml'));"&amp;gt;&lt;br /&gt;Adicionar busca do meu blog&lt;br /&gt;&amp;lt;/a&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Viu como é fácil? Quem quizer testar para ver como ficou a busca para o Globo Vídeos basta adicioná-la clicando em &lt;a href="javascript:void(window.external.AddSearchProvider('http://video.globo.com/Portal/gmc4/cda/busca/globovideo-opensearch.xml'));"&gt;Adicionar busca do Globo Vídeos em seu navegador&lt;/a&gt;. É bom lembrar que em navegadores muito antigos isso não funciona. No Internet Explorer 6 mesmo não funciona.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-1278307723465728949?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/1278307723465728949/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/05/busca-com-opensearch-no-firefox-e-ie7.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/1278307723465728949'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/1278307723465728949'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/05/busca-com-opensearch-no-firefox-e-ie7.html' title='Busca com OpenSearch no Firefox e IE7'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-7107838085586511208</id><published>2007-05-03T05:10:00.001-03:00</published><updated>2008-04-21T02:29:00.637-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='ajax'/><title type='text'>Problemas com Post do XMLHttpRequest</title><content type='html'>Recentemente tive problemas ao tentar utilizar chamadas assíncronas no javascript utilizando o método post do protocolo http. Na verdade, o que ocorria é que os parâmetros enviados por post não eram recebidos em meu servlet. Descobri então como contornar isso, mas não entendi o porque desse problema ter ocorrido. &lt;br /&gt;&lt;br /&gt;No javascript eu fazia a seguinte chamada, onde a variavel r era um objeto XMLHttpRequest:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;r.open("POST", "http://localhost/MeuServlet", true);&lt;br /&gt;r.send("nome=tiago");&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;No servlet &lt;span style="font-weight: bold;"&gt;MeuServlet&lt;/span&gt; eu apenas pegava o parâmetro nome e o imprimia na saída padrão:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;String nome = request.getParameter("nome");&lt;br /&gt;System.out.println("nome enviado = " + nome);&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;A saída era sempre "nome enviado = null". Olhando no debugger de chamadas assíncronas do firebug dava pra ver que os parâmetros estavam sendo enviados com sucesso. O problemas estava mesmo na recepção. Eis então que achei um exemplo na internet com o que faltava para meu código javascript:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;r.open("POST", "http://localhost/MeuServlet", true);&lt;br /&gt;r.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;");&lt;br /&gt;r.send("nome=tiago");&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Colocando essa linha indicando o Content-Type no header da requisição a chamada por post passou a funcionar. Se alguém tiver uma explicação do porque disso será de grande ajuda.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-7107838085586511208?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/7107838085586511208/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/05/problemas-com-post-do-xmlhttprequest.html#comment-form' title='1 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/7107838085586511208'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/7107838085586511208'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/05/problemas-com-post-do-xmlhttprequest.html' title='Problemas com Post do XMLHttpRequest'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-2117687610668950131</id><published>2007-05-02T13:48:00.000-03:00</published><updated>2007-05-02T13:54:28.781-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='css'/><title type='text'>Rolar um DIV via javascript</title><content type='html'>Segue um exemplo de como rolar um div via javascript:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;function praBaixo()&lt;br /&gt;{&lt;br /&gt;   var c = document.getElementById("meuId");&lt;br /&gt;   c.scrollTop = c.scrollHeight;&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;No exemplo, a função "praBaixo" colocaria a barra de rolagem de um div para baixo de forma a mostrar o ultimo conteudo do div. É bom lembrar que para um div ter barra de rolagem é preciso indicar seu tamanho, width para rolagem horizontal e height para rolagem vertical, e a propriedade "overflow: auto" no css.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-2117687610668950131?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/2117687610668950131/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/05/rolar-um-div-via-javascript.html#comment-form' title='4 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/2117687610668950131'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/2117687610668950131'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/05/rolar-um-div-via-javascript.html' title='Rolar um DIV via javascript'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-718867989421520940</id><published>2007-04-25T16:32:00.000-03:00</published><updated>2007-04-25T17:07:40.488-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='scwcd'/><category scheme='http://www.blogger.com/atom/ns#' term='taglib'/><title type='text'>Como criar funções em sua taglib</title><content type='html'>Caso você tenha necessidade de uma função que não existe na JSTL, você pode criar facilmente sua própria função. Nesse mini tutorial explicarei como fazer isto. Faremos uma função que retira os simbolos de maior e de menor (&lt;&gt;) dos textos para impedir que tags htmls sejam inseridas numa página. A primeira coisa a se fazer é criar uma classe com um método estático com essa funcionalidade. Veja:&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;package meupacote;&lt;br /&gt;&lt;br /&gt;public class MinhaClasse&lt;br /&gt;{&lt;br /&gt;  public static String escapeHtml(String html)&lt;br /&gt;  {&lt;br /&gt;      String texto = null;&lt;br /&gt;      if( html != null )&lt;br /&gt;      {&lt;br /&gt;          texto = html.replaceAll("&lt;", "&amp; lt;");&lt;br /&gt;          texto = texto.replaceAll("&gt;", "&amp; gt;");&lt;br /&gt;      }&lt;br /&gt;      return texto;&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;Como podemos ver, o método escapeHtml recebe um html e substitui todas as ocorrências de &lt; &gt; pelo seu código equivalente em html. O segundo passo é definir no seu arquivo tld essa function, inserindo a tag function e suas sub-tags de configuração. Veja:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;&amp;lt;function&amp;gt;&lt;br /&gt;   &amp;lt;description&amp;gt;&lt;br /&gt;       Substitui tags simbolos de maior e maior pelo seu codigo html&lt;br /&gt;   &amp;lt;/description&amp;gt;&lt;br /&gt;   &amp;lt;name&amp;gt;escapeHtml&amp;lt;/name&amp;gt;&lt;br /&gt;   &amp;lt;function-class&amp;gt;meupacote.MinhaClasse&amp;lt;/function-class&amp;gt;&lt;br /&gt;   &amp;lt;function-signature&amp;gt;boolean escapeHtml(java.lang.String)&amp;lt;/function-signature&amp;gt;&lt;br /&gt;&amp;lt;/function&amp;gt;&lt;/pre&gt;&lt;/div&gt;As tags mais importantes da configuração da função são "name", que indica por qual nome ela será referenciada no jsp, "function-class", que indica qual classe possui o método equivalente à função e "funtion-signature", que indica qual o método responderá pelas ações da função.&lt;br /&gt;&lt;br /&gt;Tendo isso, basta utilizar essa função no seu jsp, importando sua taglib e chamando a função no formato prefixo:nomefuncao(parametros). Veja o exemplo:&lt;br /&gt;&lt;br /&gt;&lt;div class="codigo"&gt;&lt;pre&gt;&amp;lt;%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %&amp;gt;&lt;br /&gt;&amp;lt;%@ taglib prefix="mt" uri="http://programandosemcafeina.blogspot.com/jsp/taglib/mt" %&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;h1&amp;gt;&amp;lt;c:out value="${mt:escapeHtml(materia.titulo)}" /&amp;gt;&amp;lt;/h1&amp;gt;&lt;br /&gt;&amp;lt;p&amp;gt;&amp;lt;c:out value="${mt:escapeHtml(materia.texto)}" /&amp;gt;&amp;lt;/p&amp;gt;&lt;/pre&gt;&lt;/div&gt;É claro que por eu usar a tag c:out não seria necessária a função escapeHtml, pois essa tag já faz a conversão que implementamos (e muitas outras). Contudo existem situações em que ela seria necessária. Um exemplo é se utilizarmos a &lt;a href="http://json-taglib.sourceforge.net/"&gt;Json Taglib&lt;/a&gt;. Essa taglib não tem a opção de escapar o html, para ela então faz-se necessário o uso de uma função como a que fizemos no exemplo.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-718867989421520940?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/718867989421520940/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/como-criar-funes-em-sua-taglib.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/718867989421520940'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/718867989421520940'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/como-criar-funes-em-sua-taglib.html' title='Como criar funções em sua taglib'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-2723451898507230080</id><published>2007-04-23T02:00:00.000-03:00</published><updated>2007-04-23T02:03:07.889-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sun tech days'/><title type='text'>Resumo do Sun Tech Days de 2007</title><content type='html'>Segue o resumão de todos os dias do Sun Tech Days de 2007. As fotos e vídeos irei postar durante a semana.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/04/resumo-do-sun-tech-days-do-dia-19042007.html"&gt;Resumo do Sun Tech Days do dia 19/04/2007&lt;/a&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/04/abertura-do-sun-tech-days.html"&gt;Abertura do Sun Tech Days&lt;/a&gt; &lt;/li&gt;&lt;li&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/04/demos-do-sun-tech-days.html"&gt;Demos do Sun Tech Days &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/04/palestra-sobre-javase-6-no-sun-tech.html"&gt;Palestra sobre JavaSE 6 no Sun Tech Days&lt;/a&gt; &lt;/li&gt;&lt;li&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/04/vmware-no-sun-tech-days.html"&gt;VMWare no Sun Tech Days&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/04/palestra-de-jax-ws-e-wsit-no-sun-tech.html"&gt;Palestra de JAX-WS e WSIT no Sun Tech Days &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/04/ejb-3-e-jpa-no-sun-tech-days.html"&gt;EJB 3 e JPA no Sun Tech Days&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/04/resumo-do-sun-tech-days-do-dia-20042007.html"&gt;Resumo do Sun Tech Days do dia 20/04/2007&lt;/a&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/04/abertura-do-segundo-dia-do-sun-tech.html"&gt;Abertura do segundo dia do Sun Tech Days&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/04/ferramentas-oracle-no-sun-tech-days.html"&gt;Ferramentas Oracle no Sun Tech Days&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/04/sobre-jsf-no-sun-tech-days.html"&gt;Sobre JSF no Sun Tech Days&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/04/linguagens-de-script-no-java.html"&gt;Linguagens de script no Java no Sun Tech Days&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/04/aplicaes-bpel-e-soa-com-oracle-no-sun.html"&gt;Aplicações BPEL e SOA com Oracle no Sun Tech Days&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/04/web-20-com-ajax-no-sun-tech-days.html"&gt;WEB 2.0 com Ajax no Sun Tech Days&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-2723451898507230080?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/2723451898507230080/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/resumo-do-sun-tech-days-de-2007.html#comment-form' title='2 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/2723451898507230080'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/2723451898507230080'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/resumo-do-sun-tech-days-de-2007.html' title='Resumo do Sun Tech Days de 2007'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-6056583875315799003</id><published>2007-04-23T01:55:00.001-03:00</published><updated>2008-04-01T18:22:24.601-03:00</updated><title type='text'>Resumo do Sun Tech Days do dia 20/04/2007</title><content type='html'>Aqui estão as minhas impressões e o que aprendi no segundo dia do Sun Tech Days de São Paulo:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/04/abertura-do-segundo-dia-do-sun-tech.html"&gt;Abertura do segundo dia do Sun Tech Days&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/04/ferramentas-oracle-no-sun-tech-days.html"&gt;Ferramentas Oracle no Sun Tech Days&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/04/sobre-jsf-no-sun-tech-days.html"&gt;Sobre JSF no Sun Tech Days&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/04/linguagens-de-script-no-java.html"&gt;Linguagens de script no Java no Sun Tech Days&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/04/aplicaes-bpel-e-soa-com-oracle-no-sun.html"&gt;Aplicações BPEL e SOA com Oracle no Sun Tech Days&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/04/web-20-com-ajax-no-sun-tech-days.html"&gt;WEB 2.0 com Ajax no Sun Tech Days&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;Demorei um pouco pra postar mais foi :)&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-6056583875315799003?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/6056583875315799003/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/resumo-do-sun-tech-days-do-dia-20042007.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/6056583875315799003'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/6056583875315799003'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/resumo-do-sun-tech-days-do-dia-20042007.html' title='Resumo do Sun Tech Days do dia 20/04/2007'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-1647288956205597431</id><published>2007-04-23T01:48:00.001-03:00</published><updated>2008-04-01T18:23:00.194-03:00</updated><title type='text'>WEB 2.0 com Ajax no Sun Tech Days</title><content type='html'>Essa palestra não trouxe muitas informações novas, era mais para iniciantes na área de ajax, explicando como funcionam as chamadas assincronas utilizando o objeto XMLHttpRequest e dando alguns exemplos bem manjados. Uma coisa apresentada que eu não conhecia é o JMaki Widget. Com essa ferramento no NetBeans, rapidamente a palestrante fez uma página com um menu de imagens de cidades, utilizando aquele efeito que o menu do OS-X tem, que ao clicar em uma delas era exibida a localização da cidade, utilizando Yahoo Maps. Só com Widgets. É uma boa dar uma pesquisada nessa ferramenta.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-1647288956205597431?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/1647288956205597431/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/web-20-com-ajax-no-sun-tech-days.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/1647288956205597431'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/1647288956205597431'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/web-20-com-ajax-no-sun-tech-days.html' title='WEB 2.0 com Ajax no Sun Tech Days'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-7857042474902238161</id><published>2007-04-23T01:34:00.001-03:00</published><updated>2008-04-01T18:23:20.604-03:00</updated><title type='text'>Aplicações BPEL e SOA com Oracle no Sun Tech Days</title><content type='html'>Com essa palestra finalmente aprendi o que é BPEL e para que serve. Relutei muito em ler sobre o assunto, mas não teve jeito :) Segundo a palestra utilizamos o BPEL quando precisamos chamar vários webservices em sequências.&lt;br /&gt;&lt;br /&gt;Achei muito interessante, mas a idéia de programar via XML não me agrada. Estruturas condicionais e loops num XML são coisas que nunca me agradarão. Contudo, o fato de ser XML torna a customização de um workflow mais fácil de ser manejado por um programa, como o da Oracle que foi exibido na palestra.&lt;br /&gt;&lt;br /&gt;Algumas tags de atividades que foram mostradas são &amp;lt;receive&amp;gt; que recebe a requisição de um cliente, o &amp;lt;reply&amp;gt; que retorna a requisição para o cliente e o &amp;lt;invoke&amp;gt; que chama um webservice. E algumas tags de estrutura que foram mostradas são &amp;lt;sequence&amp;gt; que faz com que as atividades em seu interior sejam executadas em sequencia, &amp;lt;flow&amp;gt; que faz com que as atividades em seu interior sejam executadas em paralelo e &amp;lt;switch&amp;gt; que tem a mesma responsabilidade de um switch programático.&lt;br /&gt;&lt;br /&gt;Outra coisa interessante mostrada nessa palestra foi como é formado um arquivo WSDL. Primeiro os tipos de dados, depois as mensagens, os ports referenciando as mensagens, os bindings e finalmente o service.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-7857042474902238161?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/7857042474902238161/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/aplicaes-bpel-e-soa-com-oracle-no-sun.html#comment-form' title='1 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/7857042474902238161'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/7857042474902238161'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/aplicaes-bpel-e-soa-com-oracle-no-sun.html' title='Aplicações BPEL e SOA com Oracle no Sun Tech Days'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-8089650140931530280</id><published>2007-04-23T01:20:00.001-03:00</published><updated>2008-04-01T18:23:43.146-03:00</updated><title type='text'>Linguagens de script no Java no Sun Tech Days</title><content type='html'>Palestra muito interessante mostrando a capacidade do Java 6 de executar linguagens de script. Basicamente existem duas formas de se executar uma liguagem de script no Java, por dentro de uma classe ou utilizando o executavel jrunscript.&lt;br /&gt;&lt;br /&gt;Dentro de uma classe é preciso pegar o ScriptEngine utilizando o ScriptEngineManager. O ScriptEngine possui um método eval(String ou Reader) que executa um script apartir de uma String ou apartir de um arquivo utilizando um Reader. Para invocar funções utiliza-se o método invokeFunction(String), e nessa chamada pode-se indicar os parâmetros da função pois é um método que utiliza varargs.&lt;br /&gt;&lt;br /&gt;Outra coisa legal é o método put  que serve para colocar objetos Java dentro do script. As alterações ocorridas neste objeto dentro do script são refletidas fora do script. Também foi mostrado um exemplo de javascript importanto classes Java, com a função importPackage.&lt;br /&gt;&lt;br /&gt;Muitos se perguntam se esse suporte a linguagens de script é mesmo necessário e o palestrante deu uma boa idéia do porque é sim importante. Primeiro porque a idéia é que o Java se separe de sua VM, de forma que esta VM possa rodar não só em diversas plataformas como também diversas linguagens. Segundo porque poder executar script apartir de classes Java é muito bom para customização de sistemas, principalmente na área financeira.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-8089650140931530280?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/8089650140931530280/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/linguagens-de-script-no-java.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/8089650140931530280'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/8089650140931530280'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/linguagens-de-script-no-java.html' title='Linguagens de script no Java no Sun Tech Days'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-7568038783196815132</id><published>2007-04-23T01:08:00.001-03:00</published><updated>2008-04-01T18:23:59.507-03:00</updated><title type='text'>Sobre JSF no Sun Tech Days</title><content type='html'>Nessa palestra falou-se sobre as qualidades e defeitos do JSF, motivos para utilizá-lo, e sobre o futuro deste framework. Ao final foram feitos alguns rápidos exemplos utilizando o Visual Web Pack em conjunto com o Sun Java Creator.&lt;br /&gt;&lt;br /&gt;Interessante foi saber que no futuro o JSTL e o JSF estarão no mesmo pacote. Ou seja, a Sun está fazendo de tudo para que o JSF enfim ganhe mercado. Hoje o JSTL é utilizado em conjunto com outros frameworks, mas se o JSF vier junto com o JSTL, será estranho utilizar outro que não o JSF.&lt;br /&gt;&lt;br /&gt;Outra ponto citado que eu já havia lido a respeito é que com o JSF é possivel que uma mesma página gere resultado em vários formatos, não só HTML. Basta utilizar algum RenderKit diferente do HTMLRenderKit. Pena que não foi mostrado nenhum exemplo, esse é mais um assunto que merece que eu faça uma boa pesquisa.&lt;br /&gt;&lt;br /&gt;Ao final a palestrante mostrou dois exemplos, um parecido com o da palestra do ADF da Oracle, com a diferença de que o Sun Java Creator com o Visual Web Pack funcionou muito melhor que o JDeveloper em termos de desempenho (é bom lembrar que o JDeveloper exibido era uma versão pré-alfa).&lt;br /&gt;&lt;br /&gt;O outro era uma integração com o PayPal que gerou um erro na aplicação. Quando isso ocorreu a palestrante ficou tão constrangida que tivemos dois minutos de silêncio absoluto. Felizmente o erro foi corrigido e ela enfim conseguiu exibir o exemplo pra nós.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-7568038783196815132?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/7568038783196815132/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/sobre-jsf-no-sun-tech-days.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/7568038783196815132'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/7568038783196815132'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/sobre-jsf-no-sun-tech-days.html' title='Sobre JSF no Sun Tech Days'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-6235139308675782127</id><published>2007-04-20T14:30:00.001-03:00</published><updated>2008-04-01T18:24:13.689-03:00</updated><title type='text'>Ferramentas Oracle no Sun Tech Days</title><content type='html'>Nessa palestra falou-se sobre algumas ferramentas da Oracle para desenvolvimento web como o ADF, framework da Oracle baseado em JSF e EJB3. O exemplo que o palestrante fez com o JDeveloper ficou muito bonito, um grid utilizando um componente JSF deste IDE, que ao ser renderizado é exibido com opções de redimensionar colunas e ordenar por elas. Mão na roda não ter que procurar e adaptar esses scripts. Quem já fez um grid desses, cheios de firulas web 2.0, sabe a chatice que é. Contudo, a apresentação deixou uma má impressão, pois o JDeveloper demorou muito para carregar. Sem graça o palestrante explicou que isso ocorria porque era versão pré-alfa. Amador?&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-6235139308675782127?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/6235139308675782127/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/ferramentas-oracle-no-sun-tech-days.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/6235139308675782127'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/6235139308675782127'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/ferramentas-oracle-no-sun-tech-days.html' title='Ferramentas Oracle no Sun Tech Days'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-3606655356655962707</id><published>2007-04-20T14:20:00.001-03:00</published><updated>2008-04-01T18:24:28.519-03:00</updated><title type='text'>Abertura do segundo dia do Sun Tech Days</title><content type='html'>Novamente escrevo sem acentos pois no connection point do Sun Tech Days nao tem como utilizar caracteres especiais.&lt;br /&gt;&lt;br /&gt;A abertura desse segundo dia foi muito macante. O palestrante parecia mais um pastor de igreja evangelica do que alguem da area de tecnologia. Objetividade zero. Foi mais de meia hora repetindo o mesmo refrao de comunidade. Esse tipo de discurso serve mais para pessoas da area de humanas.&lt;br /&gt;&lt;br /&gt;Depois tivemos uma palestra sobre algumas ferramentas da Oracle, que descreverei mais tarde.&lt;br /&gt;&lt;br /&gt;O bom mesmo, foi ao final da abertura, que teve um divertido concurso habilidades no palco. Desenvolvedores e arquitetos subiam para cantar, dancar e especialmente pagar mico. Destaque para os vencedores do concurso, uma menina que veio preparadissima para dancar danca do ventre e um rapaz, mais troncho nunca vi, que tambem dancou danca do ventre, sem camisa, com as banhas balancando. Eu ri muito.&lt;br /&gt;&lt;br /&gt;Essas palhacadas eu filmei. Logo logo colocarei os videos no Youtube e entao eu posto aqui no Programando sem cafeina.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-3606655356655962707?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/3606655356655962707/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/abertura-do-segundo-dia-do-sun-tech.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3606655356655962707'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/3606655356655962707'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/abertura-do-segundo-dia-do-sun-tech.html' title='Abertura do segundo dia do Sun Tech Days'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-5942437341169292433</id><published>2007-04-20T00:08:00.001-03:00</published><updated>2008-04-01T18:24:44.113-03:00</updated><title type='text'>Resumo do Sun Tech Days do dia 19/04/2007</title><content type='html'>&lt;p&gt;Aqui estão as minhas impressões e o que aprendi no primeiro dia do Sun Tech Days de São Paulo: &lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/04/abertura-do-sun-tech-days.html"&gt;Abertura do Sun Tech Days&lt;/a&gt; &lt;/li&gt;&lt;li&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/04/demos-do-sun-tech-days.html"&gt;Demos do Sun Tech Days &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/04/palestra-sobre-javase-6-no-sun-tech.html"&gt;Palestra sobre JavaSE 6 no Sun Tech Days&lt;/a&gt; &lt;/li&gt;&lt;li&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/04/vmware-no-sun-tech-days.html"&gt;VMWare no Sun Tech Days&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/04/palestra-de-jax-ws-e-wsit-no-sun-tech.html"&gt;Palestra de JAX-WS e WSIT no Sun Tech Days &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://programandosemcafeina.blogspot.com/2007/04/ejb-3-e-jpa-no-sun-tech-days.html"&gt;EJB 3 e JPA no Sun Tech Days &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Agora vou dormir porque amanhã tem mais, se eu conseguir vaga no connection point eu posto mais durante o dia.&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-5942437341169292433?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/5942437341169292433/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/resumo-do-sun-tech-days-do-dia-19042007.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/5942437341169292433'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/5942437341169292433'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/resumo-do-sun-tech-days-do-dia-19042007.html' title='Resumo do Sun Tech Days do dia 19/04/2007'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-5343183946201723421</id><published>2007-04-19T19:07:00.001-03:00</published><updated>2008-04-01T18:25:01.399-03:00</updated><title type='text'>EJB 3 e JPA no Sun Tech Days</title><content type='html'>A palestrante explicou como funciona o mapeamento objeto-relacional do JPA por anotações de relacoes ManyToOne e ManyToMany, e como mapear herança utilizando uma só tabela. Para diferenciar se um objeto é de uma subclasse ou de outra utiliza-se a anotação @DiscriminatorColumnName e @DiscriminatorValue. Essa foi a melhor palestra do dia pois explicou do ínicio ao fim o mapeamento, como funciona o EntityUnit, como funciona o EntityManager, mostrou o ciclo de vida de uma entidade e deu ótimas dicas para não termos problemas no desenvolvimento utilizando JPA. Mais tarde quando eu baixar os power points repassarei essas dicas. Agora eu vou aproveitar o cocktail :)&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-5343183946201723421?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/5343183946201723421/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/ejb-3-e-jpa-no-sun-tech-days.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/5343183946201723421'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/5343183946201723421'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/ejb-3-e-jpa-no-sun-tech-days.html' title='EJB 3 e JPA no Sun Tech Days'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-6498686101261004016</id><published>2007-04-19T19:01:00.001-03:00</published><updated>2008-04-01T18:25:10.974-03:00</updated><title type='text'>Palestra de JAX-WS e WSIT no Sun Tech Days</title><content type='html'>Não consegui entender muito sobre esta palestra porque o palestrante falava rápido demais e a tradução simultânea, como mencionei antes, é um desastre, e além disso, nessa palestra a tradução estava especialmente ruim. &lt;br /&gt;&lt;br /&gt;O que achei interessante, mas não entendi como funciona é o método de webservice assíncrono, assim que puder pesquisarei sobre o assunto. O palestrante fez também um WebService simples utilizando o JavaSE 6, com a anotacao @WebService e gerando os stubs com o comando wsimport. É incrivel como WebService fica mais facil com o JavaSE 6.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-6498686101261004016?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/6498686101261004016/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/palestra-de-jax-ws-e-wsit-no-sun-tech.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/6498686101261004016'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/6498686101261004016'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/palestra-de-jax-ws-e-wsit-no-sun-tech.html' title='Palestra de JAX-WS e WSIT no Sun Tech Days'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-4776158544541847840</id><published>2007-04-19T18:57:00.001-03:00</published><updated>2008-04-01T19:00:50.598-03:00</updated><title type='text'>VMWare no Sun Tech Days</title><content type='html'>Nao gostei muito da palestra sobre Virtual Desktop da VMWare, isso porque infra-estrutura nao eh a minha praia, e as coisas que aprendi nao me serao uteis no trabalho, apenas como curiosidade. Gostei da ideia de economia de energia que a utilizacao de maquinas virtuais pode proporcionar. E a ideia de Remotion, no qual um servidor virtual pode ser transferido de um servidor fisico para outro. Isso eh muito util. Uma sugestao que o palestrante deu eh utilizar isso para transferir ambientes totalmente testados para producao, sem nova instalacao.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-4776158544541847840?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/4776158544541847840/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/vmware-no-sun-tech-days.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/4776158544541847840'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/4776158544541847840'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/vmware-no-sun-tech-days.html' title='VMWare no Sun Tech Days'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-241608186409990817</id><published>2007-04-19T18:45:00.001-03:00</published><updated>2008-04-01T19:01:45.587-03:00</updated><title type='text'>Palestra sobre JavaSE 6 no Sun Tech Days</title><content type='html'>Tivemos uma palestra mostrando as novas funcionalidades do JavaSE 6. O que foi mais interessante foram as alterações no Swing e no AWT. Dentre essas novas funções temos a nova classe TrayIcon que permite colocar um programa na bandeja do Sistema Operacional (Ali, ao lado do relógio), uma forma exibir uma tela de splash antes mesmo de carregar a JVM, e a classe Desktop que permite abrir sistemas nativos.&lt;br /&gt;&lt;br /&gt;Outra nova funcionalidade que foi citada e que parece muito boa é a SwingWorker, que serviria para facilitar o trabalho multi-thread em aplicações Swing, pena que não mostraram nem mesmo um exemplo dela. Qualquer dia desses eu dou uma pesquisada sobre ela para colocar aqui.&lt;br /&gt;&lt;br /&gt;No JavaSE6 também foram introduzidos novos executáveis para monitoração da aplicação, como é o caso do jconsole. Outro destaque é pela nova forma de criação de WebServices que dispensa a criacao do WSDL, bastando anotar a classe que irá prover o servico com @WebService. O proprio JavaSE levantaria um mini-servidor http para oferecer este servico, bastanto publicar o endpoint com Endpoint.publish.&lt;br /&gt;&lt;br /&gt;Segundo o palestrante a migracao de JavaSE 5 para JavaSE 6 nao é custo algum pois nao há alteracao de sintaxe. Portanto é extremamente recomendável a migração devido às melhorias de performance.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-241608186409990817?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/241608186409990817/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/palestra-sobre-javase-6-no-sun-tech.html#comment-form' title='2 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/241608186409990817'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/241608186409990817'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/palestra-sobre-javase-6-no-sun-tech.html' title='Palestra sobre JavaSE 6 no Sun Tech Days'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7709637092684013725.post-467570707284590834</id><published>2007-04-19T18:39:00.001-03:00</published><updated>2008-04-01T19:02:01.389-03:00</updated><title type='text'>Demos do Sun Tech Days</title><content type='html'>Foram exibidas duas demonstracões de desenvolvimento rápido com o NetBeans. Nao sou fã dessa IDE, mas tenho que tirar o chapéu, foram ótimas demonstrações. Primeiro fez-se um menu SVG para JavaME e depois uma demonstração de uma aplicacao com o Matise. Ambos mostraram como é rápido o desenvolvimento no NetBeans. Acredito que no dia em que a Sun investir no aperfeiçoamento do editor desta IDE, o NetBeans finalmente poderá barrar o Eclipse. Enquanto isso continuo fiel ao programa que esconde o sol.&lt;br /&gt;&lt;br /&gt;Outra coisa legal foi a exibição do SunSpot, uma espécie de luva que faz com que tenhamos um efeito similar ao do filme Minority Report misturado com o LookInGlass, o destop 3D feito em Java. Não funcionou perfeitamente, teve um momento em que a menina que apresentava teve que repetir o movimento umas duas vezes para girar umas janelas, mesmo assim foi lindo de se ver. Sinto que o futuro dos desktops será emocionante.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;&lt;!--
google_ad_client = "pub-8291320703218320";
/* 468x60, criado 06/03/09 */
google_ad_slot = "5590043178";
google_ad_width = 468;
google_ad_height = 60;
//--&gt;
&lt;/script&gt;
&lt;script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;
&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7709637092684013725-467570707284590834?l=programandosemcafeina.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://programandosemcafeina.blogspot.com/feeds/467570707284590834/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/demos-do-sun-tech-days.html#comment-form' title='1 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/467570707284590834'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7709637092684013725/posts/default/467570707284590834'/><link rel='alternate' type='text/html' href='http://programandosemcafeina.blogspot.com/2007/04/demos-do-sun-tech-days.html' title='Demos do Sun Tech Days'/><author><name>Tiago Albineli Motta</name><uri>http://www.blogger.com/profile/04371749360526831167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry></feed>
