HashCode

An organizer of symbols

Archive for March, 2007

Dois Arrays para um Hash

Monday, March 26th, 2007

Em ruby exitem várias formas de resolver problemas do básico ao avançado.
Demonstraremos a criação de um Hash utilizando dois Arrays um para chaves outro para valores.

chaves=[:seu_madruga, :chiquinha, :girafales, :kiko, :bruxa71 ]
valores=%w(preguiça chata professor filhinho sogra)

Vamos criar um hash e atribuir os valores as chaves.

  h={}
  chaves.size.times do |i|
	  h[chaves]=valores[i]
  end

Inspirado no código do TaQ tentei criar uma forma mais elegante de atribuir os valores às chaves sem iterar, adicionando a classe Array o seguinte código:

class Array
  def <=(values)
    Hash[ *[self,values].transpose.flatten ]
  end
end

Como assim Bial?!

Vamos explicar utilizando os arrays chaves e valores

criando um array com os dois arrays

[chaves,valores] #=> => [[:seu_madruga, :chiquinha, :girafales, :kiko, :bruxa71], ["preguiça", "chata", "professor", "filhinho", "sogra"]]

Transpose associa os valores

[chaves,valores].transpose] #=> [[:seu_madruga, "preguiça"], [:chiquinha, "chata"], [:girafales, "professor"], [:kiko, "filhinho"], [:bruxa71, "sogra"]]

“desarrayalize” :)

[chaves,valores].transpose.flatten] #=> [:seu_madruga, "preguiça", :chiquinha, "chata", :girafales, "professor", :kiko, "filhinho", :bruxa71, "sogra"]

Por fim cria o Hash

Hash[*[chaves,valores].transpose.flatten] #=>  {:chiquinha=>"chata", :girafales=>"professor", :kiko=>"filhinho", :bruxa71=>"sogra", :seu_madruga=>"preguiça"}

Portanto podemos utilizar nosso Array modificado da seguinte forma

 chaves <= valores

Resultado

{:chiquinha=>"chata", :girafales=>"professor", :kiko=>"filhinho", :bruxa71=>"sogra", :seu_madruga=>"preguiça"}

Working With Rails 03/2007

Thursday, March 22nd, 2007

Acabei de receber do www.workingwithrails.com
Estamos crescendo!!

  • 1191 United States
  • 212 United Kingdom
  • 168 Canada
  • 151 Germany
  • 145 Brazil
  • 133 India
  • 114 Australia
  • 96 France
  • 96 Spain
  • 90 Netherlands
  • 61 Poland
  • 59 Sweden
  • 52 Denmark
  • 45 Russia
  • 42 Italy
  • 40 China
  • 39 Belgium
  • 36 Switzerland
  • 36 Ukraine
  • 33 Portugal
  • 31 Argentina
  • 29 New Zealand
  • 26 Ireland
  • 25 Israel
  • 23 Philippines
  • 22 Finland
  • 20 Japan
  • 19 South Africa
  • 19 Czech Republic
  • 19 Romania
  • 18 Austria
  • 16 Norway
  • 14 Turkey
  • 13 Hungary
  • 12 Singapore
  • 12 Mexico
  • 10 Indonesia
  • 10 Malaysia
  • 10 Korea (South)
  • 8 Bulgaria
  • 8 Lithuania
  • 8 Venezuela
  • 7 Egypt
  • 6 Chile
  • 6 Peru
  • 6 Slovenia
  • 5 Uruguay
  • 5 Slovakia
  • 5 Croatia
  • 5 Scotland
  • 5 Colombia
  • 5 Estonia
  • 4 Azerbaijan
  • 4 Northern Ireland
  • 4 Uzbekistan
  • 4 Latvia
  • 4 Greece
  • 4 Costa Rica
  • 3 Viet Nam
  • 3 Trinidad and Tobago
  • 3 Taiwan
  • 3 United Arab Emirates
  • 3 Pakistan
  • 3 Afghanistan
  • 3 Iran
  • 3 Kazakhstan
  • 3 Ecuador
  • 3 Guatemala
  • 3 Moldova, Republic of
  • 2 Serbia and Montenegro
  • 2 Jamaica
  • 2 Sri Lanka
  • 2 South Korea
  • 2 Hong Kong
  • 2 Iceland
  • 2 Luxembourg
  • 2 Belarus
  • 2 Georgia
  • 2 Maldives
  • 1 Thailand
  • 1 Kenya
  • 1 El Salvador
  • 1 Tunisia
  • 1 Cambodia
  • 1 Bahrain
  • 1 Morocco
  • 1 Dominican Republic
  • 1 Cuba
  • 1 Cook Islands
  • 1 Puerto Rico
  • 1 Jordan
  • 1 Malawi
  • 1 Macedonia
  • 1 Kuwait
  • 1 Gibraltar
  • 1 England
  • 1 Tanzania
  • 1 Malta
  • 1 Great Britain
  • 1 Nicaragua

