Arquivo da categoria: Ruby

Você sabia que dá para usar versões diferentes de ruby na mesma máquina ?

RVM ou Ruby Version Manager é o gerenciador de versões Ruby.

Com ele você pode rodar projetos com rubys diferentes (exemplo: com a versão 1.8.7 e 1.9.3) mantendo os ambientes e as gems instaladas isoladas.

Conheça mais sobre o RVM em: https://rvm.io/

Ou se preferir, pode ir direto para a página de instalação em https://rvm.io/rvm/install/

Um outro bom exemplo de uso do RVM, não é só a utilização de multiplos Rubys, e sim a separação que ele faz entre os projetos.

Você pode criar com o RVM um “Gemset”, que é um grupo isolado onde se instalada as gem de seu projeto. Com isso você pode usar o mesmo ruby, e ter mais de um gemset configurado.

Share

Raptcha: Um captcha simples para aplicações Rails

 

Humor: Robôs lendo o captcha

Captcha é um teste de desafio cognitivo, utilizado como ferramenta anti-spam. O que acontece é que um script automatizado (um robô), não consegue ler o texto embaralhado nas imagens. Isso faz com que seja uma excelente ferramenta para combater o spam ou prevenir que scripts tenham acesso a determinadas áreas.

Recentemente tive que procurar uma gem (um pacote do rubygems) de captcha para minha aplicação rails, mas que fosse a mais simples possível e sem nenhuma dependência externa.

Vi que a mais utilizada é a gem recaptcha, que utiliza a estrutura da API do google de captcha (google.com/recaptcha). Para resolver o meu problema ela não serve, já que o meu projeto vai rodar em máquinas sem acesso a internet.

Então ao continuar a busca por uma boa gem de captcha, recebi a recomendação de usar o Raptcha.

O Raptcha é realmente muito simples de configurar e de usar. Ele usa o Image Magick para renderizar as imagens em tempo real.

Em alguns casos isso pode ser um problema, mas no caso da minha aplicação, não havia necessidade de cachear as imagens.

Ao lado temos um exemplo do uso do raptcha.

Você pode conferir a documentação de instalação pelo link: https://github.com/ahoward/raptcha

Quando estava fazendo os meus testes, tive dificuldade para fazer as letras do captcha aparecer. Se isso acontecer, tente instalar o pacote GhostScript. Se você estiver usando o ubuntu, o imagemagick e o ghostscript tem no synaptic. Se você usa macbook, ambos os pacotes podem ser instalados pelo repositório do brew (não sei se tem no ports).

Um outro problema que eu tive que resolver, foi implementar o botão de gerar outro captcha. Infelizmente o raptcha não da suporte nativo a isso. Mas pode ser facilmente resolvido usando Ajax. O que eu fiz foi colocar no controller um render partial como mostra o código a seguir:

1
2
3
4
5
6
7
8
9
10
11
class RaptchaController < ApplicationController
 
  def index
    Raptcha.render(controller=self, params)
  end
 
  def get_form
    render :partial => "form"
  end
 
end

E no partial “form” ficou assim:

1
<%= raw Raptcha.input %>

Dessa forma, a chamar a url: http://localhost:3000/raptcha/get_form obtemos o retorno de todo o form do raptcha:

1
2
3
4
5
6
7
8
<div class="raptcha">
   <img class="raptcha-image" src="/raptcha?e=DaROXGWrRTrLdYXmJ05iKg" alt="Imagem de validação" width="242" height="68" />
 
   <label><img id="captcha-reload" title="Recarregar imagem" src="/images/mini-reload.gif" alt="" />Digite as letras acima:</label>
 
   <input class="raptcha-input box" name="raptcha[t]" type="textarea" />
   <input class="raptcha-word" name="raptcha[w]" type="hidden" value="DaROXGWrRTrLdYXmJ05iKg" />
   <input class="raptcha-timebomb" name="raptcha[b]" type="hidden" value="nEZ-sQYpOFC2YIE2qhAjeQ" /></div>

Então basta você colocar uma ação no botão #captcha-reload e obter um novo form por ajax e substituir o form antigo na página de sua aplicação. Fazendo assim, você poderá recarregar o seu captcha quantas vezes precisar…

Então é isso… Se você precisa de uma solução simples para o captcha em seu projeto, experimente o Raptcha usando as dicas acima… 😉

Share

Net-SFTP: Calculando o progresso do upload

Estou escrevendo esse post, para complementar o post sobre o timeout do net-sftp, onde fiquei devendo essa a solução de calculo do progresso…

