Cada um merece o ópio que tem
Café, rotina, computadores, televisão, esmaltes, redes sociais, grifes, gadgets, futebol, big mac, piriguetes, música, dinheiro, conforto, chicotes, teatro, informação, fotografia, cigarro, amor, rock’n'roll, mainstream, underground, cerveja, espelhos, solidão, efusão, café da manhã na cama, tazos, anéis de latas de alumínio, memes, gatos, pornografia, rua augusta, igreja universal, micareta, festival, instant messenger, sms, 21 centavos fale-o-quanto-quiser, onanismo, sexo verbal, sexo a dois, sexo com mais de dois, trollar, kibar, repercutir, sorrir com 100 RTs, livros, bolachões, 2 marcas de refrigerante de cola, i-qualquer-coisa, sofativismo, croutons, encaminhar e-mails pedindo doações, apresentações de slides que atingem o âmago da alma, apresentações de slides que sequer tocam a superfície, esnobar, abraçar forte sem conhecer direito, deixar pra conhecer direito depois, mmorpg, vomitar sozinho, vomitar com os amigos, vomitar longe de quem acabou de conhecer, som alto, nem ligar, rir, registrar, criticar, opinar, reclamar, lembrar, regozijar, muitas calorias, deixa pra segunda, gozar uma, gozar duas, gozar onde não pode, gozar com quem não pode, errar, se arrepender, demonstrar, afeto, disposição, chocolate, bom dia, durma bem, adorar a sexta-feira, adorar a segunda-feira, por que não?, ídolos, anti-heróis, conspirações, métricas, prazos, deadlines, traço fino, traço forte, amém, vai se foder, vintage, politicagem, confusão, prazer, dor, manhãs, madrugadas, cinema em casa, zippos, graves, ruivas, bebês, lingeries, funk alto no ônibus, funk no fone de ouvido alheio e só, céu azul, céu cinza, céu rosa, apartamento grande, distância pequena, euforia, estados mentais alterados, metrô vazio, ceia cheia, nostalgia, imagens bizarras, imagens alegres, a mãe, o pai, os avós, os irmãos, os primos, o primeiro beijo da vizinha, o primeiro beijo da colega de escola na 2ª série, a primeira mão boba, o primeiro negativo de muitos resultados de exames de sangue, o primeiro negativo revelado, a última pedra e o primeiro perdão.
Cada um merece o ópio que tem, o vício que agradavelmente o consome, que o faz ser único e que dá o direito de ser distante de quaisquer tendências, inclusive as deste texto.
Eu acho que consegui juntar um pouco do que eu reparei que as pessoas que eu conheço gostam e de certa forma pode ser uma homenagem a quem está na minha vida, que já passou ou que um dia irá embora da minha vida. Um passo no leito do rio, certo ou errado, tropeço ou sorte, tem poder de mudar o curso dele para sempre.
Facebook Deorkutizator
Admito, pra mim, o Facebook da forma como está hoje é incômodo. Em tom de brincadeira, “Orkutizado” com tantas imagens compartilhadas que antes só apareciam no Orkut e em e-mails dos anos 90. Não que eu não goste, mas acho que lá não é o lugar pra isso. O pior é que o Facebook oferece um botão “bloquear” pra quase tudo nele, exceto as Pages. Quer dizer, se não quer ver o conteúdo de uma Page na sua timeline, ou bloqueia quem fica compartilhando ou oculta manualmente.
Pensando nisso, tive a idéia de criar um userscript – mais um userscript em uns 3 ou 4 anos brincando com JavaScript no Greasemonkey – que esconde as Pages que não me agradam da minha lista de atualizações do Facebook, sem ter que bloquear ninguém ou ficar ocultando uma por uma. O resultado é o Deorkutizador de Facebook e pode ser baixado do meu repositório através daqui:
As instruções de instalação estão no arquivo. Já adianto que é necessário que a extensão Greasemonkey esteja instalada no Firefox. Também coloquei as instruções para a instalação no Chrome, mas ainda não testei nele.
Copiei o código Me baseei neste artigo chamado How to play nicely with jQuery and Greasemonkey que ensina a como usar o jQuery dentro do Greasemonkey. Considero essencial porque o modo como o jQuery faz para acessar os elementos em uma página tornou a tarefa de encontrar aqueles que contém as atualizações incômodas e removê-los muito fácil. O “Deorkutizador de Facebook” possui uma lista de páginas bloqueadas que é atualizada automaticamente conforme eu encontro as páginas. Essa lista está disponível na minha conta do Dropbox e é carregada sempre que o Facebook for aberto e o script estiver ativado.
http://dl.dropbox.com/s/pn104hbhmqs77fu/paginasBloqueadas.js?dl=1
Para garantir transparência, a mesma lista está sincronizada com o GitHub
https://github.com/MendelGusmao/Deorkutizador-de-Facebook/blob/master/paginasBloqueadas.js
Quem for usuário do GitHub poderá fazer pull requests na lista, sugerindo novas páginas a serem bloqueadas e também “forkar” o script e sugerir modificações.
Essa primeira versão só esconde os históricos, então toda vez que o Facebook for aberto, as publicações estarão lá e em instantes serão ocultas. Estou trabalhando em um jeito de fazer o script “clicar” no botão “Ocultar histórico” automaticamente, assim, na próxima vez o histórico incômodo não estará mais lá de verdade. Ainda há alguns bugs, em especial um que eu apelidei de “só pega no tranco”, porque ele carrega os scripts mas não executa a limpeza, então a página tem que ser recarregada para ele funcionar.
O script é seguro, não incomoda ninguém, não acessa seus dados (muito menos sua senha), não interfere com a sua timeline nem com a de seus amigos e também não faz propaganda no seu mural.
Tchau
Por uma web mais rápida: compressão radical de JavaScript – parte 2
No post anterior, descrevi o cenário em que um código em JS deveria ser entregue com um nível de compressão excepcional, visto que ele seria bastante requisitado. Expliquei um pouco do funcionamento do Packer para JavaScript e dei algumas dicas para otimizar o uso dele de forma que não pese no lado do cliente.
Neste ponto, um código de 36 KB (XRegExp + parte da aplicação) já está razoávelmente comprimido, mas não o suficiente para um ambiente com muitas requests: advertising.
A solução encontrada foi compressão pelo servidor com GZip. Otimização, aliás, recomendada no post do Sérgio Lopes e não só por ele!
É uma solução muito eficaz para entrega de conteúdo estático. Se utilizada com cache, então, fica lindo. Mas essa opção não se adequava ao caso, já que o JS era “eventualmente estático” e qualquer alteração das regras impactava no conteúdo do JS, seja modificando o tamanho para mais ou menos, mas, sempre modificando o arquivo. Daí a necessidade de não cachear esses arquivos e garantir que toda alteração feita pelo usuário da plataforma seria imediatamente refletida no script.
Então foi decidido: compressão máxima pelo servidor onde os arquivos JS (um para cada site, cada cliente com vários sites) seriam hospedados. Packer + Gzip nível 9 – compressão de 36 para menos de 4 KB. Incrível! (~4 KB * clientes * sites * milhões) já parece ser um número bem melhor!
Nesse ponto, fiquei incomodado – mesmo sem ter feito qualquer benchmark – sobre como o servidor responderia tendo que efetuar tantas compressões on-the-fly, sem caching e acabei chegando a uma solução muito, mas muito simples e eficaz:
Para conteúdos estáticos e muito requisitados, não deixe o servidor comprimir durante a requisição. Entregue comprimido.
Sim, isso! Armazenar o arquivo já comprimido! O backend já era responsável por obter as regras, concatenar a XRegExp, as regras e o código que faz tudo funcionar; agora também é responsável por comprimir o JS com GZip. Gastar algum tempo de processamento comprimindo 36 KB (ou mais) em 4 KB? Nunca na requisição, só no momento em que uma regra for modificada ou em algum cron job na madrugada executado por garantia.
E o servidor?
A não ser que ele seja instruído a não fazer isso, e manter a compressão habilitada para tudo, ele vai comprimir o que já foi comprimido. Então, aqui está o maior truque disso tudo: configurar o servidor para caso uma request peça por um arquivo armazenado em determinado diretório, deixe a compressão e envie uma header Content-Encoding: gzip.
E aquele browser que não tem suporte a gzip?
Não pode entregar como gzip. Excepcionalmente, o backend também trata de gerar os scripts somente com a compressão do Packer. Nesse caso, usar cache é importante, porque uma parcela de usuários receberá um arquivo um pouco maior que os outros e é importante não deixar a performance do servidor ser impactada por isso.
Usamos o Lighttpd e foi muito fácil configurá-lo para agir assim. Para caching, o escolhido foi o Varnish, mas como não fui responsável pela configuração, não posso dar detalhes.
O conceito mais importante dessa parte do post não se aplica ao JS ou ao comportamento da aplicação, mas do servidor. A idéia de entregar conteúdo já comprimido ao invés de utizar compressão on-the-fly se aplica a qualquer tipo de conteúdo que não seja interessante, do ponto de vista da performance, de ser entregue através de métodos específicos de caching. E a conclusão é de que isso não é mais do que um método alternativo de caching.
Considerei a abordagem de usar caching mais forte, separando a XRegExp, com um tempo de cache maior, enquanto as regras e o código ficariam com um tempo menor de cache. Mas aí não seria tão “emocionante” e se por algum motivo a XRegExp não fosse carregada, a aplicação não funcionaria.
Concluído o projeto, me desliguei da empresa com uma proposta mais interessante, deixando uma documentação bastante forte sobre essas idéias. Preciso dar uma olhada nos sites dos clientes para ver se eles já estão utilizando tal aplicação!
Por uma web mais rápida: compressão radical de JavaScript – parte 1
Post inspirado pela palestra do Sérgio Lopes sobre otimização de sites, que foi apresentada na #QConSP. Gostaria de ter demonstrado esse caso no momento, como sugestão para todos da área, mas eu sou tímido.
Em uma empresa onde trabalhei no ano passado, tive que desenvolver um código JS que usa uma biblioteca adicional, XRegExp, para named capture em expressões regulares. Juntando essa biblioteca e meu código, o tamanho total ficava em cerca de 36 KB – no mínimo, já que o código era modificado por variáveis que o cliente da plataforma poderia configurar. A projeção de hits no servidor solicitando um JS era da ordem de milhões por mês, dado que é uma plataforma de advertising. Considerando que cada site deve incluir esse JS e um cliente tem vários sites: (+36 KB * clientes * sites * milhões): não resulta em um número muito bom.
* Detalhe importante: o JS não é totalmente estático. Caso o cliente da plataforma modificasse uma regra, ela teria que ser imediatamente aplicada ao JS, logo, não poderia ficar muito tempo em cache do usuário. O jeito encontrado foi servir sem cache e com o máximo possível de compressão.
Há 3 anos eu conheci o Packer, um compactador de JavaScript que é bastante utilizado por frameworks JS e módulos de pipelining em frameworks web – detalhe que na época eu o conheci através de um código malicioso e como eu queria saber o que o código estava fazendo, então tive que estudar a forma como ele era compactado e o que significava aquela function(p,a,c,k,e,r) no começo do arquivo. Ele tem muito valor e utilidade em ambientes onde você precisa fazer entrega de conteúdo com eficiência.
Ele não é tão simples como outros códigos que eliminam comentários, espaços, tabs, pontos-e-vírgulas duplicados, quebras de linha, deixando a estrutura básica da linguagem para que o código seja funcional. Ele faz bem mais que isso. Explico agora:
- O Packer parseia seu código JS, fazendo tudo o que eu descrevi ali em cima e também separa cada palavra-chave ou valor, montando um array* de strings.
- Cada palavra armazenada no array possui um índice numérico e esse índice é usado como referência no código que o Packer gera.
- O Packer cria um wrapper, que é a função anônima (p,a,c,k,e,r), responsável por descompactar o código gerado, buscar as referências no array e remontar o código, substituindo as referências pelos valores respectivos, terminando com um eval() para interpretar o código remontado.
- Como as referências são únicas e as strings usadas para descrevê-las são pequenas, muitas vezes usando números de base 62 – dependendo da configuração, o código gerado é bastante econômico e sucinto.
* Este array de strings é na verdade uma string só, com seus valores delimitados por pipes
Vou dar um exemplo bem simples de como o packer vai montar um o código compactado:
function stack_messages(strings) {
var i = 0;
for (i = 0; i < strings.length(); i++) {
var message = “Annoying message #” + i + “: ” + strings[i]“;
alert(message);
}
}
Esse é o “array” de palavras úteis que o Packer gerou após fazer o parsing:
‘|i|var|function|stack_messages|for|length|Annoying|message|alert||’
E gerar o seguinte código:
’3 4(a){2 1=0;5(1=0;1<a.6();1++){2 b=”7 8 #”+1+”: “+a[1]“;9(b)}}’
Não parece, mas esta é a função stack_messages. Ignorando chaves, pontos-e-vírgulas, parênteses e outros símbolos que obviamente não podem ser reduzidos, é possível entender que que: a referência 3 corresponde ao índice 3 no “array”, que quer dizer “function”. A referência de número 4 é “stack_messages”. Isso é só pra começar.
Note algo interessante: palavras que foram utilizadas várias vezes no código, como var, message e i só aparecem uma vez no array. Elas possuem as referências 2, 8 e 1, respectivamente.
’3 4(a){2 1=0;5(1=0;1<a.6();1++){2 b=”7 8 #”+1+”: “+a[1]“;9(b)}}’
Trocando de 1 a 7 caracteres por 1 só. Inteligente demais, não?
* Não entendi o porque da palavra message ter duas referências, 8 e b, mas assim que descobrir, atualizo o post.
Não vou me aprofundar nos detalhes do algoritmo de descompressão do Packer, mas por cima é possível notar que:
- È um algoritmo iterativo: o único código que roda imediatamente é ele e o código desenvolvido por você não vai rodar até que ele tenha iterado o array de palavras úteis e transformado o código traduzido em código válido para ser interpretado com eval(). A velocidade com a qual ele roda depende bastante da velocidade da máquina e do browser de seu cliente.
- Não é bom para códigos pequenos. O overhead causado pela função de descompressão não compensa o trabalho da compressão.
- Concatene todos os arquivos JS que sua página necessita através de um pipeliner antes de comprimir com o Packer. Isso faz com que o tamanho do array de palavras úteis seja menor e centralizado. No caso de frameworks e bibliotecas de terceiros, escolha as versões non-minified e non-packed. O ideal é não judiar do forçar o browser do seu cliente a executar o descompactador do Packer mais de uma vez, seja em cada arquivo, seja em “compressão em cima de compressão”, como um código packed passando novamente pelo Packer.
Nesse ponto, meu código (XRegExp + minha aplicação) já está “minified” com o Packer, mas ainda está pesado para o cenário de milhões de requests. Vou explicar na parte 2 como o servidor foi otimizado para servir arquivos com compressão.
Instalando Eclipse Helios/3.6 no Ubuntu 11.04
Não sou fã do Eclipse. Já trabalhei bastante com ele, faz uns 2 anos, mas prefiro programas mais simples, como o Notepad++ no Windows e no máximo NetBeans quando faço alguma coisa com o Rails. Faz parte da tentativa de deixar de ser um programador point’n'click e aprender a usar emacs/vi + svn/git na linha de comando.
Mas agora quero brincar um pouco com Android e achei que seguir esse pequeno curso do The Code Bakers seria um bom começo. E um dos requisitos, porém, é o uso do Eclipse. Justificável, dada a facilidade do emulador e a minha preguiça de procurar métodos alternativos enquanto estou brincando.
Parece que a última versão do Eclipse disponível no repositório oficial do Ubuntu é a Galileo/3.5. Talvez porque a 3.6 não seja estável nas versões mais recentes do sistema operacional. Fato é que eu não gosto de usar software desatualizado, salvo sobre meu Windows – XP – e um driver da Nvidia cuja última versão fazia o sistema dar memory dumps nas tarefas mais simples.
Alguns sites aconselham a alterar os endereços de repositórios do Eclipse mas não funcionou comigo. A solução foi usar o PPA da Eclipse Team que felizmente voltou a ser atualizado. Mão na massa:
sudo apt-key adv –recv-keys –keyserver keyserver.ubuntu.com 5126890CDCC7AFE0
sudo add-apt-repository ppa:eclipse-team/debian-package
sudo apt-get install libswt-gtk-3.6-java eclipse
Adicionei libswt-gtk-3.6-java à instalação porque o Eclipse não abriu sem essa dependência e o arquivo ~/workspace/.metadata/.log exibia um erro mencionando-a.
Enjoy your new Eclipse!
* Observação: descobri que o Android Development Tools é incompatível com o Eclipse 3.6. Se sua pretensão é usar o ADT, continue com o Eclipse 3.5.
Primeiras impressões sobre a Linha Amarela do Metrô
Nas últimas duas semanas tive 3 oportunidades de usar a mais nova linha do metrô paulistano: a Amarela, de número 4. É uma linha com instalações pomposas, trens modernos, com passagens entre os vagões e operando sem condutor – na composição, já que tudo é feito remotamente.
Fico feliz por ter mais uma opção na minha viagem cruzando São Paulo no meu transporte de Guarulhos até a Berrini, onde trabalho, além da ponte ORCA da Vila Madalena/Cidade Universitária e dos trens da Barra Funda/Altino. Da primeira vez foi uma decisão de última hora: na segunda-feira da inauguração da estação Pinheiros, indo da Paraíso para a Vila Madalena, decidi descer na Consolação e logo vi não foi uma boa idéia: era dia de inauguração e na saída principal estavam manifestantes, jornalistas, empresários e políticos reunidos na porta. Uma muvuca só.
Detalhe importante: as estações são bem fundas. Na Consolação, por exemplo, existem duas esteiras e duas escadas rolantes até a plataforma de embarque. Em Pinheiros são 4 lances de escadas rolantes até a saída principal. Para usar a integração com a estação Pinheiros da CPTM, há mais uma escada rolante até a passarela. Prato cheio para quem curte andar de metrô e no metrô.
A integração entre Pinheiros da CPTM e Pinheiros do Metrô só começa no mês que vem, já que a passarela que liga elas não está pronta, assim, é necessário andar cerca de 400 metros entre as estações, passando pelo entorno do canteiro de obras do que será um terminal de ônibus em 2012 e pagar novamente 2,90 ao passar pela catraca.
É nesse ponto que eu vejo problemas de planejamento. Ainda que a necessidade de pagar nova tarifa seja temporária, eu acho que é ilegal. O governo bem que poderia implantar um esquema similar ao utilizado na ponte ORCA, em que você adquire gratuitamente um cupom que garante que ao chegar na outra estação – em uma van ou microônibus ORCA – você não terá que pagar novamente a tarifa. Oras, era só colocar as máquinas que emitem os cupons entre as estações. A diferença é que não é preciso pegar uma van. Custo irrisório para o governo e um consórcio que entregaram uma obra importante com 10 anos de atraso e claro, garantia do bem estar do cidadão.
Outro ponto que me incomoda é que os novos trens e as plataformas de embarque das estações da linha amarela são minúsculos. Menor do que estas plataformas, só a plataforma da estação Paraíso sentido Vila Prudente. Não sou um especialista, mas acredito que aquilo não tenha sido bem planejado, com números e projeções reais do volume de pessoas que vão utilizar a linha. A falta de espaço faz parecer que a linha só será usada pela “elite” que reside em Pinheiros, Morumbi e Jardins. Soa inapropriado, já que é fato que São Paulo necessita urgentemente de mais linhas de metrô e as novas estações já são inauguradas com uma quantidade considerável de usuários. Lotadas.
Bônus: mapa do metrô de São Paulo para ser visualizado no Google Earth. Linhas atuais do metrô e da CPTM, linhas em projeto e até algumas linhas imaginárias que nossos filhos e netos poderão usar muito bem, se o Governo do Estado de São Paulo trabalhar com um pouco mais de boa vontade: http://migre.me/4Dp34
Mais uma saga do TI doméstico
Meu notebook, quando não está sendo utilizado pela minha noiva, é meu servidor pessoal. Costumo rodar nele alguns robôs de twitter, scripts de provas de conceito de invasão algumas aplicações. Para monitorá-lo ou algo mais, uso o UltraVNC. Cheguei a testar o LogMeIn e até a rodar um servidor de SSH sob Cygwin, mas ambos deixam a máquina um pouco mais lenta (é um Eee 900).
Acontece que ontem eu o utilizei para ler alguns artigos na cama, a mando da preguiça de ligar o desktop e eu lembro de, em determinado momento, vários programas terem dado crash, inclusive o servidor do UltraVNC. Não me lembrei disso quando saquei o celular para ver como ele estava, usando o VNC+ para Symbian e estranhei ele não conectar. Por um breve momento pensei que o notebook tinha travado e bateu um desespero, já que a refrigeração dele está péssima – culpa dos 2 anos de idade dele. Conectei no SSH do meu NAS e pinguei o notebook. Descobri que ele estava online e lembrei do crash do VNC.
Fiquei aliviado, mas mesmo assim, eu precisava ter acesso ao VNC. Comecei a pensar em como reativar o VNC sem ter que ligar para minha irmã e pedir para ela reiniciar o notebook. Até porque eu fui dormir as 3:30 da manhã e não lembro se deixei alguma pornografia janela com minhas finanças ou documentos confidenciais aberta.
Alguns bots estão no “Agendador de Tarefas” aka “cron fail do Windows”. Todos eles são escritos em PHP. Todos os meus drives são compartilhados. PLIN, idéia: vou acessar os compartilhamentos, editar um dos robôs, pedir para ele executar o UltraVNC através de shell_exec() e voilá, tudo volta ao normal. Antes disso, para acessar os compartilhamentos, eu teria que mandar o roteador abrir as portas 139 e 445, utilizadas pelo servidor SMB do Windows. Lá vou eu, feliz e sorridente abrir o Opera Mobile para acessar o painel do roteador e mandar a NAT direcionar o tráfego das tais portas para o IP do notebook.
Não deu certo. Esse roteador, cujo código parece ter sido feito por alguém com 6 meses de experiência programando (em 2001) tem um grave problema de pirar na batatinha com NAT e Virtual Servers. Várias portas comuns (ftp 21, ssh 22, telnet 23, http 80, https 443) são utilizadas por ele para os painéis de controle. Acontece que eu uso várias dessas portas para meus dispositivos de rede e atribuo portas diferentes para acessar o roteador. Mas ele não entende. Ele pira e mistura as portas. Pira e mistura muito mais se a origem da requisição for LAN (nesse caso, estou tentando a WAN). A porta 80 deveria ser utilizada para acessar meu NAS, volta-e-meia caio no painel do roteador. A porta 22 deveria ser utilizada para acessar o SSH do NAS. Cai no SSH do roteador. É absurdo.
Então, por causa dessa confusão, não consegui acessar o painel web do roteador. Um lindo connection refused na minha cara; me impedindo de colocar a porta do SMB na lista de Virtual Servers do NAT. Não desisti. Ativei o modo inception do ghetto-sysadmin, loguei no SSH do NAS e dentro dele, loguei no SSH do roteador – isso porque a piração do dito cujo me impede de conectar diretamente no SSH dele via WAN. No help encontrei o comando virtualserver e logo digitei “virtualserver add”. A resposta não foi agradável – ele só tem 2 parâmetros: show e disable. Quer dizer, não há como abrir portas via SSH. Mais um absurdo.
Minha última tentativa, ainda na combinação de Putty e Opera no celular: fucei os arquivos do firmware,procurando algum .conf com a lista de Virtual Servers, mas não encontrei nada.
Quando consegui um desktop, abri o Dropbox, jurando que havia um link editar na interface web – na empresa não posso usar Dropbox. Mais um fail. Acabei por me logar no LockMeTight – programa para rastrear o notebook, caso ele seja roubado – e vi que estava tudo bem. Pena que ele só atualiza de 30 em 30 minutos.
Da próxima, eu ligo para minha irmã e solicito um dedoff.