Fernando Luizão

Desenvolvimento de software e nerdices em geral

Posts Tagged ‘plugin

Deslocalizando datas e números usando o i18n do Rails

with 4 comments

NOTA: Antes de começar, se você não sabe o que é ou não tem uma noção básica sobre o i18n do Rails, leia esse guia.

Fazer conversões de datas e números localizados sempre foi uma tarefa chata em Rails (e em outras linguagens/frameworks também). O usuário digita uma data no formato brasileiro (por exemplo, dd/mm/yyyy), e o Ruby não consegue converter a data corretamente. A maneira mais usada por aí é criar atributos virtuais (ou mesmo sobrecrever o setter padrão do atributo) no model para fazer a conversão, por exemplo:

class Model < ActiveRecord::Base #recebe uma data no formato dd/mm/yyyy e converte para yyyy-mm-dd def data_vencimento=value self[:data_vencimento] = value.split("/").reverse.join("-") end end [/sourcecode] Isso funciona, mas quando eu usava isso não ficava satisfeito, queria algo mais "mágico" :). Perguntei no fórum RubyOnBr se não teria um jeito de fazer a conversão automaticamente, usando o próprio i18n do Rails. Então meu camarada Rafael Rosa chegou a uma solução, usando o formato padrão de data configurado no aquivo de i18n para fazer o parsing de datas. Eu acrescentei suporte à números, o Rafael organizou tudo, escreveu testes, e surgiu o i18n_localize_core.
O plugin funciona bem, consegue converter datas e números usando as configurações do arquivo de internacionalização. O plugin resolvia bem para mim, inclusive tinha feito alguns hacks no plugin para fazer a conversão nos text_fields, mas não coloquei no meu fork porque estava muito feio :D.
Só que a um tempo atrás, encontrei o plugin delocalize, escrito por Clemens Kofler, que faz basicamente a mesma coisa, mas melhor :). O i18n_localize_core é meio invasivo, sobrescreve os métodos Date#_parse, String#to_i e String#to_f. No delocalize, o Clemens sobrescreveu apenas onde era necessário, e ainda adicionou suporte à entrada de vários formatos de datas.
Usar o plugin é simples, como tudo na vida deve ser :). Tendo configurado seu arquivo de i18n e instalado o plugin, basta adicionar a chave :input no arquivo de i18n com os formatos de data que o usuário poderá digitar e sair pro abraço :). O plugin se encarrega de fazer o resto de forma transparente.

No README do plugin tem alguns exemplos de uso, dêem uma conferida no projeto:

http://github.com/clemens/delocalize

Written by fernandoluizao

June 18, 2009 at 12:50 am

Posted in Rails

Tagged with , , , , ,

acts_as_active: mais um plugin no estilo acts_as_paranoid

leave a comment »

Pra quem é do mundo Rails, o acts_as_paranoid já é velho conhecido, ele sobrescreve alguns métodos do ActiveRecord para marcar um registro como excluído em vez de efetivamente excluí-lo. Ele também adiciona um escopo para esconder os registros “exluídos” dos finders e outras operações comuns como count, sum, max, etc. Meu novo plugin, acts_as_active, faz basicamente a mesma coisa, porém utiliza um booleano em vez de um timestamp para indicar que o registro está ativo e utiliza um escopo padrão para mostrar apenas os ativos.

Instalando o acts_as_active

Pra instalar, o de sempre:

script/plugin install git://github.com/fernandoluizao/acts_as_active.git

IMPORTANTE: Meu plugin depende de um novo recurso adicionado no Rails 2.3 chamado default_scope
Para quem usa Rails 2.1 ou 2.2, instale o plugin default_scope.

script/plugin install git://github.com/duncanbeevers/default_scope.git

Uso