Para obter o progresso durante o upload, você precisa usar o callback do método upload! e jogar numa variável os valores do tamanho local e do tamanho do arquivo no destino.

Vejamos um pequeno exemplo, retirado da documentação:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
require 'rubygems'
require 'net/sftp'
Net::SFTP.start('192.168.0.2', 'rafaelbiriba', {:password => 'teste', :timeout => 3}) do |sftp|
  sftp.upload!("/Users/rafaelbiriba/Projects/temp/video-teste-sftp.mp4", "/home/rafaelbiriba/video-teste-sftp.mp4") do |event, uploader, *args|
    case event
    when :open then
      # args[0] : file metadata
      puts "starting upload: #{args[0].local} -> #{args[0].remote} (#{args[0].size} bytes}"
    when :put then
      # args[0] : file metadata
      # args[1] : byte offset in remote file
      # args[2] : data being written (as string)
      puts "writing #{args[2].length} bytes to #{args[0].remote} starting at #{args[1]}"
    when :finish then
      puts "all done!"
    end
  end
end

No exemplo acima podemos verificar:

  • Ao abrir a conexão (when open), podemos chamar args[0].size e obter o tamanho total do arquivo
  • Durante a transferência (when put), ao chamar args[1] podemos obter o tamanho do arquivo remoto

Com isso, quando o tamanho total do arquivo for igual ao tamanho do arquivo remoto, então o upload estará terminado (100%). Para obter as porcentagens, basta fazer o cálculo.

Confira no exemplo abaixo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
require 'rubygems'
require 'net/sftp'
@total_size = 0
@remote_size = 0
@current_progress = 0
 
def update_sftp_progress
  progress = (@remote_size*100)/@total_size)
  if @current_progress < progress then
    @current_progress = progress
    puts "Uploading: #{@current_progress}%" 
  end
end
 
Net::SFTP.start('192.168.0.2', 'rafaelbiriba', {:password => 'teste', :timeout => 3}) do |sftp|
 sftp.upload!("/Users/rafaelbiriba/Projects/temp/video-teste-sftp.mp4", "/home/rafaelbiriba/video-teste-sftp.mp4") do |event, uploader, *args|
   case event
     when :open then
       @total_size = args[0].size
       puts "Starting upload..."
     when :put then
       @remote_size = args[1]
       update_sftp_progress
     when :finish then
       puts "Finished!"
     end
   end
end

Toda vez que o arquivo no destino for incrementado, o evento (:put) será disparado, chamando o método update_sftp_progress.

Nesse método é feito o cálculo da porcentagem(ver no exemplo acima). Ao invés de imprimir a porcentagem(com puts), você também pode salvar no banco de dados, ou utilizar o valor para fazer uma barra de progresso.

Espero ter ajudado, 😉

Share

Net-sftp: Solução para timeout durante o upload

Desenvolvendo um script em ruby para upload de arquivos, utilizando a gem net-sftp, acabei encontrando um problema. A falta de timeout durante a transferência.

Decidi escrever esse post, para compartilhar a minha solução e também receber opniões e outras soluções para o problema. Fiz algo parecido quando escrevi o post sobre o cron de 15 em 15 segundos e obtive resultados bem legais.

Introdução ao problema
A gem net-sftp é bastante utilizada em scripts ruby que requerem alguma transferência de arquivo utilizando uma conexão segura, como por exemplo, upload, download, criação de diretórios e etc… Utilizando a gem net-ssh para estabelecer essa conexão.

Primeiramente, vamor ver um exemplo do problema:

1
2
3
4
5
require 'rubygems'
require 'net/sftp'
Net::SFTP.start('192.168.0.2', 'rafaelbiriba', {:password => 'teste', :timeout => 3}) do |sftp|
  sftp.upload!("/Users/rafaelbiriba/Projects/temp/video-teste-sftp.mp4", "/home/rafaelbiriba/video-teste-sftp.mp4")
end

O código acima faz o upload de um arquivo de mais de 400 Mb. Se durante o upload houver um problema na sua conexão, o upload fica “esperando” a rede voltar, o que é um problemão, pois ele trava a execução do script, sem dar nenhum sinal de vida.

De acordo com a documentação do net-sftp, você pode utilizar o método “upload” no lugar de “upload!” para não travar a execução do script. Mas ainda sim o problema continua. Fazendo isso, você vai precisar rodar um loop até que o upload termine. Ou seja, se a conexão cair, o método só vai terminar(e sair do loop) quando a conexão voltar. Então essa opção foi logo descartada.

