Opa! / 47 posts / 9 comentários / feed / Feed dos Comentários
Rails Summit Latin America

Fonte para Linux

Para quem está procura de uma boa fonte para Linux pode dar uma experimentada na Liberation, é uma fonte que fica muito bem com um tamanho pequeno.

Para instalar no Arch Linux

pacman -S ttf-liberation

Adicionando e removendo um usuário de um grupo no Linux

Hoje precisei definir uns grupos para um usuário e achei os comandos abaixo

Para adicionar um usuário em um grupo:

useradd -G nome_do_grupo nome_do_usuario

Você pode passar vários grupos também, basta separar a lista por vírgula:

usermod -G nome_do_grupo,nome_de_outro_grupo nome_do_usuario

Para remover um usuário de um grupo:

usermod -G nome_do_grupo nome_do_usuario

Note que a sintaxe para remover é a mesma para adicionar, porém a lista de grupos terá apenas os grupos em que você deseja manter o usuário.

Outra forma de remover todos os grupos secundários (adicionados com os comandos acima) é usando '' (vazio)

usermod -G '' nome_do_usuario

Mais detalhes aqui e aqui

Apache ouvindo em duas (ou mais) portas diferentes

É muito simples, basta colocar quantas diretivas Listen você precisar

Listen 80
Listen 81
Listen 82

Retirado daqui

Eu estou usando isso para poder diferenciar quando estou rodando a aplicação via Selenium ou não.

PHP, PDO e ... Sei lá o que!

Eu não ainda não consegui achar explicação para o código

Gerar a saída

C:\01projetos\teste>php pdo_fetchmode.php
setting: id with 18
setting: client_id with 48
setting: is_active with 1
setting: name with Site v1
setting: proposal with 1626
setting: description with
setting: created_at with 0000-00-00 00:00:00
setting: updated_at with
setting: created_by with
calling constructor
setting: id with 17
setting: client_id with 13
setting: is_active with 1
setting: name with Supra v1
setting: proposal with 1593
setting: description with
setting: created_at with 0000-00-00 00:00:00
setting: updated_at with
setting: created_by with
calling constructor
setting: id with 19
setting: client_id with 56
setting: is_active with 1
setting: name with Atualizacao
setting: proposal with
setting: description with
setting: created_at with 0000-00-00 00:00:00
setting: updated_at with 2008-09-15 10:02:34
setting: created_by with
calling constructor

Tenho o costume de sempre retornar objetos em resultados de banco de dados, acho mais fácil de trabalhar do que com arrays.

Ontem eu estava implementando algumas funcionalidades no ORM que fizemos aqui e me deparei com esse problema, eu precisava usar nos métodos __get e __set uma variável setada no construtor, mas começou a gerar um outro erro, causado pelo fato de que a variável não estava setada.

Procurei na documentação do PHP e só achei esse comentário, onde no final diz

Note also that the constructor is called AFTER the data is set on the object.

AFTER?! Isso vai contra toda e qualquer lógica de OOP que eu conheça!! Como que ele cria um objeto, seta todos os atributos nele e só chama o construtor depois de tudo?

Se alguém puder me explicar o motivo, razão ou circunstância, por favor, sinta-se mais do que a vontade.

Problemas com o blog

Esse final de semana tive uma batalha, batalha contra os spammers. De sexta (05/09) até hoje (07/09) eu estava tentando encontrar uma forma de bloqueá-los, acho que consegui.

Primeiro, um trecho do access_log do apache:

61.139.105.163 - - [06/Sep/2008:15:44:16 -0400] "GET http://www.esavingsnet.com/adscript_contextual.php?addcode=CD2049&bannerid=3377&optionalinfo=&deploy_id=0&landing_id=0 HTTP/1.1" 302 - "http%3A%2F%2Fwww.popflashgames.com%2Findex.html" "Mozilla/4.75 [en] (Win98; U)"
60.168.227.97 - - [06/Sep/2008:15:44:16 -0400] "GET http://ad.yieldmanager.com/pixel?id=79470&t=2 HTTP/1.0" 302 - "http://ad.103092804.com/st?ad_type=iframe&ad_size=728x90&section=334436" "Mozilla/4.0 (compatible; MSIE 5.0; AOL 5.0; Windows 98; DigExt)"
221.224.78.254 - - [06/Sep/2008:15:44:16 -0400] "GET http://afe.specificclick.net/?l=1212015023&sz=300x250&wr=j&t=j&u=http%3A//www.autoefax.com/best_deals/bestdeals/index.php&r= HTTP/1.0" 200 4736 "http://www.autoefax.com/best_deals/bestdeals/index.php" "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727)"
72.52.146.79 - - [06/Sep/2008:15:44:16 -0400] "GET http://ad.yieldmanager.com/imp?Z=0x0&y=29&s=289946&_salt=3097474688&B=2&u=http%3A%2F%2Fwww.vafq.com%2Findex.html HTTP/1.1" 200 6663 "http%3A%2F%2Fwww.vafq.com%2Findex.html" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 2.0.40"
72.52.146.79 - - [06/Sep/2008:15:44:16 -0400] "GET http://ad.adserverplus.com/rw?title=&qs=iframe3%3Fks9PAJpsBABbCBIA%2Eo0FAAIAAAAAAP8AAAAHEAICAAMsnQUActkEAKJNCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgzmzM2z8AAAAAAAAAAAAA4CUp2%2DM%2EAAAAAAAAAAAAAJCf94vwPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtAncswPCCwWKJq27l%2Dt%2EpvPxUBCN1rTpsKxzOgAAAAA%3D%2C%2Chttp%3A%2F%2Fwww%2Evafq%2Ecom%2Findex%2Ehtml HTTP/1.1" 200 542 "http%3A%2F%2Fwww.vafq.com%2Findex.html" "Mozilla/4.0 (compatible; MSIE 5.5; Windows 98)"
61.139.105.166 - - [06/Sep/2008:15:44:16 -0400] "GET http://banner.addlvr.com/cpi.jsp?&pvr=84&p=112839&aid=29606&partnerMin=0&ron=on&ronMin=0&cpviw=160&cpvih=600&url=http%3A//www.freeaddictinggame.net/&context= HTTP/1.0" 302 - "http://www.freeaddictinggame.net/" "Mozilla/4.0 (compatible; MSIE 4.01; Windows 98)"

Como minha VPS tem apenas 160MB de RAM, tenho que ficar controlando o consumo de memória, e sexta veriquei que o blog estava muito lento, quase inacessível. Parei o apache, instalei o nginx e o consumo baixou.

Mas o negócio não estava 100% ainda, fui ver os logs e descobri que ele tinha todas aquelas requisições partindo do meu server, então, hoje (07/09), depois de muito pensar para tentar entender o que estava acontecendo e solucionar o problema, fiz um script para testar se era possível usar meu servidor para fazer essas requisições, e descobri que é possível :(

Qualquer pessoa pode abrir uma conexão com um servidor na porta 80 (usando socket por exemplo), e fazer uma requisição assim:

GET http://www.terra.com.br/capa/ HTTP/1.1

Fácil assim, sem dificuldade nenhuma, como é possível ver no log do apache ali em cima, todas as requisições retornam status 302, funcionando normalmente, porém quando troquei para o nginx, a coisa mudou, ficando assim:

221.224.78.234 - - [07/Sep/2008:18:48:53 -0400] 221.224.78.234 "GET http://221.224.78.234:51317/was-pv.dll HTTP/1.0" 502  529 "-" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)" "-"
221.224.78.234 - - [07/Sep/2008:18:48:53 -0400] www.google.com "GET http://www.google.com/ HTTP/1.0" 502  529 "-" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)" "-"
118.248.1.167 - - [07/Sep/2008:18:49:04 -0400] uk.vortal.com "GET http://uk.vortal.com/xml.php?Terms=Jewelry&strict=1&affiliate=lookfree&IP=69.89.12.138&rpp=10 HTTP/1.1" 502  529 "-" "Mozilla/4.0 (compatible; MSIE 4.01; Windows NT 4.0)" "-"
118.248.1.167 - - [07/Sep/2008:18:49:04 -0400] uk.vortal.com "GET http://uk.vortal.com/xml.php?Terms=Jewelry&strict=1&affiliate=woonkrant&IP=69.89.12.138&rpp=10 HTTP/1.1" 502  529 "-" "Mozilla/4.0 (compatible; MSIE 4.01; Windows NT 4.0)" "-"
222.184.123.6 - - [07/Sep/2008:18:49:04 -0400] www.linkbucks.com "GET http://www.linkbucks.com/link/f88c0a49 HTTP/1.0" 502  529 "http://www.google.com/" "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 5.0; Alexa Toolbar)" "-"
118.248.1.167 - - [07/Sep/2008:18:49:05 -0400] uk.vortal.com "GET http://uk.vortal.com/xml.php?Terms=Jewelry&strict=1&affiliate=woonkrant&IP=69.89.12.138&rpp=10 HTTP/1.1" 502  529 "-" "Mozilla/4.0 (compatible; MSIE 4.01; Windows NT 4.0)" "-"
118.248.1.167 - - [07/Sep/2008:18:49:05 -0400] uk.vortal.com "GET http://uk.vortal.com/xml.php?Terms=Jewelry&strict=1&affiliate=lookfree&IP=69.89.12.138&rpp=10 HTTP/1.1" 502  529 "-" "Mozilla/4.0 (compatible; MSIE 4.01; Windows NT 4.0)" "-"