Shairon on Wagon with Ruby on Rails

Tuesday, March 20th, 2007

Ganhei de aniversário do meu amigo Alexando Lima e resolvi colocar aqui no meu blog!!
shairon_ror3.jpg
Obs.: Eu não tenho cara de tarado. (eu acho)!

Properties => YAML

Tuesday, March 13th, 2007

Utilizei o módulo HashUtil para migrar meus Properties da finada Java para YAML.
Com adicionais de acesso indiferente das chaves e tratamento de valores.

Exemplo:
para uma entrada(chave=valor) do tipo

"java.util.logging.FileHandler.limit = 50000"

será criado um hash

{"java"=&gt;{"util"=&gt;{"logging"=&gt;{"FileHandler"=&gt;{"limit"=&gt;50000}}}}}

e um yaml

java:
  util:
    logging:
      FileHandler:
        limit: 50000

podendo ser acessado

p=Properties.new
p.java.util.logging.FileHandler.limit

ou se preferir

p[:java][:util][:logging][:FileHandler][:limit]

outra característica, os dois-pontos no valor serão convertido em array. Para uma entrada do tipo

"libs.jaxws20.classpath=/opt/netbeans-6.0dev/ide8/modules/ext/jaxws21/activation.jar:/opt/netbeans-6.0dev/ide8/modules/ext/jaxws21/FastInfoset.jar"

o yaml será

libs:
  jaxb20:
   classpath:
    - /opt/netbeans-6.0dev/ide8/modules/ext/jaxws21/activation.jar
    - /opt/netbeans-6.0dev/ide8/modules/ext/jaxws21/FastInfoset.jar

Carregando um arquivo e acessando os valores
O arquivo /opt/java1.5/lib/logging.properties(modificado para ficar breve) tem o conteúdo abaixo

log.level= INFO

############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################

# default file output is in user's home directory.
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter

# Limit the message that are printed on the console to INFO and above.
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

# For example, set the com.xyz.foo logger to only log SEVERE
# messages:
com.xyz.foo.level = SEVERE

Carregando…

p=Properties.new
p.load("/opt/java1.5/lib/logging.properties")
#modificando o INFO por WARN
p.log.level="WARN"
puts p.to_yaml

Resultado

--- !map:Properties
com:
  xyz:
    foo:
      level: SEVERE
java:
  util:
    logging:
      ConsoleHandler:
        formatter: java.util.logging.SimpleFormatter
        level: INFO
      FileHandler:
        formatter: java.util.logging.XMLFormatter
        pattern: "%h/java%u.log"
        count: "1"
        limit: "50000"
log:
  level: WARN

Properties está aqui

Extenso e ExtensoReal

Monday, March 12th, 2007

Fiz os módulos Extenso e ExtensoReal que habilitam um Float ou Integer a obter resultado do número em extenso.
Exemplo para Integer:

832.to_extenso =&gt; 'oitocentos e trinta e dois'

125623.to_extenso =&gt; 'cento e vinte e cinco mil e seiscentos e vinte e três'

1_968_854_823.to_extenso =&gt; 'um bilhão e novecentos e sessenta e oito milhões e oitocentos e cinqüenta e quatro mil e oitocentos e vinte e três'

45_233_968_854_823.to_extenso =&gt; 'quarenta e cinco trilhões e duzentos e trinta e três bilhões e novecentos e sessenta e oito milhões e oitocentos e cinqüenta e quatro mil e oitocentos e vinte e três'

E para Float usando nossa moeda Real

1230.to_extenso_real =&gt; 'um mil e duzentos e trinta reais'

1230.95.to_extenso_real =&gt; 'um mil e duzentos e trinta reais e noventa e cinco centavos'

1.50.to_extenso_real =&gt; 'um real e cinqüenta centavos'

832.01.to_extenso_real =&gt; 'oitocentos e trinta e dois reais e um centavo'

Os módulos estão aqui

String e Regexp com caracteres acentuados (pt-BR)

Saturday, March 10th, 2007

Trabalhando com ruby observei que os métodos de upcase e dowmcase da class String ignoram os acentos da nossa língua.
Transformando uma oração do tipo “João fez círculos e um Ângulo sem vétice?!”, em letras maisculas o resultado do método upcase é “JOãO FEZ CíRCULOS E UM ÂNGULO SEM VéRTICE?!”.
Vejam que os nossos queridos caracteres acentuados não foram modificados. Para solucionar o problema, em primeiro momento, criei os métodos br_upcase e br_downcase
no módulo BrazilianString e fiz um mixi na classe String, portanto para oração acima os resultados são:

