quinta-feira, 9 de outubro de 2008

Imagem de fundo no captcha gerado pelo simple_captcha

O simple_captcha, plugin de geração de captcha para rails, 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 rmagick. Vou mostrar aqui mais ou menos como fiz isso em um recente projeto.

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:

def generate_simple_captcha_image(options={})  #:nodoc

fundo = Magick::Image.read(url_da_imagem_de_fundo).first
preenchimento = Magick::TextureFill.new(fundo)

@image = Magick::Image.new(197, 45, preenchimento) do
self.format = 'JPG'
end

# ...

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:

  def url_da_imagem_de_fundo
"#{RAILS_ROOT}/vendor/plugins/simple_captcha/assets/imgs/picture_" + (rand(9)+1).to_s + ".jpg"
end

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.

quarta-feira, 8 de outubro de 2008

Validação no lado cliente com form_remote_tag

Para fazer validações de formulário no lado cliente basta informar ao método form_tag ou ao form_for, utilizando o parâmetro :onsubmit, o código javascript que se deseja executar para validá-lo. Veja no exemplo:

  <% form_tag "/entrar", :onsubmit => 'return valida(this)' do %>
<h1>ok</h1>
<% end %>

Contudo para a tag form_remote_tag e form_remote_for não há a possibilidade de utilizar o parâmetro :onsubmit. Isso porque o formulário gerado pelo rails 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 :before como é mostrado no exemplo:

  <% form_remote_tag :url => "/entrar", 
:update => 'meu_div',
:before => 'if( !valida(this) ) return false' do %>
<h1>ok</h1>
<% end %>

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.

quinta-feira, 2 de outubro de 2008

Impedindo redeclaração de objetos javascript

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.

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.
var Repositorio = {
sequencia:0,
lista:[],
registrar:function(obj) {
var id = ++this.sequencia;
this.lista[ id ] = obj;
return id;
},
recuperar:function(id) {
return this.lista[ id ];
}
}
Agora estando esse código num arquivo por exemplo repositorio.js teríamos o problema relatado com o código a seguir:
<script src="repositorio.js"></script>
<script>
Repositorio.registrar( new Object() );
Repositorio.registrar( new Object() );
</script>
<script src="repositorio.js"></script>
<script>
alert( Repositorio.recuperar( 1 ) );
</script>
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:
try{ Repositorio.existo(); }
catch(e) {
Repositorio = {
sequencia:0,
lista:[],
existo:function(){},
registrar:function(obj) {
var id = ++this.sequencia;
this.lista[ id ] = obj;
return id;
},
recuperar:function(id) {
return this.lista[ id ];
}
}
}
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.

quinta-feira, 18 de setembro de 2008

Cópia exclusiva de coleção

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, causando dores de cabeça e acessos de insanidade nos desenvolvedores. Martin Fowler em seu livro Patterns of Enterprise Application Architecture 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.

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 synchronized. Veja abaixo um exemplo de classe que torna o uso de uma lista à prova de erros de concorrência:

public class ListaSegura {
private List lista = new ArrayList();
public void adiciona(Object objeto) {
synchronized (lista) {
lista.add(objeto);
}
}
public void remove(Object objeto) {
synchronized (lista) {
lista.remove(objeto);
}
}
public void listar() {
synchronized (lista) {
for (Object o : lista) {
System.out.println(o);
}
}
}
}

É 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:

1- Alteração da coleção: 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.

2- Iteração prolongada: 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.

Para resolver esses dois casos identifiquei o padrão de desenvolvimento Cópia Exclusiva de Coleção. 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.

public class Armario {
private List coisas = new LinkedList();
public void removeLixo() {
List copia = lista();
for (Coisa coisa : copia) {
if (coisa.isLixo())
remove(coisa);
}
}
public List lista() {
List copia = null;
synchronized (coisas) {
copia = new ArrayList(coisas.size());
copia.addAll(coisas);
}
return copia;
}
public void adiciona(Coisa coisa) {
synchronized (coisas) {
coisas.add(coisa);
}
}
public void remove(Coisa coisa) {
synchronized (coisas) {
coisas.remove(coisa);
}
}
}

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 java.util.ConcurrentModificationException é evitar a utilização de objetos compartilhados entre threads. Quando isso não é possível, o jeito é utilizar um padrão como Cópia Exclusiva de Coleção.

terça-feira, 16 de setembro de 2008

On-demand javascript com PHP

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.

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 On-Demand_Javascript - AjaxPatterns.

Com PHP 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:
var Livros = {
listar:function() {
var s = document.createElement('script');
s.type = 'text/javascript';
s.src = 'listaDeLivros.php?callback=Livros.onload_listar';
document.getElementsByTagName('head')[0].appendChild(s);
}
onload_listar:function(html) {
document.getElementsByTagName('lista').innerHTML = html;
}
}

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:
<?php echo $_REQUEST['callback']; ?>("<h1>Lista de livros</h1>");

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.