O problema mesmo só acontece quando há perda de conexão depois que a sessão inicia. (O parâmetro :timeout apenas estabelece o limite de espera para iniciar a sessão e não durante a transferência dos arquivos.)

Se o net-sftp / net-ssh aceitasse a opção “ServerAliveInterval” (Parametro de configuração do ssh), ele iria verificar a conexão durante a transferência e ao alcançar o limite (“ServerAliveCountMax”) seria disparado o timeout. Infelizmente, de acordo com a documentação do net-ssh esses parâmetros não estão disponíveis.

Seguindo com a leitura e busca pela solução na documentação do net-sftp, achei uma possível pista para encontrar a solução:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
require 'rubygems'
require 'net/sftp'
Net::SFTP.start('192.168.0.2', 'rafaelbiriba', {:password => 'teste', :timeout => 3}) do |sftp|
  sftp.upload!("/Users/rafaelbiriba/Projects/temp/video-teste-sftp.mp4", "/home/rafaelbiriba/video-teste-sftp.mp4") do |event, uploader, *args|
    case event
    when :open then
      # args[0] : file metadata
      puts "starting upload: #{args[0].local} -> #{args[0].remote} (#{args[0].size} bytes}"
    when :put then
      # args[0] : file metadata
      # args[1] : byte offset in remote file
      # args[2] : data being written (as string)
      puts "writing #{args[2].length} bytes to #{args[0].remote} starting at #{args[1]}"
    when :finish then
      puts "all done!"
    end
  end
end

Acima temos um exemplo simples retirado da documentação para o controle do progresso de upload.

Quando o upload inicia, o case recebe o evento “:open” onde podemos obter o tamanho total do arquivo e logo em seguida recebe os eventos “:put” onde é possível verificar  o quanto (em bytes) do arquivo já foi enviado, possibilitando assim a criação da porcentagem do upload.

Como o foco aqui não é mostrar como obter o progresso (posso mostrar isso exclusivamente em outro post calculando o progresso durante o upload), não vou entrar muito em detalhes sobre isso. O que realmente inporta nesses eventos são que durante o upload (:put), ao perder a conexão, nenhum evento é disparado, ou seja, o script não tem como sabe que a conexão caiu e a execução fica travada no upload.

Minha solução:

Depois de todo esse estudo detalhado sobre o problema, comecei a pensar numa possível solução. Eu precisava utilizar alguma coisa que pudesse verificar e dar timeout no upload caso ele parasse de responder/transferir.

Foi então que pesquisei, testei e implementei o rufus-scheduler*,  um agendador de tarefas que roda dentro do script ruby(mas numa outra thread), onde posso definir um intervalo de execução para executar alguma coisa, como se fosse um cron. ( *Sugestão do @vicentemundim )

Então a solução foi: Assim que o upload começar, eu defino um tempo de 10 segundos para ele executar uma tarefa com “raise exception”. Então toda vez que o upload responder ao evento (:put), informando que conseguiu subir mais um trecho do arquivo, eu reseto o tempo de execução da tarefa e defino novamente em 10 segundos. Fazendo isso, quando a conexão cair ou o upload travar por algum motivo, ele vai parar de responder ao evento “:put”, e então a tarefa agendada para 10 segundos vai disparar o raise, interrompendo a execução do script.

Depois de alguns testes, tanto de desempenho quanto de falhas, percebi que essa resolução serviu perfeitamente. Segue o exemplo do código utilizado:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
require 'rubygems'
require 'rufus/scheduler'
require 'net/sftp'
 
@job = nil
@scheduler = Rufus::Scheduler::PlainScheduler.start_new  
def @scheduler.handle_exception (job, exception)
  abort(exception.message)
end
 
def job_scheduler
  @job.unschedule unless @job.nil?
  @job = @scheduler.every "10s" do
    raise "SFTP Connection Lost or upload freezed timeout"
  end    
end
 
Net::SFTP.start('192.168.0.2', 'rafaelbiriba', {:password => 'teste', :timeout => 3}) do |sftp|
  sftp.upload!("/Users/rafaelbiriba/Projects/temp/video-teste-sftp.mp4", "/home/rafaelbiriba/video-teste-sftp.mp4") do |event, uploader, *args|
    case event
    when :open then
      puts "starting upload: #{args[0].local} -> #{args[0].remote} (#{args[0].size} bytes}"
      job_scheduler
    when :put then
      puts "writing #{args[2].length} bytes to #{args[0].remote} starting at #{args[1]}"
      job_scheduler
    when :finish then
      puts "all done!"
     @job.unschedule unless @job.nil? 
    end
  end