- “João fez círculos e um Ângulo sem vértice?!”.br_upcase => “JOÃO FEZ CÍRCULOS E UM ÂNGULO SEM VÉRTICE?”

- “João fez círculos e um Ângulo sem vértice?!”.br_downcase => “joão fez círculos e um ângulo sem vértice?”

e um método auxiliar para retirar os acentos

- “João fez círculos e um Ângulo sem vértice?!”.desacentualize => “Joao fez circulos e um Angulo sem vertice?”

Algo semelhante acontece com expressões regulares, mas isso não é uma peculiaridade de Ruby mas de outras linguagens também.
Nas expressões regulares temos as abreviações

<b>Sequence  As [ ... ]        Meaning </b>
&#92;d      [0-9] 	          Digit character(Digito)
&#92;D 	[^0-9] 	          Nondigit(Não dígito)
&#92;s 	[&#92;s&#92;t&#92;r&#92;n&#92;f]    Whitespace character(caracteres considerados em branco)
&#92;S 	[^&#92;s&#92;t&#92;r&#92;n&#92;f]   Nonwhitespace character(caracteres não considerados em branco)
&#92;w 	[A-Za-z0-9_] 	  Word character A(caracter de palava)
&#92;W 	[^A-Za-z0-9_]   Nonword character(não seja caracter de palava)


Vamos testar alguns caracteres acentuados nas expressões regulares:

Quatro carateres de palavra

"João" =~ /&#92;w{4}/        => nil

sem o til

"Joao" =~ /&#92;w{4}/        => 0

Ahhh! Saquei! - Então as expressões regulares não detectam os nosso caracteres acentuados. É, as Regexp detestam os caracteres :)
As regexps não consideram nossos acentos no hiato dos ‘Word character’.

Fiz um esqueminha. Adicionei a classe String o método br_match responsável por fazer match e nas listas e as abreviações \w e \W modificadas.

Source    Para
&#92;w       &#92;wãàáâäèéêëìíîïõòóôöùúûüçÃÀÁÂÄÈÉÊËÌÍÎÏÖÒÓÔÙÚÛÜÇ
&#92;W       [^&#92;wãàáâäèéêëìíîïõòóôöùúûüçÃÀÁÂÄÈÉÊËÌÍÎÏÖÒÓÔÙÚÛÜÇ]
a-z      a-zãàáâäèéêëìíîïõòóôöùúûüç
A-Z      A-ZÃÀÁÂÄÈÉÊËÌÍÎÏÖÒÓÔÙÚÛÜÇ

Utilizando assim

"João".br_match /&#92;w{4}/        => 0

Quebra um galho
A classe e o módulo estão aqui.

Hasheados! Dinâmicos e Aninhados

Wednesday, March 7th, 2007

Estava precisando de gerar uma estrutura de dados dinamicamente sem preocupar se o nome do atributo
existe, se ele está vazio, qual o tipo do atributo, etc. Antes de apresentar uma solução vamos lembrar do Hash

Hash

defini key e value em um dicionário.

 h={}
 h[:chave]="valor"

em um Hash nativo do ruby a priori não temos a possibilidade de acessar os valores através dos métodos

h.chave