Status 502, Bad Gateway, o nginx trata esse tipo de requisição de um jeito diferente do apache, que na minha opnião, é muito mais inteligente.

Resolvi procurar como bloquear essas requisições mais explicitamente, deixando claro que essas requisições não eram permitidas. Então configurei o nginx para bloquear requisições que partirem de um host diferente que não o meu.

if ($host !~* ^www\.joeh\.com\.br$) {
    return 403;
}

Veremos agora se terei mais problemas com os spammers do mal :)

Closures no PHP 5.3

Dias desses publiquei no Gist um trecho de código que faz uso de Closures do PHP 5.3, mostrando um pouco do que é possível com essa nova versão.

Esses dias estava eu mexendo com Malline e Haml quando me surgiu a idéia, poderia eu fazer algo parecido usando PHP 5.3 e Closures? A resposta é o código abaixo:

A saída será essa:

<html><head><title>Titulo da Pagina</title></head><body><p>Hello World</p><p>Outro Paragrafo<p>Um subparagrafo agora</p></p></body></html>

Isso mostra que será possível fazer um linguagem de template usando PHP puro, muito parecido com o que Malline faz para o Ruby/Rails.

Teste com o Sinatra

Esses dias vi alguém no Twitter falando do Sinatra (não me lembro quem, e também fiquei com preguiça de procurar :P ) e resolvi testar. Fiz um script que monta um select com todos os episódios do Rails Podcast Brasil, e ainda dando a possibilidade de ver o show notes de cada um deles.

Para instalar o Sinatra basta um simples

sudo gem install sinatra

Para ver se está funcionando basta criar um arquivo muito simples como esse: (test_sinatra.rb)

require 'rubygems'
require 'sinatra'

get '/' do
    "Funciona muuuito esse Sinatra"
end

Botar o webserver a rodar

ruby test_sinatra.rb

E rodar no browser

http://localhost:4567/

Se aparecer na sua tela: Funciona muito esse Sinatra então tudo funcionou como deveria :)

Agora vamos a parte mais "complicada". Vamos pegar o nosso arquivo test_sinatra.tb e colocar o código abaixo:

require 'rubygems'
require 'sinatra'
require 'hpricot'
require 'open-uri'

get '/' do
  body = '<form method="post" action="/"><select name="link">'

  doc = Hpricot(open("http://podcast.rubyonrails.pro.br/"))
  (doc/'div.sub').each do |sub|
    a = sub.next_sibling.search('a').last
    if a
      (sub/'h1').each do |h1|
        number = h1.children[0].inner_text
        puts number.to_s
        if number[0] == 35 # igual a #
          body << '<option value="' + a[:href] + '">Episódio ' + number + ' - ' + h1.children[2].inner_text + '</option>'
        end
      end
    end
  end
  body << '</select><button>Ok</button></form>'
end

post '/' do
  body = '<form method="post" action="/"><select name="link">'

  doc = Hpricot(open("http://podcast.rubyonrails.pro.br/"))
  (doc/'div.sub').each do |sub|
    a = sub.next_sibling.search('a').last
    if a
      (sub/'h1').each do |h1|
        number = h1.children[0].inner_text
        if number[0] == 35 # igual a #
          body << '<option value="' + a[:href] + '">Episódio ' + number + ' - ' + h1.children[2].inner_text + '</option>'
        end
      end
    end
  end
  body << '</select><button>Ok</button></form>'

  doc = Hpricot(open("http://podcast.rubyonrails.pro.br#{params[:link]}"))
  body << (doc/'div.content ul').last.to_html
