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.