Rails 2.1.1 - Release de Manutenção

post

Depois do Release de Manutenção para o Rails 2.0, sai agora o Release de Manutenção para o Rails 2.1. A nova versão, 2.1.1, corrige os problemas de segurança do REXML, além de outras pequenas atualizações.

Para instalar a nova versão: gem install rails -version 2.1.1

Vejam aqui todas as correções

Usando o Observer no Rails

Este artigo seria apenas uma dica de como saber o que foi alterado em um objeto, mas para ficar mais fácil e principalmente para quem está dando os primeiros passos em rails resolvi fazer uma pequena introdução sobre o assunto.

O Observer é um objeto que se responsabiliza por "escutar" os eventos que ocorrem com outro objeto. Ou seja, qualquer alteração feito no objeto observado será analizado e poderá executar uma ação. Isto é muito útil para gravar logs, enviar emails e outros.

Vamos criar um projetinho para entender:

1
2
3
4
rails observers
cd observers
script/generate scaffold User name:string phone:string age:integer
rake db:migrate

Acima criamos um projeto e geramos uma entidade User e seus atributos, e migramos o banco. Vamos criar agora a entidade de log e o observer:

1
2
3
4
5
6
7
8
9
10
11

script/generate model Log table:string action:string field:string \ 
old_value:string new_value:string comments:text
      exists  app/models/
      ...
script/generate observer user
      exists  app/models/
      exists  test/unit/
      create  app/models/user_observer.rb
      create  test/unit/user_observer_test.rb
rake db:migrate

Feito isto precisamos "ativar" o observer, e para isto basta adicionar no config/enviroment.rb

1
2
3
4
5
6

  # config/enviroment.rb
  ...
  # Activate observers that should always be running
  # config.active_record.observers = :cacher, :garbage_collector
  config.active_record.observers = :user_observer

Agora podemos definir em qual momento ele vai gerar o log, antes ou depois de persistir o objeto:

Como tudo no rails os nomes já definem o que cada um vai fazer. Vamos gerar um log quando criar ou excluir um usuário. Para isto, vamos abrir o arquivo user_observer.rb

1
2
3
4
5
6
7
8
9
10
# app/models/user_observer.rb
class UserObserver < ActiveRecord::Observer
  def after_create(user)
    Log.create(:table => "user", :action => "create", :comments => "New user added!")
  end
  
  def after_destroy(user)
    Log.create(:table => "user", :action => "destroy", :comments => "User removed!")
  end
end

Vamos acessar http://localhost:3000/users e criar um user e veja o log gerado:

1
2
3
4
5
6
INSERT INTO "users" ("name", "updated_at", "phone", "age", "created_at")
VALUES('First User', '2008-08-22 12:07:34', '111-1111', 20, '2008-08-22 12:07:34')

INSERT INTO "logs" ("new_value", "updated_at", "comments", "old_value", "action", "field",
"table", "created_at") VALUES(NULL, '2008-08-22 12:07:34', 'New user added!', NULL, 
'create', NULL, 'user', '2008-08-22 12:07:34')

Simples? Muito. Agora vamos pedir para ele observar o que foi alterado em um objeto:

1
2
3
4
5
6
7
8
9
# app/models/user_observer.rb
  ...
  def before_update(user)
    user.changes.each do |key, values|
      Log.create(:table => "user", :action => "update", :field => key, :old_value => values.first,
        :new_value => values.last,  :comments => "User updated!")
    end
  end
  ...

O log gerado:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
INSERT INTO "logs" ("new_value", "updated_at", "comments",
"old_value", "action", "field", "table", "created_at") VALUES('2008-08-22 12:29:00', 
'2008-08-22 12:29:00', 'User updated!', '2008-08-22 12:07:34', 'update', 'updated_at', 'user',
'2008-08-22 12:29:00')

INSERT INTO "logs" ("new_value", "updated_at", "comments", "old_value", "action", "field",
"table", "created_at") VALUES('222-2222', '2008-08-22 12:29:00', 'User updated!', '111-1111', 
'update', 'phone', 'user', '2008-08-22 12:29:00')