um erro do tipo NoMethodError: undefined method `chave’ for {}:Hash
é lançado informando que não há método que responda a chave .
Com o intuito de facilitar a definição e leitura dos valores do Hash criei um módulo chamado HashUtil.
Baixe aqui o hash_util.rb (gzipped)

Forma clássica de atribuição

h={}
h[:usuario]="nome do usuario"
#ou
h.store(:usuario, "nome do usuario")
#ou por string
h['usuario']="nome do usuario"

Usando HashUtil

Pode fazer um extend do módulo na instância

 h={}
 h.extend(HashUtil)

ou adicionar o módulo diretamente no Hash, Criando uma classe Hash e inclindo HashUtil, assim

class Hash
 include HashUtil
end

Para os exemplos subjacentes utilizaremos a classe Hash especializada com include HashUtil.

Formas de atribuição

 h={}
 h.usuario="nome do usuario"
 h.tipo="pessoa física"
 h.sexo="masculino"

verifique com inspect os valores

{'usuario'=&gt;"nome do usuario", 'sexo'=&gt;"masculino", 'tipo'=&gt;"pessoa fisica"}

Os valores podem ser lidos chamando apenas pela nome da chave

h.usuario
#ou
h['usuario']

ou re-atribuir outro valor utilizando o =

Forma dinâmica e aninhada

O HashUtil utiliza internamente regras simples.
1 - se for solicitado uma chave que não existe, aponte para uma nova instância de Hash e retorne nil.
2 - se for atribuido um valor para uma chave que não existe, crie uma nova chave apontando para o valor.
3 - tenta detectar se a chave é String ou Symbol automaticamente

Criaremos uma estrutura semelhante a de diretórios com arquivos(apenas para exemplificar).
Veja como é fácil.

 raiz={}
 raiz.usr.include.sys="mount.h"
 raiz.etc.apache.conf="httpd.conf"
 raiz.etc.cron.daily="logrotate"
 #verificando
 raiz.inspect
 {"etc"=&gt;{"cron"=&gt;{"daily"=&gt;"logrotate"}, "apache"=&gt;{"conf"=&gt;"httpd.conf"}},
  "usr"=&gt;{"include"=&gt;{"sys"=&gt;"mount.h"}}}

Observe que o caminho informado pelo programador será criado automaticamente
Mas gostaríamos agora de adicionar vários diretórios no hash raiz.usr.include, vamos a prática:

 raiz={}
 #criando os hashes
 raiz.usr.include
 raiz.usr.include.sys
 raiz.usr.include.linux
 raiz.usr.include.linuxthreads

 #atribuindo os valores ou outros hashes
 raiz.usr.include.sys.video="vga"
 raiz.usr.include.sys.sound="alsa"
 raiz.usr.include.linux.kernel="mini_kernel.h"

Estamos colocando os valores como String mas aceita quaisquer outros objetos, é um hash normal(hackeado).

Mas esses diretórios aí em cima só podem ter uma arquivo?

Daquela forma sim.

Mas podemos trocar

raiz.usr.include.sys="mount.h"

Por algo mais inteligente. No HashUtil temos um método append que:
- se não tiver a chave cria e atribui o valor
- se tiver a chave e não for array reempacota tudo dentro de um array

O append

Indique o hash e adicione a chave e o valor

raiz={}
raiz.usr.include
raiz.usr.include.append(:sys, 'mount.h')
raiz.usr.include.append(:sys, 'pci.h')
raiz.usr.include.append(:sys, 'perm.h')
raiz.usr.include.append(:linux, 'kernel.h')

Com isso os valores do hash raiz.usr.include.sys podem ser lidos

raiz.usr.include.sys # ["mount.h", "pci.h", "perm.h"]

ou atribuidos

raiz.usr.include.sys &lt;&lt; "mais um valor" #  ["mount.h", "pci.h", "perm.h", "mais um valor"]

Gerando um YAML

Para gerar um yaml é só invocar o to_yaml
Exemplo utilizando a estrutura acima.

 raiz.to_yaml

gera a saída

---
usr:
  include:
    linux: kernel.h
    sys:
    - mount.h
    - pci.h
    - perm.h

o método to_yaml e todos os outro métodos nativos da classe Hash são preservados pelo HashUtil.

Gosto de utilizar o HashUtil para armazenar e recuperar informações de configuração é fácil e intuitivo.

Singleton no Model

Saturday, March 3rd, 2007

Sigleton a nível de model são úteis para minimizar as buscas no banco de dados para tabelas não muito volateis como cidades,estados,status,modelos,etc.

A estrategia e bem simples e é composta por

- uma váriavel estática
- dois métodos estáticos, um para obter os dados o outro para atualizar os dados
- uma diretiva de filtro after_create ou after_save

Estão vamos lá

Temos um Model Estados com
- id
- nome
- uf

Na programação comum deste tipo de entidade o “rubeiro” adiciona no controller ou na view(depende do cara) uma collect(ou map) para ser mostrado em um combo select(html). Assim

@estados=Estados.find(:all).collect {|s| [s.id, s.nome]}

Com isso toda requisição no controller pesquirá no banco.
Utilizando singleton vamos melhorar a obtenção dos dados sem muito esforço.
Inicialmente no corpo da classe Estados vamos adicionar o método de classe Estados.update_for_view

def Estados.update_for_view
  @@estados_view=Estados.find(:all).map {|e| [e.nome, e.id]}
end

e depois o método para obter a lista Estados.for_view

def Estados.for_view
  update_for_view unless defined? @@estados_view
  @@estados_view
end

Para finalizar vamos colocar o filtro after_save para atualizar caso haja alteração

after_save "Estados.update_for_view"

Ficando assim a classe Estados

class Estados &lt; ActiveRecord::Base
  after_save "Estados.update_for_view"

  def Estados.update_for_view
     @@estados_view=Estados.find(:all).map {|e| [e.nome, e.id]}
  end

  def Estados.for_view
    update_for_view unless defined? @@estados_view
    @@estados_view
  end

end

Agora é só adicionar na view ou no seu controller a invocação do método Estados.for_view.
Exemplo na view:

&lt;%= select('clientes', 'estados_id', Estados.for_view %&gt;

Plugins

Saturday, March 3rd, 2007

Estou começando agora em Rails, por enquanto compartilhado dois plugins.

Rails JsDOMenu Menu estilo desktop para Rails
Rails Tree TreeView para Rails