end

Pelo código podemos ver que temos uma chamada para get '/' e outra post '/'. Elas tratam da mesma URL, porém dependendendo do método HTTP atual ele chama um ou outro, isso é bom para separar alguma lógica que seja diferente entre eles.

Mas temos um problema, o nosso código não está DRY, tem muito código duplicado. Para resolver isso o Sinatra possui helpers também, que podem ser criados facilmente usando o método helpers, assim:

helpers do
    def meu_helper
        # faz alguma coisa e retorna o resultado
    end
end

Depois é só usar onde quiser

get '/minha_pagina' do
    meu_helper
end

Simples assim. Abaixo o script atualizado usando um helper

require 'rubygems'
require 'sinatra'
require 'hpricot'
require 'open-uri'

helpers do
  def episodes_form
    body = '<form method="post" action="/"><select name="link">'

    doc = Hpricot(open("http://podcast.rubyonrails.pro.br/"))
    (doc/'div.sub').each do |sub|
      a = sub.next_sibling.search('a').last
      if a
        (sub/'h1').each do |h1|
          number = h1.children[0].inner_text
          puts number.to_s
          if number[0] == 35 # igual a #
            body << '<option value="' + a[:href] + '">Episódio ' + number + ' - ' + h1.children[2].inner_text + '</option>'
          end
        end
      end
    end
    body << '</select><button>Ok</button></form>'
  end
end

get '/' do
  episodes_form
end

post '/' do
  body = episodes_form

  doc = Hpricot(open("http://podcast.rubyonrails.pro.br#{params[:link]}"))
  body << (doc/'div.content ul').last.to_html
end

Para coisas muito simples, frameworks como o Sinatra ajudam bastante, deixando tudo mais prático, mas obviamente tudo tem um limite, não recomendaria ele para aplicações maiores, então, caso precise de algo "mais sério", vá de Rails :)

OBS: O código está usando Hpricot, um parser HTML, bem rápido e fácil de usar, caso você não tenha ele instalado basta fazer como sempre

sudo gem install hpricot

OBS 2: Eu tinha colocado os códigos no gist, mas parece que ele não permite "embedar" diferentes revisões de um mesmo código, então resolvi tirar e colocar somente o link para lá, caso alguem queira dar uma fuçada.

http://gist.github.com/7610

Adicionando tipos de arquivo ao gedit

Estou testando agora o Malline, que está sendo assunto na lista rails-br, e precisei editar os templates no gedit, porém conforme eu ia renomeando os arquivos de .erb para .mn, que é a extensão do Malline, os arquivos iam desaparecendo do File Browser. Para resolver isso é simples, basta adicionar o mime-type no arquivo:

/usr/share/mime/packages/rails.xml*

assim:

<mime-type type="application/x-ruby">
    <comment xml:lang="en">Malline Template</comment>
    <glob pattern="*.mn"/>
</mime-type>

salve o arquivo, feche o gedit e rode:

sudo update-mime-database /usr/share/mime

abra o gedit e veja que os arquivos aparecem novamente no File Browser :)

* Não sei se esse arquivo já vem com o gedit ou se alguma extensão/plugin colocou ele ali.

Paginando com acts_as_taggable

Para paginar modelos que usam acts_as_taggable é bem simples

Post.paginate_tagged_with('nome_da_tag', :page => params[:page], :per_page = 10)

Demorei um pouco até descobrir isso, então resolvi anotar aqui :)

Codificando e decodificando strings com HTML Entities

HTML Entities é uma gem para Ruby que serve para codificar e decodificar html entities.

gem install htmlentities

Para codificar uma string usamos

require 'rubygems'
require 'htmlentities'

he = HTMLEntities.new
encoded = he.encode("essa gem é uma mão na roda!", :named)
puts encoded # essa gem &eacute; uma m&atilde;o na roda!

e para decodificar

require 'rubygems'
require 'htmlentities'

he = HTMLEntities.new
decoded = he.decode("essa gem &eacute; uma m&atilde;o na roda!")
puts decoded # essa gem é uma mão na roda!