Quando o FakeWeb deu um grande susto pt 2: o changelog

Como falei anteriormente, o FakeWeb nos deu susto.

Olhando no CHANGELOG na versão que estavamos acostumados a usar o correto era fazer :string => “mensagem que veio no ‘body’” ,entretanto nas versões mais atuais isso foi substituido por :body => ‘tcharam’ … e é este exemplo que esta no site. Como isso quebra o backward compatibility, foi documentado no changelog mas passou despercebido. É claro que a versão mais antiga não vai funcionar que nem a nova.

Se tentassem executar os testes antigos no fakeweb 1.2.4 receberia
esta exception:

Deprecation warning: FakeWeb’s :string option has been renamed to :body.
Just replace :string with :body in your FakeWeb.register_uri calls.

Este é um exemplo onde mudar a forma como utilizamos uma biblioteca ou qualquer software causam todo o tipo de problemas quando não prestamos atenção a todos os detalhes.

Comportamento estranho do FakeWeb versões antigas

Ontem fomos surpreendidos por este comportamento estranho do FakeWeb (versões 1.2.2 e 1.2.3), de uma hora para a outra o corpo das requisições vinham como “” (vazio).

irb(main):001:0> require 'rubygems'
=> true
irb(main):002:0> require 'fakeweb'
=> true
irb(main):003:0> FakeWeb.register_uri(:get, 'http://google.com', :body
=> 'google')
=> [#<FakeWeb::Responder:0xb78290a8 @uri="http://google.com",
@options={:body=>"google"}, @method=:get, @times=1>]
irb(main):004:0> HTTParty.get('http://google.com')
=> nil
irb(main):005:0> x = HTTParty.get('http://google.com')
=> nil
irb(main):006:0> x.code
=> 200
irb(main):007:0> x.body  # deveria vir 'google'
=> ""
irb(main):008:0> Net::HTTP.get(URI.parse("http://google.com"))
=> ""  # mesma coisa... :/

Atualizando para versões mais recentes (como a 1.2.4) o problema acaba. Alguem mais passou por isso?

O FakeWeb é uma excelente ferramenta para testes reais onde dependemos da resposta de outros servidores. Ao inves de estabelecer uma comunicação real, que pode trazer outros tipos de problema de dificil detecção, simulamos a resposta para ver se o sistema se comporta de acordo com o esperado.

Sabe quando vc tem que testar se de 1, N, 0 elementos ou 404, essas coisas, e nem sempre vc pode forçar estas situações por ser complicado demais? Nesse ponto o FakeWeb é A ferramenta certa.

Não entendo o motivo pelo qual ainda ensinam pascal

Encontrei este exercicio em um forum de programação e informatica. Um tanto engenhoso, devo admitir, e é otimo para praticar diferentes linguagens de programação, que podem exercitar muitos paradigmas. Infelizmente o professor pediu para fazer em Pascal.

o número 3025 possui a seguinte caracteristica: 30+25=55 e 55*55=3025. Escreva um programa que escreva todas os números com quatro algarismos que possuem a citada características.

Veja que solução elegante temos com ruby:

>> (1000..9999).find_all{|x| (x/100+x%100)**2 == x}
=> [2025, 3025, 9801]

Entretanto tive a apelar para uma sujeira (alguem percebe?) na versão em haskell:

Prelude> import List
Prelude List> findIndices(\x -> ((x `div` 100)+(x `mod` 100))^2 == x && x > 1000)[0..9999]
[2025,3025,9801]

Por fim, um simples ‘one liner’ usando gawk

$ seq 1000 9900 | gawk '(int($1/100) + $1%100) == sqrt($1){print}'
2025
3025
9801

É claro que a solução esperada é algo como

#include <math.h>
#include  <stdio.h>
int main(){
	int i;
 
	for(i=1000;i<=9999;i++){
		if(((i/100)+(i%100))==sqrt(i))
			printf("%d\n",i);
	}	
 
	return 0;
}

Até ANSI C é mais elegante – sem flamewars por favor, afinal todos sabem o que são programadores de verdade :)

Sempre defina a forma de abertura de arquivos

Linguagens script tornam tudo muito facil. Codificar uma informação para base64, por exemplo, pode ser feito assim:

   require 'base64'
   data = "Now is the time for all good coders\nto learn Ruby"
   Base64.b64encode(data)