end

Quando o upload inicia (:open) e durante a transferência (:put), ele chama o método job_scheduler que é responsável por resetar a tarefa anterior(se houver) e agendar uma nova com timeout de 10 segundos.

Conforme expliquei acima, se o upload travar, o tempo limite de 10 segundos é alcançado e uma exceção é disparada.

Como o agendador do rufus roda em outra thread, qualquer exceção disparada é capturada e tratada pelo próprio rufus, fazendo com que a execução do script continue. Então, sobrescrevi o método (handle_exception) e ao receber a exceção, gero um abort que irá interromper todo o script.

Infelizmente, são poucos os sites relacionados sobre esse assunto, o que dificultou bastante a busca e o desenvolvimento. Mesmo assim, acredito ter resolvido o problema com uma solução bem razoável.

Já falei em cima, mas vale ressaltar:
“Decidi escrever esse post, para compartilhar a minha solução e também receber opniões e outras soluções para o problema. Fiz algo parecido quando escrevi o post sobre o cron de 15 em 15 segundos e obtive resultados bem legais.”

Estou pensando em tentar solucionar isso direto no código do net-sftp e quem sabe mandar um patch para o autor. De qualquer forma, fico no aguardo de qualquer comentário e/ou soluções melhores.

handle_exception
Share

Cloud Crowd: A primeira action

cloud-crowd

Cloud Crowd é um controlador de filas para processamento paralelo, desenvolvido em ruby. Pode ser utilizado em Encoding de video, migração de arquivos e bancos de dados, redimensionamento de imagens e etc…

Uma explicação rápida: Em uma máquina você levanta o server, onde é gerenciado a fila (criação, exclusão, status das tarefas e etc…). Em outras máquinas você levanta o node, onde as terefas serão recebidas e executadas. Ao terminar, o node informa ao server, liberando-se para receber outra tarefa.

Essa semana comecei a desvendar os benefícios do Cloud Crowd, principalmente para encoding paralelo de vídeo. Mas antes, tive que entender como ele funciona e o que tem para nos oferecer.

Então, acompanhando o “tutorial” em http://wiki.github.com/documentcloud/cloud-crowd/writing-an-action, escrevi uma pequena action:

class HelloWorld < CloudCrowd::Action
   def process
      Thin::Logging.log("HelloWorld :: -- criando arquivo #{input}!")
      `touch #{input}`
      0
   end
end

Você passa um nome qualquer, e o script cria um arquivo em branco. Simples !?, Mas serviu bem para meus primeiros testes.

Também aproveitei e usei o log do Thin que é o servidor que o Cloud Crowd levanta, para registrar o evento de dentro da action… Isso significa que as mensagens de log da minha action foram registradas no arquivo de log do node que a executou… Se preferir você pode utilizar o Logger do Rails. Como foi apenas um experimento, utilizei o do Thin mesmo.

Cadastrando tarefas:

Para começar a utilizar sua action,  você precisa postar um JSON com as informações necessárias no server, para cadastrar uma tarefa. (Não preciso lembrar que o server e algum node deve estar rodando, certo ?)

Para isso, crie um script ruby, ou rode pelo irb os comandos*:

require 'rubygems'
require 'restclient'
require 'json'
RestClient.post('http://localhost:9173/jobs',{:job => { 'action' => 'hello_world' , 'inputs' => ['/home/rafael/arquivo_teste1', '/home/rafael/arquivo_teste2']}.to_json})

Considerando que o server está rodando na máquina que rodou o script. Caso contrário, altere o localhost pelo ip do Server. E os arquivos são criados (pelo comando “touch”) na máquina que roda o Node. Para fins de teste e desenvolvimento, é possível rodar o server e o node na mesma máquina, já que eles sobem em portas diferentes…

Então é isso… Você já pode paralelizar qualquer tarefa. Basta escrever suas próprias actions… 😉

 

Share

Ruby: Alternativas para require ‘rubygems’

ruby-logo-justruby

Baseado no comentário de Guilherme Chapiewski encontrado em https://rafaelbiriba.com/2009/04/13/ruby-require-rubygems.html, resolvi postar alternativas para suprir essa necessidade do require ‘rubygems’ e porque é errado utilizá-lo !

Primeiramente, vamos para a pergunta que originou este post:
O que há de errado em utilizar require ‘rubygems’ no meu script ?