O plugin assume que vc tem um campo booleano com o nome “active”, com valor default TRUE. Exemplo de uso:

  create_table :users do |t|
    t.string :name
    t.string :last_name
    t.boolean :active, :null => false, :default => true
  end

  class User < ActiveRecord::Base
    acts_as_active
  end

  User.first.destroy
  UPDATE users SET active = 'f' WHERE id = 1

  #chama o destroy original e acaba com nosso usuário
  User.first.destroy! 
  DELETE FROM users WHERE id = 1

  User.all
  SELECT * FROM users WHERE active = 't'

  User.all :conditions => {:name => "joe"}
  SELECT * FROM users WHERE (name = 'joe') AND (active = 't')

Se quiser procurar incluindo os inativos:

  User.find_with_inactive :conditions => {:name => "joe"}
  SELECT * FROM users WHERE (name = 'joe')

O nome do campo booleano pode ser configurado da seguinte forma:

class User < ActiveRecord::Base
  acts_as_active :with => :nome_campo
end

É isso, um plugin simples, mas que me poupa um bom trabalho. Espero que gostem :).

Written by fernandoluizao

March 6, 2009 at 12:51 am

Insensitivity: O plugin insensível

leave a comment »

Consultas case insensitive e independentes de acentos que funcionem em qualquer SGBD são difíceis de conseguir, pois cada banco possui sua maneira específica de fazer isso. Se forem tomados alguns cuidados, é possível usar a sintaxe correta de acordo com o adaptador que estamos usando. No PostgreSQL por exemplo, para conseguir uma consulta case insensitive e independente de acentos, temos que fazer coisas assim ou assim.

O problema em usar conversões nas colunas é que, além delas não fazerem parte padrão SQL, perdemos os índices na coluna, o que vai fazer nossa consulta demorar um pouco mais (dependendo da quatidade de registros, usar ou não um índice pode fazer uma grande diferença). Para resolver esse problema, criei o plugin “Insensitivity“, que usa uma coluna extra para armazenar o valor sem acentos e em minúsculas. Assim, durante as consultas, o ActiveRecord irá utilizar essa coluna em vez da coluna com o valor real.

Vantagens dessa abordagem

Independente de banco
Se hover índices, serão usados

Desvantagens

Utiliza o dobro de espaço.

Instalando e usando o plugin

script/plugin install git://github.com/fernandoluizao/insensitivity.git

No seu model, você deve indicar quais campos usarão a pesquisa case insensitive:

class User < ActiveRecord:Base
  insensible :name, :email
end
&#91;/sourcecode&#93;

Depois, você <strong>DEVE</strong> adicionar à tabela, os campos com o mesmo nome dos que foram indicados e com o sufixo "_search". Se desejar, também coloque índices para melhorar a performance. Por exemplo:


script/generate migration add_search_fields

def self.up
  change_table :users do |t|
    t.string :name_search
    t.string :email_search
  end

  add_index :users, :name_search
  add_index :users, :email_search

  #Se sua tabela ja tiver dados, vc pode usar
  #User.make_insensible!
  #Isso vai inicializar os novos campos nos registros já existentes
end

def self.down
  change_table :users do |t|
    t.remove :name_search
    t.remove :email_search
  end
end

Para fazer as buscas, não muda nada. Veja as consultas geradas:

User.find_by_name('bla')
SELECT * FROM users WHERE name_search = 'bla'
User.find(:first, :conditions => ['name = ?', 'bla'], :order => 'name')
SELECT * FROM users WHERE name_search = 'bla' ORDER BY name LIMIT 1

Tudo funciona de forma transparente, sem vc precisar se preocupar =). Sua única preocupação deve ser passar o valor nas conditions sem acentos e em minusculas, para isso pode usar o método “insensible”, adicionado à classe String:

User.find(:first, :conditions => ['name LIKE ?', "%#{params[:name].insensible}%"])
SELECT * FROM users WHERE name_search LIKE '%bla%'

Bom, é isso, pesquisa sem sentimentos :). Quem quiser colaborar:

http://github.com/fernandoluizao/insensitivity/

PS: Testei apenas com rails 2.2.2. Pode não funcionar com versões mais antigas (< 2.0)

Written by fernandoluizao

February 10, 2009 at 11:25 pm