INSERT INTO "logs" ("new_value", "updated_at", "comments", "old_value", "action", "field", 
"table", "created_at") VALUES(26, '2008-08-22 12:29:00', 'User updated!', 20, 'update',
'age', 'user', '2008-08-22 12:29:00')

UPDATE "users" SET "phone" = '222-2222', "age" = 26, "updated_at" = '2008-08-22 12:29:00'
WHERE "id" = 1

Aqui temos apenas uma introdução do que podemos ter com o Observer. Bons estudos.

Gem has_many_select

O problema

Estava fazendo a migração de um sistema para a versão 2.1 do rails, que usa tabelas sem qualquer normalização ou padrão de DB, e me deparei com o seguinte problema:

1
2
3
4
5
6
7
  class HeaderSale < ActiveRecord::Base
    set_table_name("cab_pedido")
    set_primary_key("r_e_c_n_o_")

    has_many :justifications,
      :foreign_key => 'nr_pedido'
  end

O problema é que tenho uma campo chamado 'r_e_c_n_o_' que é serial(auto incremento), mais não é uma primary key. Pra complicar eu tenho um outro campo, chamado 'cpv_pedido' que a primary key e também é auto incremento (pega o último e soma), ou seja, este banco é um "sonho" para qualquer desenvolvedor.

Quando eu faço um busca:

1
2
3
4
x = HeaderSale.find(:first, :conditions => 'cpv_pedido = 193514')
   => 'SELECT * FROM "cab_pedido_vendas" WHERE (cpv_pedido = 193514) LIMIT 1'
x.justifications
   => 'SELECT * FROM "justificativa" WHERE ("justificativa".nr_pedido = 307634)'

Mais o correto seria:


=> 'SELECT * FROM "justificativa" WHERE ("justificativa".nr_pedido = 193514)'

Para resolver este problema, criei a gem has_many_select - não sei porque dei este nome, que temporariamente resolve o problema, já que na próxima versão do rails teremos está opção.

Como usar ?

Primeiro passo é instalar a gem:

1
2
gem sources -a http://gems.github.com
gem install ozeias-has_many_select

Depois no environment.rb


require 'has_many_select'

No relacionamento do model:

1
2
3
4
5
6
7
  class HeaderSale < ActiveRecord::Base
    set_table_name("cab_pedido")
    set_primary_key("r_e_c_n_o_")

    has_many :justifications,
      :foreign_key => 'nr_pedido', :primary_key => 'cpv_pedido'
  end

O resultado:

1
2
3
4
x = HeaderSale.find(:first, :conditions => 'cpv_pedido = 193514')
   => 'SELECT * FROM "cab_pedido_vendas" WHERE (cpv_pedido = 193514) LIMIT 1'
x.justifications
   => 'SELECT * FROM "justificativa" WHERE ("justificativa".nr_pedido = 193514)'

E agora?

E agora, se a gem estiver sendo útil para você, não se esqueça de me recomendar no working with rails.

Sugestões, dicas e críticas são bem-vindas.

Update 22/08/2008:

Apesar de ter mudado meu login no github, o nome da gem não foi atualizado. Valeu Marcus. O correto é:

1
2
gem sources -a http://gems.github.com
gem install ozsantana-has_many_select

No github: http://github.com/ozeias/has_many_select/tree/master

Rails 2.1 RC1

O DHH acabou de fazer um pequeno anúncio no twitter sobre o lançamento do Rails 2.1 RC1. Segundo ele as gems já estão no servidor beta.

DHH: "Rails 2.1 RC1 has been tagged, the gems are on the beta server, official announcement shortly. But no need holding you back from trying it".

Para acompanhar as novidades acessem o blog do Calos Brando e sua série Edge Rails. Boas novidades estão chegado, vamos testar.

UPDATE:

Para instalar o Rails 2.1 RC1 do servidor beta, basta executar:


sudo gem install rails –source http://gems.rubyonrails.com/

Ou:

1
2
3
4
5
6
git clone git://github.com/rails/rails.git
git pull
git checkout v2.1.0_RC1
git checkout master
rake rails:update
script/dbconsole

Hoje de manhã saiu um novo episódio do Railscast com o procedimento de atualização.