Bom, analisando o exemplo que eu dei no post do require rubygems, transcrito logo abaixo, onde se você quiser utilizar o pacote “hpricot”, instalado pelo comando gem install hpricot, o código não irá rodar, retornando um erro referente ao rubygems. Usando o require ‘rubygems’ o problema é resolvido, mas não está correto. O motivo é bem simples. Primeiro vamos ver o código em questão:

require 'hpricot' 
require 'open-uri' 
meuXml = Hpricot.XML(open("http://www.exemplo.com/exemplo.xml"))

Concordam comigo que o código acima, é uma simples aplicação em ruby ? O que eu quero dizer é que você pode instalar o hpricot de várias maneiras diferentes ! Por exemplo, acessando o link: http://wiki.github.com/why/hpricot/installing-hpricot, você pode observar que o Hpricot pode ser instalado sem o Rubygems. Sendo assim, ao usar o require “rubygems” em um máquina que não tem o rubygems instalado, vai dar problema na execução !

Então o que fazer se eu instalei o Hpricot pelo rubygems ? Simples…

Você pode facilmente rodar o seu script incluindo -rubygems na linha de comando, conforme o exemplo abaixo:

ruby -rubygems meu_script.rb

Isso fará rodar o pacote rubygems antes de executar sua aplicação meu_script.rb

Fonte: http://gist.github.com/54177

Bom, então é isso.

Share

Ruby: require ‘rubygems’

ruby-logo-justruby

Essa semana estive escrevendo alguns códigos usando Ruby on Rails, e encontrei um problema, que demorei um pouco para descobrir sua solução, mas no fim se tratava de algo muito simples e lógico.

Ao instalar o Rails e qualquer outro pacote pelo rubygems ( gem install hpricot, por exemplo), para importar esse pacote no código, é necessário importar a biblioteca do rubygems primeiro.

Manual do RubyGems ( http://docs.rubygems.org/read/book/1 ).

Na prática, eu estava utilizando o código abaixo:

require 'hpricot' 
require 'open-uri' 
meuXml = Hpricot.XML(open("http://www.exemplo.com/exemplo.xml"))

Este codigo abre uma URL, captura seu XML e armazena o conteúdo em uma variável ( string ).

Porém, isso só é possível se incluir a biblioteca do rubygems. Verifique o código abaixo, desta vez, funcionando:

require 'rubygems'
require 'hpricot'
require 'open-uri' 
meuXml = Hpricot.XML(open("http://www.exemplo.com/exemplo.xml"))

 

Bom, então é isso !
—————–
Baseado no comentário recebido depois da publicação deste Post, visite também:
https://rafaelbiriba.com/2009/04/29/ruby-require-rubygems-alternativas.html

Share

Ruby: Aprenda a programar

ruby-logo-justruby

Nas últimas semanas estive empenhado para aprender Ruby on Rails. Procurei aqui e ali na internet, juntei conteúdo de todas as partes e blogs, para aprender cada vez mais.

Para quem não sabe, Ruby on Rails é um framework para desenvolvimento Web, bem mais fácil de trabalhar comparando com outras linguagens Web.

Como diz no site do Rails, “Mostre, não fale: Ver é acreditar !”, visite o link abaixo e veja alguns exemplos de Rails na prática:
http://www.rubyonrails.pro.br/apresentacoes

Depois de vários dias, descobri um site ótimo para quem quer aprender a programar: ( Pena não ter encontrado ele antes )
http://aprendaaprogramar.rubyonrails.pro.br/

O site apresenta capitulos divididos em 11 links. A didática é ótima, e diz o autor que os exemplos encontrados no site são realmente “executados” , ou seja, o resultado do exemplo que você vê no site, realmente é executado. Isso é apenas um dos benefícios de usar o Ruby on Rails.

Eu recomendo tanto o site, quanto a linguagem para todos os programadores/desenvolvedores. Inclusive eu recomendo também para os futuros programadores. Quem tem pouco conhecimento na área, vai facilmente aprender a lógica de programação e a linguagem de maneira simplificada.

Em falar nisso, eu achei extremamente fácil de aprender e entender. Pra quem quer começar, visite o site abaixo para baixar a última distribuição do ruby:
http://www.ruby-lang.org/pt/downloads/

Ou então visite direto o site do Ruby on Rails, utilizem seus links para o download tanto do ruby quanto do rails*:
http://www.rubyonrails.pro.br/down
Neste mesmo link ainda exemplos de como criar sua primeira aplicação em Rails.
* Instalação do Rails e muitos outros pacotes, serão efetuadas pelo gerenciador RubyGems.

Bom, espero que se divirtam… =)

Share