Posts de ‘Tiago Motta’

[Tiago Motta] Validação no lado cliente com form_remote_tag

Wednesday, October 8th, 2008

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.

[Tiago Motta] Impedindo redeclaração de objetos javascript

Thursday, October 2nd, 2008

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.

[Tiago Motta] Cópia exclusiva de coleção

Thursday, September 18th, 2008

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.