Entretanto se vc quer processar o conteudo de um arquivo, vale um cuidado extra: definir a forma de abertura dos arquivos.

Por exemplo, eu posso fazer isso

   require 'base64'
   data = File.open('imagem.jpg').read
   Base64.b64encode(data)

Se eu estou desenvolvendo no linux, para mim o resultado é coerente. Eu ficaria feliz (sem sacanagem) se fosse o caso de desenvolver um software que rodasse exclusivamente nesse sistema operacional. As vezes não é bem assim…

Ruby é um bom exemplo de uma linguagem que permite desenvolver software multi-plataforma, entretanto é muito facil atrapalhar o interpretador (afinal, quem nunca teve problema com uma referência a um arquivo “C:\xxx”?). No exemplo real que eu passei, bastaria informar o modo ‘rb’ (r de read, b de binary) para o comando open que eu teria o mesmo comportamento em todas as plataformas.

   require 'base64'
   data = File.open('imagem.jpg','rb').read
   Base64.b64encode(data)

É claro que este tipo de comportamento pode ser verificado através de uma suite de testes em todas as plataformas-alvo, entretanto vale de alerta para estudarmos um pouco as caracteristicas das apis e bibliotecas nas diferentes plataformas (não adianta, desenvolver software signfica estudar constantemente).

Para terminar, vejamos esta thread:

Summary: open should default to binary mode on windows

Initial Comment:
On windows the open() function defaults to reading
files in text mode. To get a binary mode file I need
to append a “b” to the mode string. I think this is an
unnessary platform inconsistency. Twice now I’ve had
hard to track down bugs because I was reading a file in
text mode and should have been using binary. This is a
wart, IMO.

Ou seja, muita gente, em diferentes linguagens, vai enfrentar o mesmo problema de achar que o open, pode padrão, vai abrir o arquivo em modo binario. A origem disso é antiga e não vai desaparecer de uma hora para outra.

Por fim, vale lembrar que o conceito de ‘nova linha’ é diferente de acordo com o sistema operacional, no unix é \n enquanto no windows é \r\n. Também é antigo e remonta a epoca de impressoras matriciais, DOS e por ai vai. É o tipo da coisa que, pela lei de murphy, vai sacanear a gente quando menos esperamos.

Obrigado ao Rafael por ter visto este detalhe comigo tarde da noite.

Python é orientado à objetos. E bem orientado!

Quem sabe alguma coisa de Ruby ja se deparou com algumas caracteristicas da linguagens, como a não existencia de tipos primitovos: tudo é objeto, incluindo o numero 1.

Quando eu faço, em Ruby, algo como

i = 1 + 2

estou fazendo, na verdade,

i = 1.+( 2 )

Pois o 1 é um objeto da classe FixNum, que responde ao método + (simbolo de adição).

E no Python, sera que isso funciona? Na verdade a coisa é ligeiramente diferente!

i = 1 + 2

é equivalente à

i = (1).__add__(2)

É claro que vão falar: puxa, eu tenho que colocar os parentesis ao redor do 1 para invocar um método (que não tem o mesmo nome do operador + e sim algo bizarro como __XPTO__). Isso se deve à como o interpretador funciona, que vê o primeiro ‘.’ apos um numero como o delimitador da parte inteira da parte não-inteira.

É possivel, então, fazer a mesma coisa que eu faço com Ruby de duas formas:

i = int(1).__add__(2) # ou 
i = 1.0.__add__(2)

Percebam o 1.0.metodo tinhoso ali: eu estou invocando um método do objeto 1.0 – da mesma forma que o Ruby ou outras linguagens totalmente OO – apenas a sintaxe difere um pouco.

RPG em Ruby com apenas 15 linhas

Achei este link bem interessante: MUD in 15 Lines of Ruby. Para quem não sabe MUD é uma familia de jogos textuais inspirados em RPGs conhecidos: normalmente vc escolhe uma raça e uma classe e, dentro do jogo, evolui através da morte de criaturas controladas pelo jogo e através de missões varias como recuperar itens ou decifrar enigmas, são os avós dos MMORPGs modernos.

O codigo foi um tanto obsfuscado, na verdade são umas 77 linhas, mas não deixa de ser interessante o poder das linguagens scripts :)