Para evitar isso podemos utilizar a função ob_start, 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:
<?php 
function captura_buffer($html) {
$html = addslashes($html);
$html = str_replace("\n", '\n', $html);
$html = str_replace("\r", '\r', $html);
return $_REQUEST['callback'] . '("' . $html . '");';
}
ob_start('captura_buffer');

$livros = funcao_ficticia_para_obter_livros();

foreach($livros as $livro) {
?>

<p>
<?php echo $livro->id ?> -
<b><?php echo $livro->titulo ?></b> -
<?php echo $livro->autor ?>
</p>

<?php } ?>

quarta-feira, 3 de setembro de 2008

KeyDown e KeyUp para applets java

Um grande problema da interface KeyListener do Java é 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 keyPressed e keyReleased 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 applet abaixo:
public class ComProblema extends Applet implements KeyListener {
    @Override
    public void init() {
        addKeyListener(this);
    }
    public void keyPressed(KeyEvent e) {
        System.out.println("Pressionou: " + e.getKeyCode());
    }
    public void keyReleased(KeyEvent e) {
        System.out.println("Soltou: " + e.getKeyCode());
    }
    public void keyTyped(KeyEvent e) {}
}
Ao executarmos esse applet clicando e segurando a tecla "a" por alguns segundos, depois apertar e segurar a "w" por outros tantos e ao final soltar as duas, teremos o seguinte resultado:
Pressionou: 65
Soltou: 65
Pressionou: 65
Soltou: 65
Pressionou: 65
Soltou: 65
Pressionou: 65
Soltou: 65
Pressionou: 65
Soltou: 65
Pressionou: 65
Soltou: 65
Pressionou: 65
Pressionou: 87
Soltou: 87
Pressionou: 87
Soltou: 87
Pressionou: 87
Soltou: 87
Pressionou: 87
Soltou: 87
Pressionou: 87
Soltou: 87
Pressionou: 87
Soltou: 87
Pressionou: 87
Soltou: 87
Pressionou: 87
Soltou: 65
Soltou: 87
Encontrei em diversos fóruns pessoas com o mesmo problema e nenhuma solução. Então, para resolvê-lo implementei uma espécie de KeyAdapter para facilitar a notificação de quando uma tecla é apertada e quando ela é solta. O uso de exemplo dela ficaria como o mostrado abaixo:
public class SemProblema extends Applet {
    @Override
    public void init() {
        super.init();
        addKeyListener(new CumulativeKeyAdapterImpl());
    }
}
class CumulativeKeyAdapterImpl extends AcumuladorKeyAdapter {
    @Override
    public void keyDown(KeyEvent e) {
        System.out.println("Down: " + e.getKeyCode());
    }
    @Override
    public void keyUp(KeyEvent e) {
        System.out.println("Up: " + e.getKeyCode());
    }
}
O resultado fazendo a mesma combinação de teclas apertadas no teste anterior neste outro applet, seria como o mostrado a seguir:
Down: 65
Down: 87
Up: 65
Up: 87
Você também pode ver outros exemplos de applets utilizando o AcumuladorKeyAdapter aqui. Para utilizar esse recurso basta baixar os fontes e binários da timotta-api e colocar o timotta-api-applet.jar no atributo archive da sua tag applet, como demonstrado no exemplo abaixo:
<applet code="br.com.timotta.applet.MostraTeclas" 
    archive="timotta-api-applet-exemplos.jar,timotta-api-applet.jar"
    width=256 height=256 >
</applet>

segunda-feira, 18 de agosto de 2008

Passagem de array como parâmetro em subrotinas de Perl

Passar um array como parâmetro em uma subrotina de Perl 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:
sub faz_nada_util
{
print "(" . $_[0] . "," . $_[1] . ")";
}

my @array_1 = (1,2);
my @array_2 = [3,4];

faz_nada_util @array_1;
faz_nada_util \@array_1;
faz_nada_util @array_2;
faz_nada_util (5,6);
faz_nada_util [7,8];

A saída será:
(1,2)
(ARRAY(0x81536b4),)
(ARRAY(0x815360c),)
(5,6)
(ARRAY(0x8176f5c),)

O importante no exemplo é verificar que quando criamos um array utilizando colchetes [] estamos retornando na verdade não o array, mas a referência a este array. Dessa forma o primeiro item de @_ seria a referência ao array. Isso é mostrado na terceira e na última linha do resultado.

Quando criamos com o uso de parênteses () 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 @_. Isso é demonstrado na primeira e na quarta linha do resultado. Usando a contra-barra \ indicamos no segundo print que queremos enviar a referência do array criado com parênteses.

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:
sub continua_fazendo_nada_util
{
my $nome = shift;
print $nome . ": \n";
foreach $nome ( @_ ) {
print "- " . $nome . "\n";
}
}
continua_fazendo_nada_util("tiago", (1,2,3,4));

É 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 shift 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.
sub nada_pra_fazer_aqui
{
my @array_1 = @{$_[0]};
my @array_2 = @{$_[1]};

print "array1: \n";
foreach $a (@array_1) {
print "- $a\n";
}
print "array2: \n";
foreach $a (@array_2) {
print "- $a\n";
}

}
nada_pra_fazer_aqui [1,2,3], [4,5,6];