Você está na página 1de 120

Ruby on Rails 2.

O QUE HÁ DE NOVO?

Primeira Edição
Ruby on Rails 2.2

O QUE HÁ DE NOVO?

Primeira Edição

por Carlos Brando


traduzido por Carl Youngblood
© Copyright 2008 Carlos Brando. Todos os direitos reservados.

Primeira Edição: Outubro 2008

Carlos Brando
http://www.nomedojogo.com

Carl Youngblood
http://blog.youngbloods.org
Ruby on Rails 2.2

INTRODUÇÃO

O Ruby on Rails 2.2 está cheio de novidades, incluindo novas funcionalidades, melhorias e correções de bugs antigos.
Neste livro você encontrará uma breve descrição, acompanhada de um exemplo (na maioria dos casos) de cada uma
das principais novidades deste versão.

Escrever este livro foi um trabalho árduo, por isto espero que ele lhe seja muito útil, ajudando-o a usar mais
plenamente cada novo recurso incluído no Ruby on Rails.

À partir desta versão o Rails passa a ser políglota. Fazendo uso do novo sistema de internacionalização (i18n) podemos
construir aplicativos para usuários do mundo com pouquíssimo esforço.

Muito trabalho foi feito para deixar o Rails thread-safety e o mais preparado possível para o futuro Ruby 1.9. Também
houve uma preocupação grande para deixá-lo mais compatível com o JRuby. Embora o recurso de thread-safe ainda
não esteja disponível para todos nós, já que ele apenas funcionará em máquinas virtuais com suporte a threads nativas,
como o JRuby por exemplo, ele é uma grande adição ao framework.

Se no passado houve muita reclamação quanto a documentação do Rails, agora ninguém poderá mais reclamar. Um
grande trabalho foi feito para documentar o código e as funcionalidades do Rails. Quer um exemplo? Execute no
terminal:

rake doc:guides

Esta tarefa criará uma pasta doc/guides na raiz do seu projeto com vários guias para auxiliá-lo durante o aprendizado
do Rails.

7
Ruby on Rails 2.2 - O que há de novo?

Capitulo 1

ActiveRecord

NOVA OPÇÃO PARA ASSOCIAÇÕES: :VALIDATE

Uma nova opção foi acrescentado às associações do Rails. A opção :validate pode ser usada para ligar ou desligar a
validação de objetos associados ao modelo. Veja um exemplo:

class AuditLog < ActiveRecord::Base


belongs_to :developer, :validate => false
end

log = AuditLog.create(:developer_id => 0 , :message => "")


log.developer = Developer.new

puts log.developer.valid?
# => false

8
Capitulo 1: ActiveRecord

puts log.valid?
# => true

puts log.save
# => true

Como você pode ver no exemplo acima, embora o objeto associado ( Developer) não seja válido, ainda assim o objeto
principal (AuditLog) foi salvo no banco de dados. Este não era o comportamento normal nas versões anteriores do
Rails, onde um objeto pai só poderia ser gravado se todos os filhos fossem válidos.

Embora no exemplo acima estamos desligando a validação de associações, para demostrar o novo recurso, este é o
novo valor padrão para este tipo de relacionamento de agora em diante. Ou seja, todas as validações em associações
belongs_to estarão desligadas (como no exemplo) e para habilitarmos o comportamento antigo devemos usar a
expressão :validate => true.

UMA NOVA FORMA DE ESPECIFICAR CONDITIONS USANDO HASH

Ao realizar buscas no banco de dados, por vezes temos de fazer uso da opção :joins a fim de melhorar a
performance de nosso aplicativo, em outros casos precisamos simplesmente recuperar algum tipo de informação que
depende do resultado de duas tabelas.

Por exemplo, se desejássemos recuperar todos os usuários do sistema que compraram itens da cor vermelha, faríamos
algo assim:

User.all :joins => :items, :conditions => ["items.color = ?", 'red']

Este tipo de sintaxe parece incomodar já que você precisa incluir o nome da tabela (no caso items) dentro de uma
string. O código parece estranho.

9
Ruby on Rails 2.2 - O que há de novo?

No Rails 2.2 encontraremos uma novidade nesta questão, nos permitindo fazer a mesma coisa de uma forma um
pouco diferente, usando uma chave dentro do Hash para identificar a tabela:

User.all :joins => :items, :conditions => {


:age => 10,
:items => { :color => 'red' }
}

# um outro exemplo que talvez deixe o código mais claro


User.all :joins => :items, :conditions => {
:users => { :age => 10 },
:items => { :color => 'red' }
}

Na minha opinião, desta forma o código fica muito mais claro, principalmente se temos de condicionar muitos campos
de várias tabelas.

Só tenha em mente que a chave usada é o nome da tabela (você percebe pelo nome pluralizado) ou um alias caso você
o tenha especificado na query.

NOVA OPÇÃO :FROM PARA MÉTODOS DE CÁLCULO DO ACTIVERECORD

Uma nova opção foi incluída aos métodos de cálculos do ActiveRecord (count, sum, average, minimum e maximum).

Ao fazer uso da opção :from, podemos sobrecarregar o nome da tabela na query gerada pelo ActiveRecord, o que
não parece muito útil em um primeiro momento. Mas algo interessante que esta opção nos permite fazer é forçar o
MySQL a usar um índice especifico ao realizar o cálculo desejado.

Veja alguns exemplos de uso:

10
Capitulo 1: ActiveRecord

# Forçando o MySQL a usar um índice para realizar o cálculo


Edge.count :all, :from => 'edges USE INDEX(unique_edge_index)',
:conditions => 'sink_id < 5')

# Realizando o cálculo em uma tabela diferente da classe associada


Company.count :all, :from => 'accounts'

MÉTODO MERGE_CONDITIONS DO ACTIVERECORD AGORA É PÚBLICO

O método merge_conditions do ActiveRecord agora é um método público, o que significa que ele estará presente
em todas os seus modelos.

Este método faz exatamente o que o nome diz. Você pode informar várias conditions separadas em seus parâmetros
e ele junta tudo em uma condition só. Por exemplo:

class Post < ActiveRecord::Base


end

a = { :author => 'Carlos Brando' }


b = [ 'title = ?', 'Edge Rails' ]

Post.merge_conditions(a, b)
# => "(\"posts\".\"author\" = 'Carlos Brando') AND (title = 'Edge Rails')"

Note que ele une as conditions com um AND, sempre.

11
Ruby on Rails 2.2 - O que há de novo?

DEFININDO COMO O MÉTODO VALIDATES_LENGTH_OF DEVE FUNCIONAR

O método validates_length_of faz parte dos muitos métodos de validação contidos no ActiveRecord. Este método
em particular serve para garantir que o valor gravado em uma determinada coluna no banco de dados terá um
tamanho máximo, mínimo, exato, ou até mesmo se está em um intervalo de valores.

Mas o termo "tamanho" é relativo. Hoje quando dizemos "tamanho" estamos nos referindo a quantidade de caracteres
no texto.

Mas imagine um caso onde eu tenha um campo em um formulário onde a limitação não seja definida pela quantidade de
caracteres e sim pela quantidade de palavras, algo como "escreva um texto com no mínimo 100 palavras". Imagine uma
página onde o usuário tenha de redigir uma redação, por exemplo.

Hoje, para validar isto não teríamos outra escolha senão criarmos um novo método que faça esta validação. Mas à
partir do Rails 2.2 poderemos personalizar o método validates_length_of para funcionar da forma como desejamos
usando a opção :tokenizer.

Veja um exemplo que resolveria o problema citado acima:

validates_length_of :essay,
:minimum => 100,
:too_short => "Sua redação deve ter no mínimo %d palavras."),
:tokenizer => lambda {|str| str.scan(/\w+/) }

Este é apenas um exemplo do que podemos fazer com esta nova opção. Além disso podemos usa-lá para contar
apenas a quantidade de dígitos, menções de uma única palavra, etc..

12
Capitulo 1: ActiveRecord

TRATANDO A OPÇÃO :LIMIT COMO BYTES

À partir desta versão do Rails quando usarmos a opção :limit para colunas com números inteiros, em nossas
migrations, estaremos nos referindo ao número de bytes, no MySQL e no PostgreSQL (no sqlite sempre foi assim).

O tipo da coluna no banco de dados dependerá da quantidade de bytes especifica. Veja o trecho de código que
identifica o tipo da coluna para o MySQL:

when 1; 'tinyint'
when 2; 'smallint'
when 3; 'mediumint'
when nil, 4, 11; 'int(11)' # compatibility with MySQL default
when 5..8; 'bigint'
else raise(ActiveRecordError, "No integer type has byte size #{limit}")

E para o PostgreSQL:

when 1..2; 'smallint'


when 3..4, nil; 'integer'
when 5..8; 'bigint'

INFORMANDO OUTRO PRIMARY_KEY EM ASSOCIAÇÕES

Uma nova opção foi acrescentado aos métodos has_many e has_one: a opção :primary_key.

Fazendo uso desta opção podemos definir qual método do modelo associado retornará a chave primária que será
usada na associação. Obviamente o método padrão é o id.

Veja um exemplo de uso:

13
Ruby on Rails 2.2 - O que há de novo?

has_many :clients_using_primary_key, :class_name => 'Client',


:primary_key => 'name', :foreign_key => 'firm_name'

O método has_one funciona exatamente como no exemplo acima.

NOVO HELPER PARA TESTES (ASSERT_SQL)

Talvez você já conheça o método assert_queries que ajuda a validar nos testes a quantidade de queries executadas.
Por exemplo:

assert_queries(Firm.partial_updates? ? 0 : 1) { firm.save! }

No teste acima estou afirmando que se houver partial_updates uma query deve ser executada no banco de dados,
caso contrário nenhuma deve ser executada.

Agora ganhamos mais um helper para ajudar a testar o tipo de query executada, o método assert_sql. Um exemplo:

def test_empty_with_counter_sql
company = Firm.find(:first)
assert_sql /COUNT/i do
assert_equal false, company.clients.empty?
end
end

No exemplo acima estou confirmando que no bloco informado ao método pelo menos uma query deve conter a
palavra COUNT. Obviamente você pode ser mais especifico na expressão regular que estiver usando. Vamos pegar
um outro exemplo:

14
Capitulo 1: ActiveRecord

assert_sql(/\(\"companies\".\"id\" IN \(1\)\)/) do
Account.find(1, :include => :firm)
end

MIGRATIONPROXY

Imagine que ao rodar uma série de migrations um determinado modelo seja renomeado. Agora imagine que antes disto
acontecer uma outra migration faça referencia a este modelo. Isto causará um erro feio e parará a execução de suas
migrations.

Para evitar este tipo de problema foi criado uma nova classe chamada MigrationProxy que armazena o nome, a versão
e o nome do arquivo de cada migration e usa estas informações para carregar a migration somente quando ela for
necessária, evitando carregar todas elas de uma vez.

POOL DE CONEXÕES NO RAILS

Algo que muita gente reclama sobre o Rails é que ele é lento. Sabemos que isto não é totalmente verdade, mas
também sabemos que muita coisa pode ser feita para melhorar a performance dele.

Uma destas coisas acabou de se feita. Foi incluído ao Rails um Pool de Conexões com o banco de dados.

Toda vez que uma requisição ao banco de dados é feita, perde-se algum tempo criando uma nova conexão e só depois
é que a pesquisa ou gravação é realizada. Olhando superficialmente pode parecer algo muito rápido e simples, mas
abrir uma conexão com o banco de dados não é tão simples assim. É necessário estabelecer uma conexão física com o
servidor do banco, autenticar a conexão e realizar mais uma série de validações. Tudo isto consome recursos e tempo.
Após criar esta conexão, o Rails a usa para todas as requisições necessárias, e queries mais pesadas podem atrasar a

15
Ruby on Rails 2.2 - O que há de novo?

execução de outras requisições. Isto explica muito bem o porque de o banco de dados se tornar o vilão de alguns
grandes projetos criados em Rails.

A solução para este tipo de problema é criar um pool de conexões com o banco de dados e distribuir as requisições
feitas entre estas conexões.

O processo é o seguinte: Uma conexão com o banco de dados é aberta e utilizada para realizar uma pesquisa. Depois
disso, ao invés de fecha-la, ela é armazena no pool de conexões. Quando uma outra requisição é feita, ao invés de abrir
uma nova conexão, o sistema reaproveita uma que já foi aberta, diminuindo o tempo e os recursos necessários para
realizar a tarefa. Várias conexões podem ser armazenadas no pool ao mesmo tempo e as requisições serão distribuídas
entre elas. Isto significa que mesmo com uma querie lenta sendo executada no banco de dados, a aplicação continuará
recebendo e executando outras queries usando as demais conexões armazenadas no pool.

No Rails será criado um novo pool para cada execução do método establish_connection. Em outras palavras, cada
banco de dados cadastrado no arquivo database.yml terá seu próprio pool de conexões.

O pool começa vazio e vai crescendo até o limite de 5 conexões (padrão), mas você pode aumentar este limite
acrescentando a opção pool na configuração do banco de dados.

development:
adapter: mysql
host: localhost
username: myuser
password: mypass
database: somedatabase
pool: 10
wait_timeout: 15

16
Capitulo 1: ActiveRecord

Se nenhuma das conexões estiver disponível, uma thread irá esperar durante 5 segundos (padrão) antes de desistir de
esperar por uma conexão. Este tempo também pode ser configurado adicionando a chave wait_timeout na
configuração do banco de dados.

Se você desejar usar o pool de conexões fora do ActionPack, existe o método


ActiveRecord::Base.connection_pool que permite que você manualmente faça o checkout/checkin das conexões.
Não esqueça de fazer o checkin quando terminar de usar a conexão.

connection = ActiveRecord::Base.connection_pool.checkout

# faz alguma coisa no banco de dados

ActiveRecord::Base.connection_pool.checkin(connection)

Você também pode usar o método ActiveRecord::Base.connection_pool.with_connection que já faz o checkout/


checkin automaticamente para você, tornando-se uma opção mais segura.

ActiveRecord::Base.connection_pool.with_connection do |connection|
# faz alguma coisa no banco de dados
end

MIGRATIONS TRANSACIONAIS NO POSTGRESQL

Quando uma migration está em execução e um erro ocorre, tudo que já foi executado será aplicado ao banco de
dados, mas tudo que estiver após o erro, não será aplicado. Além disso a migration será marcada como concluída. Isto
pode dar uma certa dor de cabeça para corrigir.

17
Ruby on Rails 2.2 - O que há de novo?

Mas, se o banco de dados que você estiver usando tiver suporte a DDL rollbacks em transações, então ele pode
fazer uso deste recurso para desfazer tudo que foi feito antes do erro. O problema é que nem todos os bancos de
dados possuem este recurso. O MySQL, por exemplo não possuí.

Mas o PostgreSQL, SQL Server e outros bancos possuem.

Neste caso o código do Rails foi atualizado para permitir o uso de transações em migrations quando você estiver
usando estes bancos de dados. Embora o Rails permita este recurso, o adapter do banco deve ser atualizado (apenas
fazendo com que o método supports_ddl_transactions? retorne true) para fazer uso de transações. Até o
lançamento deste livro somente o do PostgreSQL parecia ter sido atualizado.

NOVA VERSÃO DESTRUTIVA DOS MÉTODOS DE PESQUISA DO


ACTIVERECORD

Os métodos dinâmicos de pesquisa do ActiveRecord receberam uma versão destrutiva, que dispara um erro do tipo
RecordNotFound caso nenhum registro seja encontrado, ao invés de apenas retornar nil como acontece com a versão
original.

Para usar esta versão destrutiva, basta adicionar o sinal de exclamação no final do método. Veja um exemplo:

Topic.find_by_title!("The First Topic!")


# => ActiveRecord::RecordNotFound

MELHORANDO A PERFORMANCE NOS MÉTODOS ASSOCIATION_IDS

Se você tiver dois modelos: Post e Comment. Onde um post tem muitos (has_many) comentários. Se você executar:

18
Capitulo 1: ActiveRecord

Post.first.comment_ids

O Rails usará a seguinte query para recuperar os ids:

SELECT * FROM `comments` WHERE (`comments`.post_id = 1)

Mas neste caso, não precisamos dos objetos inteiros. A seguinte query seria mais do que suficiente para o
funcionamento deste método, além de possuir uma performance melhor:

SELECT `comments`.id FROM `comments` WHERE (`comments`.post_id = 1)

Tanto para associações has_many, como para associações has_many :through o código do Rails foi alterado para
incluir esta melhora de performance a partir desta versão.

MAIS UM FINDER DINÂMICO

Aumentando o número de finders dinâmicos do ActiveRecord, agora temos o find_last_by. Já tínhamos os famosos
find_by e o find_all_by.

Além de simplificar, ficou muito mais elegante recuperar o último comentário feito por um usuário, por exemplo. Veja:

Comment.find_last_by_author("Carlos Brando")

# é a mesma coisa que

Comment.last(:conditions => { :author => "Carlos Brando" })

19
Ruby on Rails 2.2 - O que há de novo?

SUPORTE À OPÇÃO :LIMIT NO MÉTODO UPDATE_ALL

O método update_all agora também funciona com a opção :limit. Isto é muito bom porque garante que ao usar o
update_all em associações has_many que façam uso da opção :limit tudo funcionará adequadamente.

NOVAS OPÇÕES PARA O MÉTODO COMPOSED_OF

O método composed_of recebeu duas novas opções: :constructor e :converter.

A opção :constructor pode receber um símbolo com o nome de um método ou um Proc. Por padrão, a classe de
composição é criada através do método new, recebendo todos os atributos mapeadas como parâmetros, exatamente
na ordem que em foram mapeados. Se por algum motivo a sua classe não aceitar esta convenção, você deve fazer uso
da opção :constructor. Com ela você pode alterar a forma como sua classe deve ser criada. Veja um exemplo
retirado da própria documentação do Rails:

composed_of :ip_address,
:class_name => 'IPAddr',
:mapping => %w(ip to_i),
:constructor => Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) }

No exemplo, como você pode ver, ao criar uma nova instancia da classe IPAddr é necessário informar mais um
parâmetro ao construtor. Fazendo uso da opção :constructor isto se torna bem simples.

Quanto a opção :converter, ela também aceita um símbolo que represente um método da classe informada na opção
:class_name ou um Proc, e é disparado quando um valor diferente de uma instância da classe informada for passado
para a propriedade criada, o que torna necessário uma conversão. Mais um exemplo:

20
Capitulo 1: ActiveRecord

composed_of :balance,
:class_name => "Money",
:mapping => %w(balance amount),
:converter => Proc.new { |balance| Money.parse(balance) }

No exemplo acima o método balance= sempre estará esperando por uma instancia da classe Money, mas caso um
outro tipo de objeto seja informado ele deverá ser convertido usando o método parse do objeto Money.

Com esta nova opção não devemos mais usar o bloco de conversão que o método permitia antes, a conversão deve
ser feita através do uso da opção :converter.

ATUALIZANDO UMA ASSOCIAÇÃO ATRAVÉS DE SUA FOREIGN KEY

Não sei dizer se isto é um bug ou não, mas na minha opinião isto representava um problema. Veja o código abaixo,
onde tento alterar a conta de um usuário usando sua foreign key em um projeto Rails 2.1 ou anterior:

class User < ActiveRecord::Base


belongs_to :account
end

user = User.first
# => #<User id: 1, login: "admin", account_id: 1>

user.account
# => #<Account id: 1, name: "My Account">

user.account_id = 2
# => 2

21
Ruby on Rails 2.2 - O que há de novo?

user.account
# => #<Account id: 1, name: "My Account">

Note que estou alterando a conta do usuário, mas a associação não foi atualizada. Mesmo depois de salvar o objeto
user, se ele não for recarregado, a associação continuará mostrando a conta errada.

No Rails 2.2 este problema foi corrigido. Veja:

class Comment < ActiveRecord::Base


belongs_to :post
end

comment = Comment.first
# => #<Comment id: 1>

>> comment.post
# => #<Post id: 1>

>> comment.post_id = 2
# => 2

>> comment.post
# => #<Post id: 2>

Veja que ao alterar o post por meio de sua foreign key, automaticamente a associação foi atualizada.

ALIAS_ATTRIBUTE FUNCIONANDO COM DIRTY OBJECTS

Para entender esta alteração, vamos precisar analisar o mesmo código sendo executado em uma versão mais antiga do
Rails e depois nesta nova versão. Vamos pegar um modelo como exemplo:

22
Capitulo 1: ActiveRecord

class Comment < ActiveRecord::Base


alias_attribute :text, :body
end

Note que estou usando o método alias_attribute para criar um alias para o atributo body com o nome de text. Na
teoria este método deveria replicar todos os métodos de leitura, escrita, pesquisa e qualquer outro que envolva o
atributo body. Mas vejamos um exemplo sendo executado no Rails 2.1 ou anterior:

c = Comment.first
# => #<Comment id: 1, body: "my comment">

c.body
# => "my comment"

c.text
# => "my comment"

c.body = "a new message"


# => "a new message"

c.body_changed?
# => true

c.text_changed?
# => NoMethodError: undefined method `text_changed?' ...

Ao executar o método text_changed? temos um erro, porque o alias_attribute não estava replicando os métodos
de rastreamento, mas isto já foi corrigido. Veja o mesmo código executado agora em um projeto Rails 2.2:

c = Comment.first
# => #<Comment id: 1, body: "my comment">

c.body

23
Ruby on Rails 2.2 - O que há de novo?

# => "my comment"

c.text
# => "my comment"

c.body = "a new message"


# => "a new message"

c.body_changed?
# => true

c.text_changed?
# => true

c.text_change
# => ["my comment", "a new message"]

NOVO MÉTODO DE INSTÂNCIA MODEL#DELETE

Para tornar o ActiveRecord mais consistente foi adicionado o método de instância Model#delete. Ele é similar ao
método de classe com o mesmo nome. O método delete, diferente do método destroy, apaga o registro do banco
de dados sem disparar callbacks, como o before_destroy e o after_destroy.

Este método também não aplicará nenhuma das regras impostas na associação através de cláusulas como :dependent.

client = Client.find(1)
client.delete

24
Capitulo 1: ActiveRecord

TORNANDO ATRIBUTOS DO ACTIVERECORD PRIVADOS

No Rails 2.2 você poderá definir atributos do ActiveRecord como private. Como estes atributos são criados via
metaprogramação, até agora isto era impossível.

Para entender como isto funcionará, vamos tornar o atributo name da classe User privado:

class User < ActiveRecord::Base

private
def name
"I'm private"
end

end

Agora ao tentar recuperar o valor do atributo name:

user = User.first
# => #<User id: 1, name: "teste">

user.name
# => NoMethodError: undefined method `NoMethodError' for #<User:0x234df08>

Veja que uma exceção NoMethodError foi disparada ao executar o método que agora é privado. Por outro lado eu
posso alterar o nome do usuário, já que o método name= é ainda público.

user.name = "Carlos"
# => "Carlos"

25
Ruby on Rails 2.2 - O que há de novo?

Capitulo 2

ActiveSupport

ARRAY#SECOND ATÉ ARRAY#TENTH

No objeto Array já tínhamos o método first e last, então porque não ter também os métodos second, third,
fourth e assim por diante? É isso mesmo, foram acrescentados ao objeto Array os métodos que vão do second
(segundo) até o tenth (décimo), que servem para retornar o objeto especifico dentro do Array (o terceiro objeto do
array, por exemplo).

Vamos aos exemplos:

array = (1..10).to_a

array.second # => array[1]


array.third # => array[2]
array.fourth # => array[3]

26
Capitulo 2: ActiveSupport

array.fifth # => array[4]


array.sixth # => array[5]
array.seventh # => array[6]
array.eighth # => array[7]
array.ninth # => array[8]
array.tenth # => array[9]

NOVO MÉTODO ENUMERABLE#MANY?

Um novo método foi adicionado ao módulo Enumerable: many?. E como o nome mesmo diz, ele verifica se a coleção
possui mais de um objeto, ou em outras palavras se tem muitos objetos associados.

Este método é um alias para collection.size > 1. Vamos ver alguns exemplos:

>> [].many?
# => false

>> [ 1 ].many?
# => false

>> [ 1, 2 ].many?
# => true

Além deste formato dado nos exemplos, este método também recebeu uma nova implementação permitindo que ele
aceite blocos, que funciona exatamente como o método any?.

Vamos aos exemplos:

>> x = %w{ a b c b c }
# => ["a", "b", "c", "b", "c"]

27
Ruby on Rails 2.2 - O que há de novo?

>> x.many?
# => true

>> x.many? { |y| y == 'a' }


# => false

>> x.many? { |y| y == 'b' }


# => true

# um outro exemplo...
people.many? { |p| p.age > 26 }

Apenas para relembrar e reforçar, este método só retornará true se mais de um objeto passar nas condições quando
usado o bloco, e quando a coleção tiver mais de um objeto quando usado sem condicionais.

Só uma curiosidade, o método inicialmente se chamaria several?, mas foi alterado para many? depois.

CRIE REGRAS PARA O STRING#HUMANIZE

Já faz um certo tempo que Pratik Naik estava tentando colocar este patch no Rails e parece que finalmente conseguiu.

No arquivo config/initializers/inflections.rb você tem a opção de acrescentar novas inflexões para pluralização,
singularização e outros:

Inflector.inflections do |inflect|
inflect.plural /^(ox)$/i, '\1en'
inflect.singular /^(ox)en/i, '\1'
inflect.irregular 'person', 'people'

28
Capitulo 2: ActiveSupport

inflect.uncountable %w( fish sheep )


end

No Rails 2.2 você também pode incluir inflexões para o método humanize da classe String. Vamos aos famosos
exemplos:

'jargon_cnt'.humanize # => "Jargon cnt"


'nomedojogo'.humanize # => "Nomedojogo"

ActiveSupport::Inflector.inflections do |inflect|
inflect.human(/_cnt$/i, '\1_count')
inflect.human('nomedojogo', 'Nome do Jogo')
end

'jargon_cnt'.humanize # => "Jargon count"


'nomedojogo'.humanize # => "Nome do jogo"

INTRODUZINDO MEMOIZABLE PARA CACHE DE ATRIBUTOS

Performance é coisa séria, e um dos métodos mais usados para aumentar a velocidade de execução em códigos é o
uso de cache. Quem nunca fez algo assim?

class Person < ActiveRecord::Base


def age
@age ||= um_calculo_muito_complexo
end
end

Nesta versão do Rails temos uma forma mais elegante de fazer isto usando o método memoize (é memoize mesmo e
não memorize). Vamos alterar o exemplo acima para funcionar com esta nova funcionalidade:

29
Ruby on Rails 2.2 - O que há de novo?

class Person < ActiveRecord::Base


def age
um_calculo_muito_complexo
end
memoize :age
end

O método age será executado apenas uma vez e o seu retorno será armazenado e retornado em futuras chamadas ao
método.

Só existe uma diferença entre os dois códigos acima. No primeiro, como o método é executado todas as vezes, se o
valor armazenado na variável @age for nil ou false o cálculo (muito complexo) será executado novamente até
termos a idade da pessoa.

No segundo exemplo, o método age só será executado uma vez e o valor retornado será sempre devolvido nas
próximas chamadas, mesmo que seja nil ou false.

Se em algum momento você precisar desligar ou religar o cache em propriedade marcadas com o memoize, você pode
fazer uso dos métodos unmemoize_all e memoize_all.

@person = Person.first

# Para desligar o cache do método age


@person.unmemoize_all

# Para ligar novamente o cache do método age apenas


@person.memoize_all

30
Capitulo 2: ActiveSupport

NOVO MÉTODO OBJECT#PRESENT?

Um novo método foi acrescentado à classe Object. O método present? é o equivalente a !Object#blank?.

Em outras palavras um objeto está presente se ele não for vazio. Mas o que é um objeto vazio?

class EmptyTrue
def empty?() true; end
end

a = EmptyTrue.new
b = nil
c = false
d = ''
e = ' '
g = " \n\t \r "
g = []
h = {}

a.present? # => false


b.present? # => false
c.present? # => false
d.present? # => false
e.present? # => false
f.present? # => false
g.present? # => false
h.present? # => false

Todos estes objetos são vazios ou não estão presentes.

Mas, muito cuidado, algumas pessoas tem confundido as coisas. Veja alguns exemplos de objetos que NÃO estão
vazios, ou seja, estão presentes:

31
Ruby on Rails 2.2 - O que há de novo?

class EmptyFalse
def empty?() false; end
end

a = EmptyFalse.new
b = Object.new
c = true
d = 0
e = 1
f = 'a'
g = [nil]
h = { nil => 0 }

a.present? # => true


b.present? # => true
c.present? # => true
d.present? # => true
e.present? # => true
f.present? # => true
g.present? # => true
h.present? # => true

Qualquer objeto que contenha um valor, está presente, isto vale até mesmo para um Array preenchido com um nil,
porque o Array não está vazio.

STRINGINQUIRER

Uma nova classe foi incluída ao Rails, a classe StringInquirer.

Para entender como funciona, vou ter de explicar usando alguns exemplos. Vamos criar uma classe chamada Cliente
que contém um método que retorna o status do cliente:

32
Capitulo 2: ActiveSupport

class Cliente
def status
"ativo"
end
end

c = Cliente.new
c.status
# => "ativo"

c.status == "ativo"
# => true

c.status == "inativo"
# => false

Ok, até aqui tudo normal. Agora vou modificar a implementação do método status usando a classe StringInquirer,
sempre lembrando que o retorno do método status pode vir de uma coluna do banco de dados (claro), isto é apenas
um exemplo.

class Cliente
def status
ActiveSupport::StringInquirer.new("ativo")
end
end

c = Cliente.new
c.status
# => "ativo"

# Agora vem a grande diferença:


c.status.ativo?
# => true

33
Ruby on Rails 2.2 - O que há de novo?

c.status.inativo?
# => false

Para verificar se o status do cliente é o esperado, ao invés de comparar Strings, eu uso um método com o valor do
status e o sinal de interrogação.

Claro que isto já começou a ser usado no próprio Rails. Por exemplo, caso você precise verificar se o Rails foi
carregado em ambiente de produção, você pode substituir o velho Rails.env == "production", por:

Rails.env.production?

NOVA SINTAXE PARA TESTES

Uma nova forma de se declarar testes foi adicionada ao Rails, usando declarações test/do. Veja:

test "verify something" do


# ...
end

Este é o novo padrão para testes do Rails, veja como ficou um arquivo de teste unitário recém criado nesta versão:

require 'test_helper'

class PostTest < ActiveSupport::TestCase


# Replace this with your real tests.
test "the truth" do
assert true
end
end

34
Capitulo 2: ActiveSupport

A forma convencional, usando métodos, também continuará funcionando, então nossos testes antigos não quebrarão.

LIGANDO E DESLIGANDO CARGA DE DEPENDÊNCIAS

Um novo parâmetro de inicialização foi adicionado ao Rails, a fim de ligar ou desligar a carga de novas classes durante
uma requisição.

config.dependency_loading = true
# ou
config.dependency_loading = false

Se dependency_loading for verdadeiro, durante uma requisição o Rails estará apto a carregar em memória qualquer
classe que não tenha sido inicialmente carregada durante a inicialização do projeto. Caso ele seja falso, estas classes
serão ignoradas.

Se você for executar seu projeto em um ambiente de threads concorrentes deve desabilitar esta opção e carregar
todas estas classes usando eager load ou através do método require na inicialização do sistema.

FILE.ATOMIC_WRITE COPIA AS PERMISSÕES DO ARQUIVO ORIGINAL

Talvez alguns não conheçam o método File.atomic_write. Ele serve para escrever arquivos de forma atômica. Isto
pode ser muito útil em situações onde você não quer que outros processos ou threads vejam um arquivo escrito pela
metade.

File.atomic_write("important.file") do |file|
file.write("hello")
end

35
Ruby on Rails 2.2 - O que há de novo?

O que este método faz é criar um arquivo temporário enquanto você escreve nele, e quando terminar ele substitui o
arquivo antigo pelo novo.

A novidade no Rails 2.2 é que agora este método copia todas as permissões do arquivo original antes de substitui-lo.

CAMELIZE(:LOWER)

Por padrão o método camelize do Rails é usado para converter string para o formato UpperCamelCase. Mas
também podemos converter para o formato lowerCamelCase se usarmos o argumento :lower. Porém, tente
executar o código abaixo no terminal de um projeto Rails (menor ou igual ao 2.1.1):

'Capital'.camelize(:lower)
# => "Capital"

Como você pode ver, a letra ‘C’ no início da palavra não retornou minúscula como deveria. Isto foi corrigido. Veja o
retorno do mesmo trecho de código, agora executado no Rails 2.2:

'Capital'.camelize(:lower)
# => "capital"

TROCA DE BIBLIOTECA GERADORA DE CHAVES SECRETAS

A classe Rails::SecretKeyGenerator, usada para gerar chaves secretas aleatórias como as usadas para armazenar a
sessão do usuário em cookies, foi marcada para ser removida do Rails (Deprecate).

Em seu lugar o Rails passou a usar a nova classe ActiveSupport::SecureRandom que foi feita para o Ruby 1.9. A
biblioteca SecureRandom faz a mesma coisa que a anterior, mas um pouco melhor.

36
Capitulo 2: ActiveSupport

Esta nova biblioteca suporta os seguintes geradores de números aleatórios:

• openssl
• /dev/urandom
• Win32

Vejamos alguns exemplos de chaves geradas com esta nova biblioteca:

# random hexadecimal string.


ActiveSupport::SecureRandom.hex(10) #=> "52750b30ffbc7de3b362"
ActiveSupport::SecureRandom.hex(10) #=> "92b15d6c8dc4beb5f559"
ActiveSupport::SecureRandom.hex(11) #=> "6aca1b5c58e4863e6b81b8"
ActiveSupport::SecureRandom.hex(12) #=> "94b2fff3e7fd9b9c391a2306"
ActiveSupport::SecureRandom.hex(13) #=> "39b290146bea6ce975c37cfc23"

# random base64 string.


ActiveSupport::SecureRandom.base64(10) #=> "EcmTPZwWRAozdA=="
ActiveSupport::SecureRandom.base64(10) #=> "9b0nsevdwNuM/w=="
ActiveSupport::SecureRandom.base64(10) #=> "KO1nIU+p9DKxGg=="
ActiveSupport::SecureRandom.base64(11) #=> "l7XEiFja+8EKEtY="
ActiveSupport::SecureRandom.base64(12) #=> "7kJSM/MzBJI+75j8"
ActiveSupport::SecureRandom.base64(13) #=> "vKLJ0tXBHqQOuIcSIg=="

# random binary string.


ActiveSupport::SecureRandom.random_bytes(10) #=> "\016\t{\370g\310pbr\301"
ActiveSupport::SecureRandom.random_bytes(10) #=> "\323U\030TO\234\357\020\a\337"

37
Ruby on Rails 2.2 - O que há de novo?

NOVO MÉTODO EACH_WITH_OBJECT

O método each_with_object do Ruby 1.9 foi adicionado ao Rails, caso você ainda não esteja usando a nova versão do
Ruby. Este método é bem interessante, pois ele funciona como o conhecido método inject, com um pequeno
diferencial. Cada iteração além de receber um elemento da coleção, recebe também um objeto que chamamos de
memo.

Por exemplo, vamos dizer que eu pretenda preencher um hash com valores de uma coleção:

%w(first second third).each_with_object({}) do |str, hash|


hash[str.to_sym] = str
end
# => {:second=>"second", :first=>"first", :third=>"third"}

Note que no exemplo acima o memo é um hash vazio ({}). Dentro do bloco eu preencho este hash com os itens da
minha coleção.

Apenas um alerta: Não podemos usar objetos imutáveis como memo, tais como números, true e false. No exemplo
abaixo o retorno sempre será 1, já que o número um não pode ser alterado:

(1..5).each_with_object(1) { |value, memo| memo *= value } # => 1

INFLECTOR#PARAMETERIZE SIMPLIFICA A CRIAÇÃO DE URLS ELEGANTES

Um novo inflector foi incluído no Rails, e particularmente acho este é muito útil. O parameterize transforma um
texto qualquer em um formato ideal para o uso em URLs. Por exemplo:

Model:

38
Capitulo 2: ActiveSupport

class User
def to_param
"#{id}-#{name.parameterize}"
end
end

Controller:

@user = User.find(1)
# => #<User id: 1, name: "Carlos E. Brando">

View:

link_to @user.name, user_path


# => <a href="/users/1-carlos-e-brando">Carlos E. Brando</a>

Um fato interessante é que logo de inicio a implementação feita não aceitava o uso de caracteres acentuados, o que
significava um problema para muita gente ao redor do mundo, inclusive nós brasileiros. Um dia depois da primeira
implementação, Michael Koziarski salvou nossas vidas incluindo este suporte. Mesmo assim o código ainda não estava
perfeito, então decidiu-se adaptar o código do ótimo plugin slugalizer criado por Henrik Nyh. Agora sim, ficou
perfeito!

Para aqueles que ainda não estão fazendo uso do Rails 2.2, o plugin slugalizer resolve o problema.

39
Ruby on Rails 2.2 - O que há de novo?

TRÊS NOVOS MÉTODOS PARA CLASSES QUE TRABALHAM COM DATAS E


HORAS

As classes Time, Date, DateTime e TimeWithZone receberam três novos métodos muito convenientes. Os métodos
today?, past? e future? foram introduzidos em todas as classes que trabalham com datas e horas para facilitar nossa
vida em algumas situações.

Acredito que não seja necessário explicar o funcionamento de cada um. Então vejamos os métodos em ação:

date = Date.current
# => Sat, 04 Oct 2008

date.today?
# => true

date.past?
# => false

date.future?
# => false

time = Time.now
# => Sat Oct 04 18:36:43 -0300 2008

time.today?
# => true

40
Capitulo 2: ActiveSupport

ALTERADA A MENSAGEM DO MÉTODO ASSERT_DIFFERENCE

Quando usávamos o método assert_difference com múltiplas expressões e um erro ocorria era difícil de saber qual
das expressões não teve seu valor alterado, já que a mensagem de erro não informava isto.

No Rails 2.2 a mensagem devolvida pelo método informará exatamente qual expressão não passou no teste. Por
exemplo:

assert_difference ['Post.count', 'current_user.posts.count'] do


Post.create(params)
end

O código acima retornará a seguinte mensagem, caso a segunda expressão não tenha passado:
"<current_user.posts.count> was expression that failed. expected but was .".

ADICIONANDO UM PREFIXO AOS DELEGATES

O método Module#delegate agora possui uma nova opção :prefix que pode ser atribuída caso você queira que o
nome da classe alvo seja usado como prefixo antes do nome do método. Vejamos dois exemplos, primeiro da forma
comum:

class Invoice < ActiveRecord::Base


delegate :street, :city, :name, :to => :client
end

Invoice.new.street
Invoice.new.city
Invoice.new.name

41
Ruby on Rails 2.2 - O que há de novo?

Agora usando a opção :prefix:

class Invoice < ActiveRecord::Base


delegate :street, :city, :name, :to => :client, :prefix => true
end

Invoice.new.client_street
Invoice.new.client_city
Invoice.new.client_name

Você também pode personalizar, atribuindo um outro nome ao prefixo:

class Invoice < ActiveRecord::Base


delegate :street, :city, :name, :to => :client, :prefix => :customer
end

Invoice.new.customer_street
Invoice.new.customer_city
Invoice.new.customer_name

42
Capitulo 3: ActiveResource

Capitulo 3

ActiveResource

RECUPERANDO O ÚLTIMO REGISTRO DO ACTIVERESOURCE

Seguindo o padrão do ActiveRecord, também foi adicionado ao método find do ActiveResource a opção :last.

Person.find(:last, :from => :managers)


# => GET /people/managers.xml

Desta forma podemos recuperar o último recurso encontrado.

ACTIVERESOURCE::BASE #TO_XML E #TO_JSON

Mais dois novos métodos foram acrescentados ao ActiveResource::Base: to_xml e to_json. O primeiro converte o
recurso em uma string XML e o segundo retorna também uma string, mas no formato JSON representando o modelo.

43
Ruby on Rails 2.2 - O que há de novo?

O método to_json pode ser configurado através de um parâmetro opcional com as opções :only e :except,
permitindo restringir ou remover determinados atributos. Por exemplo:

person = Person.new(:first_name => "Carlos", :last_name => "Brando")


person.to_json
# => {"first_name": "Carlos", "last_name": "Brando"}

person.to_json(:only => ["first_name"])


# => {"first_name": "Carlos"}

person.to_json(:except => ["first_name"])


# => {"last_name": "Brando"}

44
Capitulo 4: ActionPack

Capitulo 4

ActionPack

NOVA OPÇÃO :LAYOUT NO MÉTODO CACHES_ACTION

Foi acrescentado a opção :layout no método caches_action.

class ListsController < ApplicationController


...

caches_action :index, :layout => false

...
end

No exemplo acima eu especifiquei :layout => false o que significa que o layout não será armazenado no cache,
apenas o conteúdo da action será. Isto é muito útil quando temos conteúdo dinâmico no layout (o que acontece na
maioria dos casos).

45
Ruby on Rails 2.2 - O que há de novo?

Se você não especificar nada, ele assumirá o padrão atual, que é true.

TEMPLATES EM CACHE

Para melhorar a performance do Rails as páginas de template serão armazenadas em cache automaticamente em
ambiente de produção.

Em outras palavras, se você alterar algum template em produção terá de reiniciar o servidor para que isto tenha efeito.

ALTERAÇÃO NO MÉTODO CONCAT

Se você tem o costume de evitar repetições em suas views criando helpers, com certeza já usou o método concat. Se
você nunca usou este método, saiba que ele é como o puts para uma view.

A implementação atual do método recebe dois parâmetros, uma String com o texto que será exibido na view e um
segundo chamado binding. Acontece que devido a melhorias no código, embora ele ainda espere estes dois
parâmetros, o binding não é mais necessário. Este parâmetro foi deprecado, ou seja, se você estiver rodando o seu
projeto sob o Rails 2.2, receberá uma mensagem de alerta quando este método for executado. Em uma futura versão
do Rails, este parâmetro será removido.

A mensagem de alerta exibida é a seguinte: "The binding argument of #concat is no longer needed. Please remove it
from your views and helpers".

46
Capitulo 4: ActionPack

MÉTODO LINK_TO COM BLOCOS

O método link_to recebeu uma atualização que permite seu uso com blocos. Isto é interessante para os casos onde
temos textos muito longos no hyperlink. Por exemplo, se hoje fazemos assim:

<%= link_to "<strong>#{@profile.name}</strong> -- <span>Check it out!!</span>", @profile %>

Agora podemos fazer assim, que teremos o mesmo resultado:

<% link_to(@profile) do %>


<strong><%= @profile.name %></strong> -- <span>Check it out!!</span>
<% end %>

Não é uma mudança significativa em funcionalidade, mas permite deixar o código mais legivel, e isto também é
importante.

RJS#PAGE.RELOAD

O método reload foi incluído ao ActionView::Helpers::PrototypeHelper para ser usado em templates .rjs ou
blocos render(:update). Este método força a recarga da página atual no browser usando javascript. Em outras
palavras é um atalho para o já muito usado window.location.reload();.

Veja como usar:

respond_to do |format|
format.js do
render(:update) { |page| page.reload }
end
end

47
Ruby on Rails 2.2 - O que há de novo?

DANDO UM NOME PARA A VARIÁVEL LOCAL DE UMA COLEÇÃO DE


PARTIALS

No código abaixo estamos usando uma partial com uma coleção de dados:

render :partial => "admin_person", :collection => @winners

Dentro da partial podemos usar então a variável admin_person para acessar os itens da coleção. Mas temos de
concordar que este nome de variável é meio ruim.

Agora temos a opção de personalizar o nome desta variável usando a opção :as. Vamos alterar o exemplo acima:

render :partial => "admin_person", :collection => @winners, :as => :person

Agora podemos acessar cada item da coleção usando a variável person que tem um nome mais intuitivo.

PARTIALS NÃO VÃO MAIS DEFINIR AS VARIÁVEIS LOCAIS


IMPLICITAMENTE

No exemplo abaixo estou renderizando uma partial, e não estou informando qual variável ela deve usar para preencher
o conteúdo. Hoje o Rails encara que como tenho uma variável de instância com o mesmo nome, implicitamente é esta
que deve ser usada.

@customer = Customer.new("Carlos Brando")


render :partial => "customer"

48
Capitulo 4: ActionPack

Isto funciona mas é um pouco arriscado. A partir do Rails 2.2, esta funcionalidade continua funcionando mas sempre
emitindo uma aviso de que será por pouco tempo:

"@customer will no longer be implicitly assigned to customer"

Sim, este recurso será removido do Rails numa futura versão.

POLYMORPHIC_URL AGORA É CAPAZ DE LIDAR COM RECURSOS


SINGLETON

Até agora o helper polymorphic_url não estava tratando singleton resources corretamente.

Um novo patch foi incluído no Rails para permitir que especifiquemos um singular resource usando símbolos, assim
como fazemos com namespaces. Exemplo:

# este código
polymorphic_url([:admin, @user, :blog, @post])

# é a mesma coisa que


admin_user_blog_post_url(@user, @post)

SUPORTE A EXPRESSÕES REGULARES NO TIME_ZONE_SELECT

Na classe TimeZone do ActiveSupport existe o método us_zones que convenientemente retorna uma lista dinâmica
com todos os fusos-horários americanos.

49
Ruby on Rails 2.2 - O que há de novo?

O problema é que em alguns casos vamos desenvolver software para pessoas em outros países, mas não existe um
método tão conveniente assim que liste os fusos-horários destes países.

Houve uma longa discussão sobre criar ou não métodos como african_zones, american_zones, etc.. No fim
prevaleceu o seguinte:

Foi implantado no objeto TimeZone o suporte para =~ afim de ajudar a montar uma lista de fusos-horários a partir de
uma expressão regular. Além disso o método time_zone_select foi alterado para trabalhar em conjunto com esta
novidade.

Agora podemos fazer isto:

<%= time_zone_select( "user", 'time_zone', /Australia/) %>

O código acima retornará todos os fusos-horários, mas colocará no topo da lista os seguintes:

(GMT +08:00) Perth


(GMT +09:30) Adelaide
(GMT +09:30) Darwin
(GMT +10:00) Brisbane
(GMT +10:00) Canberra
(GMT +10:00) Hobart
(GMT +10:00) Melbourne
(GMT +10:00) Sydney

Para aprender mais sobre TimeZones, aconselho assistir ao episódio 106 do RailsCasts (http://railscasts.com/episodes/
106) e dar uma olhada no livro antecessor a este.

50
Capitulo 4: ActionPack

RENDER :TEMPLATE AGORA ACEITA :LOCALS

Os métodos render :action e render :partial permitem que passemos um Hash através da opção :locals com
dados para serem processados por eles, mas o render :template não permitia.

Nesta versão do Rails isto irá funcionar também:

render :template => "weblog/show", :locals => {:customer => Customer.new}

:USE_FULL_PATH DO MÉTODO RENDER NÃO EXISTIRÁ MAIS


render :file => "some/template", :use_full_path => true

A opção :use_full_path do método render não existe mais no Rails 2.2. Não é algo sério já que ela nem mesmo era
necessária.

DEFINE_JAVASCRIPT_FUNCTIONS

Mais um método que deixa de existir no Rails: define_javascript_functions.

Faz sentido já que métodos como javascript_include_tag e outros realizam a mesma tarefa de uma forma melhor.

DESABILITANDO O CABEÇALHO ACCEPT EM REQUISIÇÕES HTTP

Vamos iniciar com um código de exemplo:

51
Ruby on Rails 2.2 - O que há de novo?

def index
@people = Person.find(:all)

respond_to do |format|
format.html
format.xml { render :xml => @people.to_xml }
end
end

No exemplo acima o Rails tem duas formas de identificar qual é o formato que deve ser usado no bloco respond_to.
A primeira e mais comum é através do formato informado na URL (/index.xml, por exemplo) e a segunda forma para
o caso de o formato não ter sido especificado é examinando o cabeçalho Accept da requisição HTTP.

Para quem não sabe o cabeçalho Accept é aquele que informa o tipo do documento desejado em strings mais ou
menos assim:

Accept: text/plain, text/html

Accept: text/x-dvi; q=.8; mxb=100000; mxt=5.0, text/x-c

# recuperando esta informação via código


@request.env["HTTP_ACCEPT"] = "text/javascript"

Para ver uma lista dos tipos mais comuns acesse a endereço: http://www.developershome.com/wap/detection/
detection.asp?page=httpHeaders

Acontece que este cabeçalho é porcamente implementado na maioria dos browsers. E quando ele é usado em sites
públicos algumas vezes estranhos erros acontecem quando robôs indexadores fazem seus requests.

52
Capitulo 4: ActionPack

Assim, tomou-se a decisão de desativar este cabeçalho por padrão. É sempre uma melhor opção utilizar URLs
formatadas para indicar o formato desejado. Se precisar do cabeçalho Accept, você deve habilitá-lo de volta, para isto
basta incluir a seguinte linha no arquivo environment.rb:

config.action_controller.use_accept_header = false

Quando desligado, caso o formato não seja informado na URL o Rails assumirá que deve usar o format.html.

Existe um caso especial quando fazemos requisições usando ajax se o cabeçalho X-Requested-With estiver
presente. Neste caso o formato format.js ainda será usado mesmo que o formato não tenha sido especificado
(/people/1, por exemplo).

BUTTON_TO_REMOTE

Se você já usa Rails há um tempo, com certeza conhece o método submit_to_remote, certo? Este método retorna um
botão HTML preparado para enviar o formulário via XMLHttpRequest.

No Rails 2.2 este método foi renomeado para button_to_remote, para manter uma certa consistência com o nome do
seu método irmão o link_to_remote.

Aqueles que irão migrar seus projetos não precisam se preocupar já que o nome antigo ( submit_to_remote) será um
alias para novo.

53
Ruby on Rails 2.2 - O que há de novo?

RECEBENDO ALERTAS PARA MELHORIA DE DESEMPENHO

O Rails recebeu mais um parâmetro de configuração que faz com que ele emita um alerta caso esteja renderizando um
template fora do diretório especificado para views. Isto é muito bom já que arquivos fora dos diretórios especificados
para views não são armazenados em cache, o que resulta em mais operações no disco.

Para começar a receber os avisos basta incluir a seguinte linha no arquivo environment.rb do projeto:

config.action_view.warn_cache_misses = true

Fazendo isto a seguinte mensagem aparecerá caso algum arquivo fora dos diretórios configurados seja renderizado:

[PERFORMANCE] Rendering a template that was not found in view path. Templates outside the view path
are not cached and result in expensive disk operations. Move this file into /Users/user/project/app/
views or add the folder to your view path list

Esta configuração está desligada como padrão.

OPÇÃO :INDEX FUNCIONANDO NO FIELDS_FOR

Muitas vezes a opção :index do método select pode ser útil, como por exemplo quando se precisa criar diversos
dropdowns dinamicamente em uma página.

Até agora o método fields_for não repassava esta opção para métodos como select, collection_select,
country_select e time_zone_select, o que limitava o seu uso em determinados casos.

54
Capitulo 4: ActionPack

Isto já foi corrigido nesta versão do Rails. Por exemplo, apenas para efeito de teste veja o código abaixo e o seu
retorno:

fields_for :post, @post, :index => 108 do |f|


concat f.select(:category, %w( abe <mus> hest))
end

Isto retornará:

<select id=\"post_108_category\" name=\"post[108][category]\">


<option value=\"abe\">abe</option>
<option value=\"&lt;mus&gt;\" selected=\"selected\">&lt;mus&gt;</option>
<option value=\"hest\">hest</option>
</select>

Veja que estou usando a opção :index => 108 no método fields_for. Agora repare as propriedades id e name da
tag criada pelo método select. Embora nada tenha sido especificado para este método, ele acrescenta o indice em seu
resultado também.

MELHORANDO A PERFORMANCE USANDO ETAGS

Antes de começar a explicar este novo recurso, deixe-me tentar explicar o que são ETags (Entity Tags). Etags seriam
de uma forma grosseira identificadores associados a cada recurso para determinar se o arquivo que está no servidor é
o mesmo que está no cache do browser. No caso, o recurso seria uma página em HTML, mas também poderia ser um
XML ou JSON.

Cabe ao servidor a responsabilidade de verificar se o recurso solicitado é igual dos dois lados. Caso o servidor
confirme que o recurso armazenado no cache do browser do usuário é exatamente o mesmo que seria enviado de

55
Ruby on Rails 2.2 - O que há de novo?

volta para ele, ao invés de devolver todo o conteúdo do recurso novamente ele apenas retorna um status 304 (Not
Modified) e o browser usará o que está em seu cache.

Servidores Web como o Apache e o IIS já sabem fazer isto para página estáticas. Mas quando o conteúdo é dinâmico,
como na maioria das páginas de um projeto Ruby on Rails, a responsabilidade é todo nossa.

Dois novos atributos foram adicionados ao objeto response, são o last_modified e o etag. Quando um valor é
atribuído a estes atributos eles são automaticamente repassados aos cabeçalhos HTTP_IF_MODIFIED_SINCE e
HTTP_IF_NONE_MATCH respectivamente. Quando uma nova requisição (request) deste recurso for feita ela retornará
com estes cabeçalhos permitindo que você possa comparar com o valor atual e determinar se o que está no cache do
usuário é uma versão recente ou antiga do conteúdo do recurso. Caso a versão do usuário seja a mais recente, ao
invés de renderizar o recurso novamente, você pode simplesmente enviar um status "304 Not Modified" solicitando ao
browser que use a versão armazenada em seu cache.

Para realizar esta operação temos dois métodos que podemos usar dependendo da situação: stale? e fresh_when.

Vejamos um exemplo:

class ArticlesController < ApplicationController


def show
@article = Article.find(params[:id])

if stale?(:last_modified => @article.published_at.utc, :etag => @article)


respond_to do |wants|
wants.html
wants.xml { render :xml => @article }
end
end
end
end

56
Capitulo 4: ActionPack

No exemplo acima, se no cabeçalho do request os valores forem diferentes dos atribuídos ao método stale? significa
que a versão no cache do usuário não é recente, então o bloco respond_to é disparado e os valores informados ao
método são atribuídos aos atributos last_modified e etag do objeto response.

Se os valores forem iguais, quer dizer que a versão no cache do usuário é a mais recente. Então o bloco respond_to
não é disparado e apenas um status "304 Not Modified" é devolvido.

Ainda temos o método fresh_when que é uma versão simplificada do método stale?. Veja um exemplo:

def show
@article = Article.find(params[:id])
fresh_when(:etag => @article, :last_modified => @article.created_at.utc)
end

Basicamente este método faz o seguinte: Ele atribui os valores informados aos seus respectivos atributos no objeto
response e verifica se eles são iguais aos enviados no objeto request, se forem diferentes (stale) então ele renderizará
o template padrão da action. Caso os atributos sejam iguais em ambos os objetos (fresh), então ele apenas retornará
um status "304 Not Modified" no lugar de renderizar o template.

Em algumas situações talvez seja necessário informar um Array à opção :etag, como no exemplo abaixo:

fresh_when(:etag => [@article, current_user], :last_modified => @article.created_at.utc)

# ou

if stale?(:last_modified => @article.published_at.utc, :etag => [@article, current_user])


# ...
end

57
Ruby on Rails 2.2 - O que há de novo?

ALTERAÇÃO NA ASSINATURA DO MÉTODO NUMBER_WITH_DELIMITER

O método number_with_delimiter recebeu uma nova implementação. Além de uma melhora no código para que
fique mais limpo a assinatura do método também mudou. Veja a antiga:

def number_with_delimiter(number, delimiter=",", separator=".")

# Exemplos de uso
number_with_delimiter(12345678) # => 12,345,678
number_with_delimiter(12345678.05) # => 12,345,678.05
number_with_delimiter(12345678, ".") # => 12.345.678
number_with_delimiter(98765432.98, " ", ",")

E a nova:

def number_with_delimiter(number, *args)

# Exemplos de uso
number_with_delimiter(12345678) # => 12,345,678
number_with_delimiter(12345678.05) # => 12,345,678.05
number_with_delimiter(12345678, :delimiter => ".") # => 12.345.678
number_with_delimiter(12345678, :seperator => ",") # => 12,345,678
number_with_delimiter(98765432.98, :delimiter => " ", :separator => ",")

Então atenção, agora ao usarmos o método number_with_delimiter devemos informar as opções na chamada do
método.

58
Capitulo 4: ActionPack

EXECUTANDO MÚLTIPLAS INSTÂNCIAS DE UM PROJETO EM


SUBDIRETÓRIOS

Às vezes você tem de rodar múltiplas cópias do mesmo projeto. Talvez você tenha um produto que será usado por
vários clientes, ou talvez você apenas deseje rodar uma versão de teste e produção do seu software ao mesmo tempo.

A forma mais simples de se fazer isto é ter múltiplos (sub)domínios com uma instância do aplicativo em cada uma. Mas
se isto não for possível você pode colocar seu aplicativo em um subdiretório e usar um prefixo na sua URL para
distinguir as instâncias do seu software. Por exemplo, você poderia rodar vários blogs de usuários diferentes usando
URLs como:

• http://www.nomedojogo.com/fulano/blog
• http://www.nomedojogo.com/sicrano/blog
• http://www.nomedojogo.com/beltrano/blog

Nestes casos, os prefixos fulano, sicrano e beltrano identificarão as instâncias do aplicativo rodando em
subdiretórios com os mesmos nomes. O roteamento do aplicativo começa depois disto. Você pode dizer ao Rails para
ignorar esta parte das URLs quando uma requisição for feita, mas colocá-la nas URLs geradas por ele, configurando isto
através da constante RAILS_RELATIVE_URL_ROOT ou do método AbstractRequest.relative_url_root.

Se seu projeto Rails estiver rodando sob o Apache, esse recurso já é ativado automaticamente. Por isso na maioria dos
casos não temos de nos preocupar em configurar isto hoje. Isto se você estiver usando Apache.

Porém, no Rails 2.2 o relative_url_root não será mais configurado automaticamente pelo HTTP header. Teremos
de fazer isto manualmente, colocando uma linha mais ou menos assim no arquivo environment.rb de cada um dos
aplicativos:

config.action_controller.relative_url_root = "/fulano"

59
Ruby on Rails 2.2 - O que há de novo?

Feito isto, seu aplicativo irá ignorar o prefixo "fulano" logo depois do domínio. Mas ao gerar URLs ele sempre colocará
este prefixo para garantir que você estará acessando o projeto no subdiretório correto.

ALTERAÇÃO NO MÉTODO ERROR_MESSAGE_ON

O método error_message_on é extremamente útil. Com ele podemos exibir mensagens de erro retornadas por
determinados métodos em um objeto de uma forma bem simples.

<%= error_message_on "post", "title" %>

<!-- ou -->

<%= error_message_on @post, "title" %>

Isto fará com que uma mensagem de erro seja exibida na sua página dentro de uma tag DIV, caso um erro esteja
associado ao campo title do modelo post.

Mas o mais interessante do método error_message_on é que podemos personaliza-lo para que exibida mensagens
mais amigáveis. E é aqui que entra a alteração para o Rails 2.2.

Na versão atual os parâmetros de personalização são passadas diretamente para o método, mas no Rails 2.2 serão
passadas via um Hash de opções:

<%= error_message_on "post", "title",


:prepend_text => "Title simply ",
:append_text => " (or it won't work).",
:css_class => "inputError" %>

60
Capitulo 4: ActionPack

Fique tranqüilo quanto a uma possível migração de seus projetos atuais, pois o código está preparado para funcionar
também da forma antiga (pelo menos por um tempo), mas emitindo um aviso de alerta para que o código seja
atualizado.

MAIS MÉTODOS ATUALIZADOS PARA RECEBER HASHES DE OPÇÕES

Os seguintes métodos também foram alterados para aceitarem seus argumentos na forma de um Hash de opções,
tornando o código mais legível e facilitando a manutenção.

truncate
truncate("Once upon a time in a world far far away")
# => Once upon a time in a world f...

truncate("Once upon a time in a world far far away", :length => 14)
# => Once upon a...

truncate("And they found that many people were sleeping better.",


:omission => "... (continued)", :length => 15)
# => And they found... (continued)

highlight
highlight('You searched for: rails', ['for', 'rails'], :highlighter => '<em>\1</em>')
# => You searched <em>for</em>: <em>rails</em>

highlight('You searched for: rails', 'rails', :highlighter => '<a href="search?q=\1">\1</a>')


# => You searched for: <a href="search?q=rails">rails</a>

61
Ruby on Rails 2.2 - O que há de novo?

excerpt
excerpt('This is an example', 'an', :radius => 5)
# => ...s is an exam...

excerpt('This is an example', 'is', :radius => 5)


# => This is a...

excerpt('This next thing is an example', 'ex', :radius => 2)


# => ...next...

excerpt('This is also an example', 'an', :radius => 8, :omission => '<chop> ')
# => <chop> is also an example

word_wrap
word_wrap('Once upon a time', :line_width => 8)
# => Once upon\na time

word_wrap('Once upon a time', :line_width => 1)


# => Once\nupon\na\ntime

auto_link
post_body = "Welcome to my blog at http://www.nomedojogo.com/. Please e-mail me at me@email.com."

auto_link(post_body, :urls)
# => "Welcome to my blog at
<a href=\"http://www.nomedojogo.com/\">http://www.nomedojogo.com</a>.
Please e-mail me at me@email.com."

auto_link(post_body, :all, :target => "_blank")

62
Capitulo 4: ActionPack

# => "Welcome to my blog at


<a href=\"http://www.nomedojogo.com/\" target=\"_blank\">http://www.nomedojogo.com</a>.
Please e-mail me at <a href=\"mailto:me@email.com\">me@email.com</a>."

auto_link(post_body, :link => :all, :html => {:target => "_blank"})


# => "Welcome to my blog at
<a href=\"http://www.nomedojogo.com/\" target=\"_blank\">http://www.nomedojogo.com</a>.
Please e-mail me at <a href=\"mailto:me@email.com\">me@email.com</a>."

Todos os métodos continuam funcionando da forma antiga por enquanto, mas alertas serão emitidos no terminal para
lembra-lo de atualizar seu código o mais rápido possível.

AUMENTANDO AS POSSIBILIDADES COM PARTIALS

Vamos pegar o seguinte exemplo:

<!-- Arquivo _layout.html.erb -->


inicio
<%= yield %>
fim

<!-- uma view qualquer -->


<%= render :layout => 'layout' do %>
meio
<% end %>

O resultado deste código seria:

inicio
meio
fim

63
Ruby on Rails 2.2 - O que há de novo?

No exemplo acima estou incluindo uma partial dentro da minha view e usando o método yield para personalizar o
conteúdo, que é passado dentro de um bloco através do método render.

Porém, até agora você não podia passar nenhum argumento dentro do bloco. No Rails 2.2. isto será possível. Você
poderá fazer coisas realmente legais. Veja um exemplo usando uma coleção de livros:

<!-- app/views/books/_book.html.erb -->


<div class="book">
Price: $<%= book.price %>
<%= yield book %>
</div>

<!-- app/views/books/index.html.erb -->


<% render :layout => @books do |book| %>
Title: <%= book.title %>
<% end %>

Isto devolveria algo como:

<div class="book">
Price: $29.74
Title: Advanced Rails
</div>

Veja que dentro do bloco estou informando o título do livro, mas em uma outra view eu poderia informar também a
quantidade em estoque ou outras informações relevantes, sempre usando a mesma partial.

Você também poderia usar a mesma partial várias vezes na mesma view e usar blocos para diferenciar as seções
dentro da página, por exemplo:

64
Capitulo 4: ActionPack

<!-- app/views/books/_book.html.erb -->


<div class="book">
<%= yield book, :header %>
Price: $<%= book.price %>
<%= yield book, :footer %>
</div>

<!-- app/views/books/index.html.erb -->


<% render :layout => @books do |book, section| %>
<%- case section when :header -%>
Title: <%= book.title %>
<%- when :footer -%>
(<%= book.reviews.count %> customer reviews)
<%- end -%>
<% end %>

RECUPERANDO A STRING ATUAL DO MÉTODO CYCLE

Muito provavelmente você já conhece o método cycle. Ele é muito usado para alternar as cores de linhas em um
tabela, alterando a propriedade class de cada row.

@items = [1,2,3,4]

<table>
<% @items.each do |item| %>
<tr class="<%= cycle("even", "odd") -%>">
<td>item</td>
</tr>
<% end %>
</table>

65
Ruby on Rails 2.2 - O que há de novo?

Um novo método foi criado para auxiliar o uso do método cycle em tabelas mais complexas ou outros tipos de
design onde se faça necessário recuperar a string corrente desde a última execução do método cycle. Veja um
exemplo similar ao mostrado acima usando o novo método current_cycle:

@items = [1,2,3,4]

<% @items.each do |item| %>


<div style="background-color:<%= cycle("red", "white", "blue") %>">
<span style="background-color:<%= current_cycle %>"><%= item %></span>
</div>
<% end %>

EVITANDO FEEDS DUPLICADOS

Algumas vezes você assina o feed de um blog e de repente se depara com uma série de posts que já foram lidos
aparecendo como se fossem novos no seu Google Reader. Já aconteceu com você?

Isto pode acontecer por vários motivos, mas não seria legal deixar isto acontecer com os assinantes do seu feed,
correto?

Para ajudar-nos a evitar este tipo de constrangimento, cada entrada e o próprio feed criado pelo builder atom_feed
receberam uma nova opção chamada :id, que permite a personalização do id.

atom_feed({ :id => 'tag:nomedojogo.com,2008:test/' }) do |feed|


feed.title("My great blog!")
feed.updated((@posts.first.created_at))

for post in @posts


feed.entry(post, :id => "tag:nomedojogo.com,2008:" + post.id.to_s) do |entry|
entry.title(post.title)

66
Capitulo 4: ActionPack

entry.content(post.body, :type => 'html')

entry.author do |author|
author.name("Carlos Brando")
end
end
end
end

Fazendo desta forma, mesmo que você tenha de reescrever algum trecho do código que gera os feeds, ou fazer alguma
grande alteração no conteúdo do seu site, o id criado para aquela entrada será sempre o mesmo fazendo com que o
leitor de feeds não duplique as entradas antigas.

Seus leitores agradecem.

ADICIONANDO INSTRUÇÕES DE PROCESSAMENTO EM DOCUMENTOS


XML

Uma nova opção foi incluida ao método atom_feed, agora podemos incluir instruções de processamento ao xml. Veja
um exemplo:

atom_feed(:schema_date => '2008', :instruct => {


'xml-stylesheet' => {
:href=> 't.css', :type => 'text/css'
}
}) do |feed|

# ...

end

67
Ruby on Rails 2.2 - O que há de novo?

Instruções de processamento em arquivos XML são informações contidas no documento XML que serão repassadas
para o aplicativo que o requisitou. Essas instruções são na maioria das vezes usadas para informar ao aplicativo como
ele deve manipular os dados que estão no documento XML.

No exemplo acima estou dizendo ao aplicativo que recebe o XML que ele deve exibi-lo com uma folha de estilo (CSS)
especifico. Veja como fica no XML:

<?xml-stylesheet type="text/css" href="t.css"?>

SIMPLIFICANDO O ACESSO AOS RESOURCES

Rotas aninhadas já não é mais novidade. Ao configurar nossas rotas é comum codificarmos algo mais ou menos assim:

map.resources :users do |user|


user.resources :posts do |post|
post.resources :comments
end
end

No código acima estou deixando claro que meus usuários tem posts, que por sua vez tem comentários. Da forma
como minhas rotas estão configuradas posso recuperar os posts de um usuário através da url '/users/1/posts'. Ou
recuperar os comentários de um determinado post através da url '/users/1/posts/5/comments'.

Com a nova opção :shallow => true, ganhamos mais flexibilidade. Note que ao adicionar esta opção no primeiro
resource, todos os outros que estiverem abaixo dele herdarão esta característica.

map.resources :users, :shallow => true do |user|


user.resources :posts do |post|
post.resources :comments

68
Capitulo 4: ActionPack

end
end

Com esta opção habilitada eu posso continuar recuperando dados da mesma forma como fazia antes. A vantagem é
que agora eu também posso recuperar todas os comentários de um post sem precisar informar o usuário, através da
url '/posts/2/comments'. Ou recuperar um determinado post usando apenas a url '/posts/2'.

A opção :shallow também funciona em recursos que estão usando as notações has_many ou has_one, por exemplo:

map.resources :users, :has_many => { :posts => :comments }, :shallow => true

Todos os helpers para acessar as rotas diretamente também são criados, como:

user_posts_path(@user) # => '/users/1/posts'


posts_path # => '/posts'
post_comments_path(@post) # => /posts/5/comments

USANDO ARRAYS PARA DEFINIR OS VERBOS DE MEMBROS E COLEÇÕES


DE ROTAS

Agora podemos fornecer um array de métodos para novos membros ou coleções de rotas. Isso remove o problema
de termos que definir uma rota aceitando qualquer verbo (:any) quando na verdade apenas precisávamos que ele
respondesse a mais de um. No Rails 2.2 podemos declarar uma rota desta maneira:

map.resources :photos, :collection => { :search => [:get, :post] }

69
Ruby on Rails 2.2 - O que há de novo?

POLYMORPHIC ROUTES

Os métodos *_polymorphic_url e *_polymorphic_path, muito usados para gerar URLs a partir de registros do banco
de dados, receberam um novo parâmetro opcional. Agora, além dos parâmetros normais eles também aceitam um
hash de opções, tornando possível gerar rotas com parâmetros adicionais na url.

Vamos aos exemplos, com o método equivalente nos comentários:

edit_polymorphic_url(@article, :param1 => '10')


# => edit_article_url(@article, :param1 => '10')

polymorphic_url(@article, :param1 => '10')


# => article_url(@article, :param1 => '10')

polymorphic_url(@article, :format => :pdf, :param1 => '10')


# => formatted_article_url(@article, :pdf, :param1 => '10')

COUNTRY_SELECT REMOVIDO DO RAILS

O helper country_select foi removido do Rails. Para quem não se lembra, este método retorna uma lista com todos
os países do mundo.

O motivo deste método ter sido removido do Rails é que ele foi atualizado para utilizar a norma ISO 3166 para os
nomes dos países. O problema todo é que Taiwan, segundo a norma ISO 3166, se chama na verdade "Taiwan, Province
of China". E foi exatamente assim que Michael Koziarski deixou no método.

70
Capitulo 4: ActionPack

Então, Jamis Buck questionou se não seria possível deixar apenas "Taiwan", já que o "Province of China" parece ser
politicamente agressivo. No GitHub iniciou-se então uma série de comentários que foram entrando cada vez mais em
questões políticas e deixando o técnico totalmente de lado.

Mas, Michael Koziarski foi categórico em afirmar que essas questões políticas estão muito além do que poderíamos
resolver com uma simples alteração no código. E se aceitasse esta alteração, logo outras seriam solicitadas para países
como Kosovo, Ossétia do Sul, Abecásia, Transnístria e uma longa lista.

A melhor solução, ou pelo menos a que geraria menos controvérsias, foi remover o helper do Rails e disponibiliza-lo
na forma de um plugin. Desta forma qualquer um poderia facilmente criar um fork e montar sua própria lista da forma
como mais lhe agradar.

Para instalar o plugin oficial:

./script/plugin install git://github.com/rails/country_select.git

BENCHMARKING REPORTS EM MILISEGUNDOS

Todas as mensagens de log que continham uma indicação do tempo que determinado processo levou para ser
executado, foram alteradas para exibir o tempo em milisegundos.

Por exemplo, a mensagem:

"Completed in 0.10000 (4 reqs/sec) | Rendering: 0.04000 (40%) | DB: 0.00400 (4%) | 200 OK"

Agora será exibida da seguinte forma:

"Completed in 100ms (View: 40, DB: 4) | 200 OK"

71
Ruby on Rails 2.2 - O que há de novo?

OPÇÃO :CONFIRM NO MÉTODO IMAGE_SUBMIT_TAG

A opção :confirm, muito utilizada em helpers como o link_to, agora também está disponível para o método
image_submit_tag.

Esta opção faz com que uma caixa de confirmação, com uma pergunta personalizada, seja exibida ao se clicar na
imagem. Se o usuário aceitar, o formulário é enviado normalmente, caso contrário nada acontece.

image_submit_tag("delete.gif", :confirm => "Are you sure?")


# => <input type="image" src="/images/delete.gif"
# onclick="return confirm('Are you sure?');"/>

COOKIE DE SESSÃO AGORA É HTTP ONLY

Ao se criar um cookie existe uma opção esquecida por muita gente. A opção http_only faz com que o cookie
somente seja acessível via HTTP, impedindo que um trecho de código em javascript consiga acessá-lo. O valor padrão
para esta opção é false.

No Rails 2.2 o cookie que armazena a sessão do usuário terá a opção http_only ligada por padrão. A intenção é
aumentar a segurança em nossos projetos. Obviamente esta opção pode ser desligada caso necessário. Se este for o
caso, basta incluir a seguinte linha no ApplicationController ou em um controller especifico:

session :session_http_only => false

CAMINHO COMPLETO DA VIEW NO CASO DE UMA EXCEÇÃO

Quando uma exceção é dispara recebemos em nossa view uma mensagem semelhante a esta:

72
Capitulo 4: ActionPack

NoMethodError in Administration/groups#show
Showing app/views//_list.erb where line ...

Quando na verdade deveríamos receber uma mensagem com o caminho completo do arquivo que disparou o erro,
assim:

NoMethodError in Administration/groups#show
Showing app/views/administration/reports/_list.erb where line ...

Este problema já está corrigido nesta nova versão do Rails, facilitando nosso trabalho.

OPÇÕES NO MÉTODO FIELD_SET_TAG

Foi adicionado ao método field_set_tag um parâmetro opcional para facilitar a formatação do HTML. Este
parâmetro aceita todas as opções que o método tag já aceita. Exemplo:

<% field_set_tag nil, :class => 'format' do %>


<p>Some text</p>
<% end %>

O código acima retornará o seguinte:

<fieldset class="format">
<p>Some text</p>
</fieldset>

73
Ruby on Rails 2.2 - O que há de novo?

SUPORTE A XHTML NO ATOM_FEED

O helper atom_feed agora possui um builder interno que permite a criação de XHTML simplesmente acrescentando
:type=>"xhtml" em qualquer elemento content, rights, title, subtitle ou summary. Assim:

entry.summary(:type => 'xhtml') do |xhtml|


xhtml.p "A XHTML summary"
xhtml.p post.body
end

Veja como este bloco se encaixa dentro do atom_feed:

atom_feed do |feed|
feed.title("My great blog!")
feed.updated((@posts.first.created_at))

for post in @posts


feed.entry(post) do |entry|
entry.title(post.title)

entry.summary(:type => 'xhtml') do |xhtml|


xhtml.p "A XHTML summary"
xhtml.p post.body
end

entry.author do |author|
author.name("DHH")
end
end
end
end

74
Capitulo 4: ActionPack

Desta forma o builder interno do atom_feed irá incluir o XHTML gerado dentro de uma tag DIV.

Claro que ainda podemos fazer da forma antiga passando todo o HTML dentro de uma string, mas desta forma nosso
código fica mais limpo.

EVITANDO ATAQUES RESPONSE SPLITTING

Até agora no Rails as URLs passadas para o método redirect_to não passavam por um processo de santization. Isto
era perigoso, pois abria brechas para que pessoas mal intencionadas pudessem realizar ataques do tipo response
splitting e header injection em sua aplicação.

Um exemplo desta vulnerabilidade é quando seu aplicativo recebe uma URL via query string e redireciona seu usuário
através do método redirect_to para esta URL. Através desta brecha de segurança pessoas mal intencionadas podem
gravar cookies na máquina e forjar falsos responses para seus usuários se o seu projeto usar estes parâmetros para
construir cabeçalhos HTTP.

Para evitar este tipo de problema o Rails foi atualizado para limpar (sanitize) todas as URLs passadas para o método
redirect_to. Mas isto não significa que não precisamos nos preocupar mais com este problema, é sempre bom ficar
atento.

UMA MANEIRA MELHOR DE INTERCEPTAR ERROS

A pior coisa que pode acontecer em nossa aplicação é uma horrível página com uma mensagem de erro. Por este
motivo é sempre bom se preparar para estes casos. Agora podemos facilmente mostrar uma página de erro
personalizada para exceções lançadas quando estamos roteando uma requisição, usando o método rescue_from. Veja
um exemplo:

75
Ruby on Rails 2.2 - O que há de novo?

class ApplicationController < ActionController::Base


rescue_from User::NotAuthorized, :with => :deny_access
rescue_from ActiveRecord::RecordInvalid, :with => :show_errors

protected
def deny_access

end

def show_errors(exception)
exception.record.new_record? ? …
end
end

A adição de ActiveSupport:Rescuable permite qualquer classe de fazer mixin da sintaxe rescue_from.

NOVA OPÇÃO :RECURSIVE PARA OS HELPERS JAVASCRIPT_INCLUDE_TAG


E STYLESHEET_LINK_TAG

Os helpers javascript_include_tag e stylesheet_link_tag receberam uma nova opção :recursive que pode ser
usada junto com :all, para que eles possam carregar toda a árvore de arquivos em uma única linha de código. Por
exemplo, caso eu tenha os seguintes arquivos:

public/javascripts/super_calendar/calendar.js
public/stylesheets/super_calendar/calendar.css

Ambos estarão inclusos, mesmo estando em um subdiretório, quando eu executar os métodos da seguinte forma:

javascript_include_tag :all, :recursive => true


stylesheet_link_tag :all, :recursive => true

76
Capitulo 5: ActionMailer

Capitulo 5

ActionMailer

APLICANDO LAYOUTS EM EMAILS

A partir deste release do Rails teremos uma nova funcionalidade que será a alegria dos spammers!

Bincadeiras à parte, assim como temos layouts para controllers, teremos layouts para mailers também. A única
restrição é que devemos colocar o sufixo _mailer no nome do arquivo se quisermos que o layout seja aplicado
automaticamente.

Vamos aos nossos exemplos. Primeiro as views:

<!-- layouts/auto_layout_mailer.html.erb -->


Hello from layout <%= yield %>

77
Ruby on Rails 2.2 - O que há de novo?

<!-- auto_layout_mailer/hello.html.erb -->


Inside

Agora vamos dar uma olhada no ActionMailer:

# auto_layout_mailer.rb
class AutoLayoutMailer < ActionMailer::Base
def hello(recipient)
recipients recipient
subject "You have a mail"
from "tester@example.com"
end
end

Da forma como fizemos qualquer novo mailer fará uso do layout criado.

Para evitar que o mailer use o layout padrão, basta incluir a cláusula :layout => false no corpo do email.

def nolayout(recipient)
recipients recipient
subject "You have a mail"
from "tester@example.com"
body render(:inline => "Hello, <%= @world %>", :layout => false,
:body => { :world => "Earth" })
end

Você também pode informar um outro layout para o mailer, alterando a opção :layout. Neste caso o layout criado
não é obrigado a ter o sufixo ‘_mailer’.

def spam(recipient)
recipients recipient
subject "You have a mail"

78
Capitulo 5: ActionMailer

from "tester@example.com"
body render(:inline => "Hello, <%= @world %>", :layout => 'spam',
:body => { :world => "Earth" })
end

Além disso você também pode informar ao ActionMailer qual layout ele deve usar da seguinte forma:

class ExplicitLayoutMailer < ActionMailer::Base


layout 'spam', :except => [:logout]

79
Ruby on Rails 2.2 - O que há de novo?

Capitulo 6

Railties

ESTÁ CHEGANDO O FIM DOS PLUGINS?

No Rails 2.1, gems passaram a poder ser usadas como plugins em nossos projetos. Para isto bastava criar uma pasta
chamada rails dentro do projeto do gem e incluir um arquivo init.rb.

Isto acrescentou um leque de novidades como config.gem e rake:gems. Mas isto nos faz pensar, "Já que agora eu
posso carregar gems dentro da minha aplicação Rails, seria apenas uma questão de tempo até que plugins deixassem de
existir"?

E parece que isto realmente pode acontecer. Para esta versão do Rails, por exemplo, foi incluída uma alteração que
permite inicializar plugins tanto pelo arquivo init.rb na raiz do plugin, como em um arquivo em um diretório rails/
init.rb (da mesma forma como fazemos com os gems), sendo esta segunda opção a prioritária.

80
Capitulo 6: Railties

Assim, eu poderia por exemplo criar um gem (que funcionaria como um plugin) e instalar de duas maneiras:

./script/plugin install git://github.com/user/plugin.git

sudo gem install user-plugin --source=http://gems.github.com

Isto sem precisar manter dois arquivos init.rb (um na raiz e outro no diretório rails).

SUPORTE AO THIN MELHORADO NO RAILS

O script/server agora verifica a disponibilidade do Thin e o usa. Muito conveniente se vocês estiver usando Thin
no seu ambiente de produção (e quiser rodar o mesmo em desenvolvimento). Você deve acrescentar a seguinte linha
no seu arquivo environment.rb para que isto funcione.

config.gem 'thin'

TESTANDO APENAS ARQUIVOS UNCOMMITTEDS NO GIT

Existe uma tarefa rake no Rails que pouca gente conhece mas que é muito útil:

rake test:uncommitted

Como o nome já diz esta tarefa roda os testes apenas dos arquivos que ainda não foram enviados (commit) para o
subversion, ao invés de rodar todos os testes do projeto.

Eu costumava usar isto muito, mas quando mudei para Git ela não funcionou mais, o suporte era apenas para o SVN.
Mas um patch foi aplicado no Rails garantindo que daqui em diante teremos o mesmo suporte também para o Git.

81
Ruby on Rails 2.2 - O que há de novo?

RAILS.INITIALIZED?

Novo método adicionado ao Rails:

Rails.initialized?

Ele informa se todos os processos de inicialização já foram finalizados.

CACHE_CLASSES AGORA ESTARÁ LIGADO POR PADRÃO

Nos arquivos de configuração do seu projeto provavelmente deve haver uma linha assim:

config.cache_classes = false

Esta linha informa ao Rails que ele não deve fazer cache do código de seu projeto, ou seja, para cada requisição feita
ele carregará o código novamente. Embora isto torne a execução de seu código mais lenta, é ótimo para o ambiente
de desenvolvimento, assim você não precisa ficar recarregando o seu servidor web a cada alteração.

Em produção é importantíssimo que você deixe isto ligado.

Em projetos Rails 2.1, caso a linha acima não se encontre em seus arquivos de configuração, o Rails assumirá que não
deve fazer o cache. Esta era a condição padrão.

No Rails 2.2 isto foi invertido, caso nenhuma configuração seja encontrada ele assumirá que deve fazer o cache. Isto
ajudará os inexperientes que colocam seus projetos em produção sem configurá-lo corretamente.

82
Capitulo 6: Railties

FAZENDO O CONFIG.GEM NÃO CARREGAR O GEM

Uma das novidades introduzidas no Rails 2.1 foi o config.gem, que nos permitia configurar de quais gems nosso
projeto era dependente.

Com isto ganhamos diversas tarefas que facilitaram nosso trabalho, como o rake gems:install, que instala todas as
dependências automaticamente.

Mas tínhamos de tomar alguns cuidados ao configurar as dependências, porque em alguns casos o nome da gem não é
necessariamente o nome da biblioteca que o método require precisa carregar. Por exemplo, a gem aws-s3 deve ser
carregada com o nome aws/s3, trocando o traço por uma barra invertida. Prevendo este tipo de coisa, foi incluído
no config.gem a opção :lib. Veja como ficaria o caso acima:

config.gem 'aws-s3', :lib => 'aws/s3'

Depois de um tempo surgiu um outro problema. Meu projeto depende desta gem, mas eu não quero que ela seja
carregada neste momento. Como fazer?

Até agora nada, mas nesta nova versão podemos configurar a opção :lib como false e isto fará com que o Rails não
carregue a gem.

config.gem 'aws-s3', :lib => false

Mesmo sem carregá-la ela ainda será instalada caso você execute a tarefa rake gems:install e estará incluída em
qualquer outra tarefa relacionada a gems.

83
Ruby on Rails 2.2 - O que há de novo?

THREADSAFE!

Foi incluído no Rails um novo método de configuração para ligar o threaded mode.

config.threadsafe!

Ao executar este método em seu arquivo de configuração (normalmente no environments/production.rb),


você estará fazendo com que as actions de um controller aceitem requisições concorrentes e múltiplas conexões com
o banco de dados. E dependendo da infra-estrutura do seu servidor você poderá receber mais requisições enquanto
mantém menos cópias do Rails em memória.

Ele também desliga o carregamento de dependências após a inicialização. Para mais detalhes sobre isto veja o tópico
"Ligando e desligando carga de dependências" no capítulo de ActiveSupport.

FIXTURES_PATH

As tarefas rake db:fixtures:load e rake db:fixtures:identify receberam um novo parâmetro opcional:


FIXTURES_PATH.

rake db:fixtures:load FIXTURES_PATH=spec/fixtures

Desta forma você pode especificar um caminho alternativo para suas fixtures (por exemplo: spec/fixtures).

RELACIONAMENTOS BELONGS_TO AUTOMATIZADOS

Se você já estiver usando o Rails 2.2, tente executar o seguinte comando para criar um novo modelo:

84
Capitulo 6: Railties

./script/generate scaffold comment author:string body:text post:references

Note que estou informando que meus comentários terão uma referência a tabela posts. Ou em outras palavras que
meus comentários pertencem (belongs_to) a um post. Agora veja o arquivo app/models/comment.rb gerado
pelo script:

class Comment < ActiveRecord::Base


belongs_to :post
end

O relacionamento com a tabela posts já foi incluído automaticamente no modelo. Este é um novo recurso que
encontramos nesta versão.

MENSAGEM DE AJUDA PARA NOVOS NO RAILS

Foi adicionado ao arquivo 500.html do Rails uma mensagem para ajudar os programadores que estão começando com
Rails:

(If you’re the administrator of this website, then please read the log file “development.log” to find out what went
wrong.)

É o Rails se preocupando com a galera que está começando.

DEBUG NO CONSOLE DO RAILS

Da mesma forma como temos o script/server --debugger, agora também temos o script/console --debugger.
Esta opção basicamente carrega a biblioteca ruby-debug ao iniciar o console.

85
Ruby on Rails 2.2 - O que há de novo?

É mais fácil usar esta opção do que executar um require 'ruby-debug' no console toda vez que precisar deste
recurso.

TAREFA DB:MIGRATE:REDO AGORA ACEITA A VERSÃO DA MIGRATION

A tarefa rake db:migrate:redo tem se mostrado muito útil quando precisamos voltar e executar novamente a última
migration criada. Agora esta tarefa ficou ainda mais útil, porque podemos utilizar a opção VERSION e informar qual
migration queremos que seja reexecutada.

rake db:migrate:redo VERSION=20080725004631

SUPORTE À BANCO DE DADOS IBM DB NO GERADOR DO RAILS

Foi adicionado ao generator do Rails a opção IBM DB para o banco de dados. Para usá-la basta executar no terminal o
seguinte comando ao criar o projeto Rails:

rails app_name -d ibm_db

Isto criará seu projeto com o seguinte arquivo database.yml:

# IBM Dataservers
#
# Home Page
# http://rubyforge.org/projects/rubyibm/
#
# To install the ibm_db gem:
# On Linux:
# Source the db2profile file and set the necessary environment variables:

86
Capitulo 6: Railties

#
# . /home/db2inst1/sqllib/db2profile
# export IBM_DB_DIR=/opt/ibm/db2/V9.1
# export IBM_DB_LIB=/opt/ibm/db2/V9.1/lib32
#
# Then issue the command: gem install ibm_db
#
# On Windows:
# Issue the command: gem install ibm_db
# If prompted, select the mswin32 option

development:
adapter: ibm_db
username: db2inst1
password:
database: app_name_dev
#schema: db2inst1
#host: localhost
#port: 50000
#account: my_account
#app_user: my_app_user
#application: my_application
#workstation: my_workstation

test:
adapter: ibm_db
username: db2inst1
password:
database: app_name_tst
#schema: db2inst1
#host: localhost
#port: 50000
#account: my_account
#app_user: my_app_user

87
Ruby on Rails 2.2 - O que há de novo?

#application: my_application
#workstation: my_workstation

production:
adapter: ibm_db
username: db2inst1
password:
database: app_name_prd
#schema: db2inst1
#host: localhost
#port: 50000
#account: my_account
#app_user: my_app_user
#application: my_application
#workstation: my_workstation

88
Capitulo 7: Internacionalização (I18n)

Capitulo 7

Internacionalização (I18n)

Tudo começou em setembro de 2007 quando um grupo de desenvolvedores começou a construção de um plugin para
o Rails chamado rails-I18n, que visava eliminar a necessidade de monkey patching no Rails para internacionalizar uma
aplicação.

O QUE É I18N?

Primeiro, para entender o porque deste nome, precisamos ter um conhecimento profundo de cálculos matemáticos.
Conte comigo, quantas letras temos entre o I e o n na palavra Internationalization? 18. Muito bom, I18n.

O mesmo vale para Localization e L10n.

89
Ruby on Rails 2.2 - O que há de novo?

Já viu quando um site tem aquelas bandeirinhas no topo, permitindo que você escolha em que língua quer navegar?
Quando você clica em uma delas, todos os textos do site mudam para a língua correspondente daquele país. Isto se
chama Internationalization, ou como acabamos de aprender I18n.

Claro que estou sendo muito simplista, na maioria das vezes não é só os textos que mudam de um país para outro.
Não podemos nos esquecer do formato da data, fuso-horário, padrões de peso e medida. E talvez o mais importante a
moeda.

I18N E L10N, QUAL A DIFERENÇA?

Internacionalização é preparar seu software para que pessoas de outros países e línguas possam usá-lo.

Localização (L10n) é adaptar o seu produto as necessidades de um país. É como pegar um site americano que só aceita
pagamento via PayPal e adaptá-lo para aceitar boleto bancário, por exemplo.

O QUE TEM DE NOVO NO RAILS?

No Rails 2.2 este plugin de internacionalização será integrado ao seu código fonte.

Isto não significa que o Rails sofreu grandes alterações. Na verdade quase nada mudou, ele continua sendo o mesmo
framework de sempre, com todas as suas mensagens de validações em inglês e tudo mais.

A diferença é que se quiséssemos estas mensagem em português, ou em outro idioma, teríamos de criar um monkey
patch para isto. Não posso deixar de citar como exemplo o famoso Brazilian Rails, que faz exatamente isto para
traduzir as mensagens do Active Record.

90
Capitulo 7: Internacionalização (I18n)

A novidade é que com o lançamento do Rails 2.2 temos uma forma padronizada e mais simples de fazer isto, usando
uma interface comum.

COMO ISTO FUNCIONA?

Básicamente este gem é dividido em duas partes:

• A primeira acrescenta à API do Rails uma coleção de novos métodos, estes métodos basicamente servirão
para acessar a segunda parte do gem.

• A segunda parte é um backend simples que implementa toda a lógica para fazer o Rails funcionar exatamente
como funcionava antes, usando a localização en-US.

A grande diferença é que esta segunda parte poderá ser substituída por uma que dê suporte à internacionalização que
você deseja. Melhor ainda, uma série de plugins que serão lançados irão fazer exatamente isto.

O alicerce desta implementação é um módulo chamado I18n que vem através do gem incorporado ao ActiveSupport.
Este módulo acrescenta as seguintes funcionalidades ao Rails:

• O método translate, que será usado para retornar traduções.

• O método localize, que usaremos para "traduzir" objetos Date, DateTime e Time para a localização atual.

Além destes métodos este módulo traz todo o código necessário para armazenar e carregar os backends de
"localização". E já vem com um manipulador de exceções padrão que captura exceções levantadas no backend.

91
Ruby on Rails 2.2 - O que há de novo?

Tanto o backend como o manipulador de exceções podem (devem) ser substituídos. Além disso para facilitar, os
métodos #translate e #localize também poderão ser executados usando os métodos #t e #l respectivamente. E
ambos os métodos funcionam em suas views e controllers.

EXEMPLOS PRÁTICOS

A melhor forma de entender como usar este suporte a internacionalização no Rails é vendo seu funcionamento na
prática. Eu aconselho então acessar o projeto criado por Clemens Kofle e outros no endereço: http://i18n-
demo.phusion.nl/.

PLURALIZAÇÃO NA INTERNACIONALIZAÇÃO

Em alguns casos as traduções de certas frases pode depender de um número ou quantidade. Isto é algo muito comum
e o pacote de internacionalização já vem preparado para estes casos.

Como exemplo, vamos pegar o método distance_in_words. Se o tempo for de 1 segundo a frase “1 segundo” no
singular nos serve muito bem, mas se o tempo for maior do que isto a frase deve estar no plural como em “5
segundos”.

No arquivo de localização, podemos internacionalizar frases do nosso aplicativo que dependem de um número ou
quantidade, por exemplo:

datetime:
distance_in_words:
x_seconds:
one: "1 segundo"
other: "{{count}} segundos"

92
Capitulo 7: Internacionalização (I18n)

Este é um recurso valioso que é usado por vários métodos nativos do Rails e pode ser usado em seus próprios
métodos.

93
Ruby on Rails 2.2 - O que há de novo?

Capitulo 8

Performance

MELHORANDO A PERFORMANCE DO RAILS

Jeremy Kemper andou trabalhando em melhorias de performance no Rails.Uma das coisas que ele andou melhorando
foi o Erb, tornando-o mais rápido. Além disso ele tem atacado alguns métodos especiais como o concat e capture
que são usados por muitos helpers do Rails.

Jeremy também atacou o processo de inicialização de partials e otimizou diversos helpers que geravam código em
Javascript.

A classe RecordIdentifier também foi melhorada através do uso de caches. O RecordIdentifier incorpora uma
série de convenções para lidar com registros ActiveRecords e ActiveResources ou praticamente qualquer
outro tipo de modelo que tenha um id.

94
Capitulo 8: Performance

É interessante ver este tipo de ação, o Rails já está ficando grande e pesado demais, e processos de otimização devem
se tornar uma constante daqui para frente.

CRIANDO TESTES DE PERFORMANCE

No Rails 2.2 ganhamos um novo generator para testes de performance.

Ao executar no terminal o seguinte comando:

[carlosbrando:edge]$ ./script/generate performance_test Login


exists test/performance/
create test/performance/login_test.rb

Será criado um arquivo chamado test/performance/login_test.rb. Veja o código gerado:

require 'performance/test_helper'

class LoginTest < ActionController::PerformanceTest


# Replace this with your real tests.
def test_homepage
get '/'
end
end

Neste arquivo podemos colocar todos os testes que desejarmos e ao executá-lo teremos informações sobre cada um
dos testes, como tempo de processamento, uso de memória e outros. Para realizar o teste executamos no terminal:

[carlosbrando:edge]$ ruby test/performance/login_test.rb


Loaded suite test/performance/login_test
Started

95
Ruby on Rails 2.2 - O que há de novo?

LoginTest#test_homepage (32 ms warmup)


process_time: 11 ms
memory: unsupported
objects: unsupported
.
Finished in 0.870842 seconds.

96
Capitulo 9: Bugs e Correções

Capitulo 9

Bugs e Correções

ACTIVERECORD

Correção de uma colisão entre named_scope e :joins.

Quando se usava with_scope junto com :joins todos os atributos da tabelas secundárias eram adicionados ao
modelo da tabela principal.

Método find_all falhando no named_scope

Quando você executava o método find_all em um named_scope o método não estava sendo direcionado para o
proxy_found conforme o esperado. Isto fazia com que um erro NoMethodError fosse retornado.

97
Ruby on Rails 2.2 - O que há de novo?

Topic.base.find_all(&:approved)
# => NoMethodError: undefined method `find_all' for #<Class:0x19a0fb4>

Este problema podia ser contornado usando o método to_a:

Topic.base.to_a.find_all(&:approved)
# => [#<Reply:0x179e720>#<Topic:0x179e388>#<Reply:0x179e20c>]

Nesta versão do Ruby on Rails isto já foi resolvido.

Partial updates não atualizavam o lock_version se nada foi alterado.

Quando usávamos optimistic locking com partial updates, tínhamos queries extras quando na verdade elas não eram
necessárias.

CORREÇÃO NOS MÉTODOS TIME#END_OF_QUARTER E


DATE#END_OF_QUARTER

Nem bem havia saído o Rails 2.1 e já foi encontrado um erro sério. Se você ainda tiver um projeto criado nesta versão
entre no irb e tente rodar isto:

Date.new(2008, 5, 31).end_of_quarter

ERRO!

Por que? A implementação do método end_of_quarter foi feita da maneira errada, ele avança até o último mês do
trimestre e depois pega o último dia. O problema é que ele apenas avança o mês, e como estou partindo do dia 31 de

98
Capitulo 9: Bugs e Correções

maio, ele tentar criar uma nova instância do objeto Date para 31 de junho, que não existe. Com o objeto Time não é
disparado uma exceção, mas ele retorna a data errada: 31 de julho.

Nesta versão este erro já foi corrigido, mas caso você ainda esteja usando a versão 2.1 em algum projeto, muito
cuidado, porque este erro só ocorrerá se usarmos o método end_of_quarter nos dias 31 de maio, julho e agosto.

CORREÇÃO NAS TAREFAS DB:MIGRATE:DOWN E :UP

Quando se usava o comando rake db:migrate:down VERSION=alguma_versão, os registros na tabela


schema_migrations não eram atualizados.

Isto significa que após usar o comando rake db:migrate:down ou up se você rodar o comando rake db:migrate
algumas migrations podem não ser executadas. Vamos simular isto para ficar fácil de entender o problema:

$ ./script/generate migration test_migration


create db/migrate
create db/migrate/20080608082216_test_migration.rb

$ rake db:migrate
(in /Users/erik/projects/railstest)
== 20080608082216 TestMigration: migrating ====================================
-- create_table("foo")
-> 0.0137s
== 20080608082216 TestMigration: migrated (0.0140s) ===========================

$ rake db:migrate:down VERSION=20080608082216


(in /Users/erik/projects/railstest)
== 20080608082216 TestMigration: reverting ====================================
-- drop_table("foo")
-> 0.0139s

99
Ruby on Rails 2.2 - O que há de novo?

== 20080608082216 TestMigration: reverted (0.0144s) ===========================

$ rake db:migrate
(in /Users/erik/projects/railstest)

Este problema foi corrigido ao se certificar de atualizar a tabela schema_migrations após a execução destas
tarefas.

POSTGRESQL

No PostgreSQL, a sintaxe dele de typecast é a seguinte: <column>::<type>

O problema é que quando se usava essa sintaxe, o ActiveRecord achava que o na verdade era um named bind e
reclamava que o valor para ele não estava sendo passado no Hash. Agora este problema está corrigido, permitindo que
façamos algo assim:

:conditions => [':foo::integer', { :foo => 1 }]

SOLUÇÃO DE BUG NO MÉTODO RENAME_COLUMN

Esta alteração trata-se na verdade de uma correção de um bug no método rename_column. Para entender qual era o
problema precisamos de um cenário como exemplo. Primeiro criamos um migration:

create_table "users", :force => true do |t|


t.column :name, :string, :default => ''
end

100
Capitulo 9: Bugs e Correções

Ok, agora criamos um segundo migration onde vamos renomear a coluna name da tabela:

rename_column :users, :name, :first_name

Se você fizer o teste em sua máquina, notará que ao usar o método rename_column a "nova" coluna first_name não
terá mais o valor padrão definido no primeiro migration.

Este bug já está resolvido para esta versão do Rails.

MÉTODO COUNT DO ACTIVERECORD NÃO INCLUI UM ALIAS PARA


ASSOCIAÇÕES

Digamos que temos a seguinte associação has_many :through:

class Author < ActiveRecord::Base


has_many :authorships
has_many :books, :through => :authorships
end

Ao procurar por um livro você pode incluir a autoria em sua busca:

author.books.find(:all, :include => :authorships,


:conditions => ["authorships.primary = ?", true])

Isto funciona muito bem, sem erros. Mas tente fazer o mesmo com o método count:

author.books.count(:include => :authorships,


:conditions => ["authorships.primary = ?", true])

101
Ruby on Rails 2.2 - O que há de novo?

Temos um erro. Isto acontece porque a tabela authorships foi incluída duas vezes na mesma query.

O método find é mais esperto, porque ele cria um apelido para a tabela, coisa que o método count não faz. Eu sei
que o exemplo dado não é muito bom, mas é apenas para tentar mostrar o problema com o método count.

Esta falha foi corrigida. Agora o método count se comporta exatamente como o método find em relação ao
:include.

CORREÇÃO DE BUG NO MÉTODO COUNT DO ACTIVERECORD

Existia um bug no método count do ActiveRecord quando usávamos uma associação has_many em conjunto com a
opção :limit ou :offset. Vejamos um exemplo:

class Post < ActiveRecord::Base


has_many :comments, :limit=> 2
end

No código acima quando tentarmos recuperar os comentários de um post, apenas 2 comentários devem ser
retornados.

post.comments.length # => 2

# Veja o SQL usado:


# SELECT * AS count_all FROM "comments" WHERE
# ("comments".post_id = 1) LIMIT 2

Mas, ao usarmos o método count:

post.comments.count # => 3

102
Capitulo 9: Bugs e Correções

# Veja o SQL usado:


# SELECT count(*) AS count_all FROM "comments" WHERE
# ("comments".post_id = 1)

Como você pode ver o erro ocorria porque a clausula LIMIT 2 não era incluída na query do SQL.

Obviamente isto já foi corrigido e já está funcionando no Rails 2.2.

BUG NO SUBMIT_TAG CORRIGIDO

Quando usávamos o método submit_tag com a opção :disable_with ligada, ele suprimia o parâmetro :commit
quando o formulário era enviado para o servidor. Isto acontecia porque após submeter o formulário, o javascript do
evento onclick primeiro desabilita o botão e só depois envia o formulário ao servidor, e como sabemos campos
desabilitados não são enviados no submit.

Isto representava um problema para os casos onde o formulário possuía mais de um submit_tag e a sua lógica de
atualização/criação dependia do valor do parâmetro :commit para fazer alguma coisa.

Este problema foi corrigido incluindo um código no inicio do javascript que copia o valor deste parâmetro para um
campo hidden no formulário e o envia para o servidor mesmo com o botão desabilitado.

BUG AO TESTAR ROTAS NOMEADAS

Existe um bug bem especifico até a versão 2.1 quando em um teste funcional testamos uma rota nomeada com
parâmetros antes de executar um request. Para entender do que estou falando, veja um exemplo:

103
Ruby on Rails 2.2 - O que há de novo?

def test_something
post_url(:id => 1) # Antes do request isto retornará um erro
get :edit, :id => 1
post_url(:id => 1) # Aqui funciona
end

Este problema já foi corrigido no Rails 2.2.

TIME#ADVANCE RECONHECE FRAÇÕES DE DIAS E SEMANAS

Após o lançamento do Rails 2.1 o método Time#advance parou de funcionar corretamente com frações de tempo
como:

>> Time.stubs(:now).returns Time.local(2000)

>> 1.5.days.ago == 36.hours.ago


# => false

Este erro foi corrigido no Rails 2.2, faça o teste.

ERRO AO CRIAR DOIS CONTROLLERS COM O MESMO NOME

No Rails 2.1 em alguns casos ao se criar dois controllers com o mesmo nome, mas em namespaces diferentes um erro
acontece, veja:

$ rails -v
Rails 2.1.0

104
Capitulo 9: Bugs e Correções

$ ruby -v
ruby 1.8.6 (2008-03-03 patchlevel 114) [universal-darwin9.0]

$ rails test
$ cd test/
$ script/generate scaffold Posts title:string body:text
$ script/generate controller -t Admin::Posts
The name 'Admin::PostsHelper' is either already used in your application or reserved by Ruby on Rails.
Please choose an alternative and run this generator again.
...

Mais um bug corrigido nesta versão.

105
Ruby on Rails 2.2 - O que há de novo?

Capitulo 10

CHANGELOG

ACTIONMAILER

2.2.0 [RC1] (October 24th, 2008)

• Add layout functionality to mailers [Pratik]

Mailer layouts behaves just like controller layouts, except layout names need to have '_mailer' postfix for them to be
automatically picked up.

ACTIONPACK

2.2.0 [RC1] (October 24th, 2008)

106
Capitulo 10: CHANGELOG

• Fix incorrect closing CDATA delimiter and that HTML::Node.parse would blow up on unclosed CDATA
sections [packagethief]

• Added stale? and fresh_when methods to provide a layer of abstraction above request.fresh? and friends
[DHH]. Example:

class ArticlesController < ApplicationController def show_with_respond_to_block

@article = Article.find(params[:id])

if stale?(:last_modified => @article.published_at.utc, :etag => @article)


respond_to do |wants|
# normal response processing
end
end

end

def show_with_implied_render

@article = Article.find(params[:id])

fresh_when(:last_modified => @article.published_at.utc, :etag => @article)

end end

• Added inline builder yield to atom_feed_helper tags where appropriate [Sam Ruby]. Example:

entry.summary :type => 'xhtml' do |xhtml| xhtml.p pluralize(order.lineitems.count, "line item") xhtml.p "Shipped
to #{order.address}" xhtml.p "Paid by #{order.paytype}" end

107
Ruby on Rails 2.2 - O que há de novo?

• Make PrototypeHelper#submit_to_remote a wrapper around PrototypeHelper#button_to_remote. [Tarmo


Tänav]

• Set HttpOnly for the cookie session store's cookie. #1046

• Added FormTagHelper#image_submit_tag confirm option #784 [Alastair Brunton]

• Fixed FormTagHelper#submit_tag with :disable_with option wouldn't submit the button's value when was
clicked #633 [Jose Fernandez]

• Stopped logging template compiles as it only clogs up the log [DHH]

• Changed the X-Runtime header to report in milliseconds [DHH]

• Changed BenchmarkHelper#benchmark to report in milliseconds [DHH]

• Changed logging format to be millisecond based and skip misleading stats [DHH]. Went from:

Completed in 0.10000 (4 reqs/sec) | Rendering: 0.04000 (40%) | DB: 0.00400 (4%) | 200 OK
[http://example.com]

...to:

Completed in 100ms (View: 40, DB: 4) | 200 OK [http://example.com]

• Add support for shallow nesting of routes. #838 [S. Brent Faulkner]

Example :

108
Capitulo 10: CHANGELOG

map.resources :users, :shallow => true do |user|

user.resources :posts

end

• GET /users/1/posts (maps to PostsController#index action as usual) named route "user_posts" is added as
usual.

• GET /posts/2 (maps to PostsController#show action as if it were not nested) Additionally, named route
"post" is added too.

• Added button_to_remote helper. #3641 [Donald Piret, Tarmo Tänav]

• Deprecate render_component. Please use render_component plugin from http://github.com/rails/


render_component/tree/master [Pratik]

• Routes may be restricted to lists of HTTP methods instead of a single method or :any. #407 [Brennan Dunn,
Gaius Centus Novus] map.resource :posts, :collection => { :search => [:get, :post] } map.session 'session',
:requirements => { :method => [:get, :post, :delete] }

• Deprecated implicit local assignments when rendering partials [Josh Peek]

• Introduce current_cycle helper method to return the current value without bumping the cycle. #417 [Ken
Collins]

• Allow polymorphic_url helper to take url options. #880 [Tarmo Tänav]

• Switched integration test runner to use Rack processor instead of CGI [Josh Peek]

109
Ruby on Rails 2.2 - O que há de novo?

• Made AbstractRequest.if_modified_sense return nil if the header could not be parsed [Jamis Buck]

• Added back ActionController::Base.allow_concurrency flag [Josh Peek]

• AbstractRequest.relative_url_root is no longer automatically configured by a HTTP header. It can now be set


in your configuration environment with config.action_controller.relative_url_root [Josh Peek]

• Update Prototype to 1.6.0.2 #599 [Patrick Joyce]

• Conditional GET utility methods. [Jeremy Kemper] response.last_modified = @post.updated_at


response.etag = [:admin, @post, current_user]

if request.fresh?(response) head :not_modified else # render ... end

• All 2xx requests are considered successful [Josh Peek]

• Fixed that AssetTagHelper#compute_public_path shouldn't cache the asset_host along with the source or
per-request proc's won't run [DHH]

• Removed config.action_view.cache_template_loading, use config.cache_classes instead [Josh Peek]

• Get buffer for fragment cache from template's @output_buffer [Josh Peek]

• Set config.action_view.warn_cache_misses = true to receive a warning if you perform an action that results
in an expensive disk operation that could be cached [Josh Peek]

• Refactor template preloading. New abstractions include Renderable mixins and a refactored Template class
[Josh Peek]

110
Capitulo 10: CHANGELOG

• Changed ActionView::TemplateHandler#render API method signature to render(template, local_assigns = {})


[Josh Peek]

• Changed PrototypeHelper#submit_to_remote to PrototypeHelper#button_to_remote to stay consistent


with link_to_remote (submit_to_remote still works as an alias) #8994 [clemens]

• Add :recursive option to javascript_include_tag and stylesheet_link_tag to be used along with :all. #480
[Damian Janowski]

• Allow users to disable the use of the Accept header [Michael Koziarski]

The accept header is poorly implemented by browsers and causes strange errors when used on public sites
where crawlers make requests too. You can use formatted urls (e.g. /people/1.xml) to support API clients in
a much simpler way.

To disable the header you need to set:

config.action_controller.use_accept_header = false

• Do not stat template files in production mode before rendering. You will no longer be able to modify
templates in production mode without restarting the server [Josh Peek]

• Deprecated TemplateHandler line offset [Josh Peek]

• Allow caches_action to accept cache store options. #416. [José Valim]. Example:

caches_action :index, :redirected, :if => Proc.new { |c| !c.request.format.json? }, :expires_in => 1.hour

• Remove define_javascript_functions, javascript_include_tag and friends are far superior. [Michael Koziarski]

111
Ruby on Rails 2.2 - O que há de novo?

• Deprecate :use_full_path render option. The supplying the option no longer has an effect [Josh Peek]

• Add :as option to render a collection of partials with a custom local variable name. #509 [Simon Jefford,
Pratik Naik]

render :partial => 'other_people', :collection => @people, :as => :person

This will let you access objects of @people as 'person' local variable inside 'other_people' partial template.

• time_zone_select: support for regexp matching of priority zones. Resolves #195 [Ernie Miller]

• Made ActionView::Base#render_file private [Josh Peek]

• Refactor and simplify the implementation of assert_redirected_to. Arguments are now normalised relative to
the controller being tested, not the root of the application. [Michael Koziarski]

This could cause some erroneous test failures if you were redirecting between controllers in different namespaces and
wrote your assertions relative to the root of the application.

• Remove follow_redirect from controller functional tests.

If you want to follow redirects you can use integration tests. The functional test version was only useful if you were
using redirect_to :id=>...

• Fix polymorphic_url with singleton resources. #461 [Tammer Saleh]

• Replaced TemplateFinder abstraction with ViewLoadPaths [Josh Peek]

• Added block-call style to link_to [Sam Stephenson/DHH]. Example:

112
Capitulo 10: CHANGELOG

<% link_to(@profile) do %> <%= @profile.name %> -- Check it out!! <% end %>

• Performance: integration test benchmarking and profiling. [Jeremy Kemper]

• Make caching more aware of mime types. Ensure request format is not considered while expiring cache.
[Jonathan del Strother]

• Drop ActionController::Base.allow_concurrency flag [Josh Peek]

• More efficient concat and capture helpers. Remove ActionView::Base.erb_variable. [Jeremy Kemper]

• Added page.reload functionality. Resolves #277. [Sean Huber]

• Fixed Request#remote_ip to only raise hell if the HTTP_CLIENT_IP and HTTP_X_FORWARDED_FOR


doesn't match (not just if they're both present) [Mark Imbriaco, Bradford Folkens]

• Allow caches_action to accept a layout option [José Valim]

• Added Rack processor [Ezra Zygmuntowicz, Josh Peek]

ACTIVERECORD

2.2.0 [RC1] (October 24th, 2008)

• Skip collection ids reader optimization if using :finder_sql [Jeremy Kemper]

• Add Model#delete instance method, similar to Model.delete class method. #1086 [Hongli Lai]

113
Ruby on Rails 2.2 - O que há de novo?

• MySQL: cope with quirky default values for not-null text columns. #1043 [Frederick Cheung]

• Multiparameter attributes skip time zone conversion for time-only columns [#1030 state:resolved] [Geoff
Buesing]

• Base.skip_time_zone_conversion_for_attributes uses class_inheritable_accessor, so that subclasses don't


overwrite Base [#346 state:resolved] [miloops]

• Added find_last_by dynamic finder #762 [miloops]

• Internal API: configurable association options and build_association method for reflections so plugins may
extend and override. #985 [Hongli Lai]

• Changed benchmarks to be reported in milliseconds [DHH]

• Connection pooling. #936 [Nick Sieger]

• Merge scoped :joins together instead of overwriting them. May expose scoping bugs in your code! #501
[Andrew White]

• before_save, before_validation and before_destroy callbacks that return false will now ROLLBACK the
transaction. Previously this would have been committed before the processing was aborted. #891 [Xavier
Noria]

• Transactional migrations for databases which support them. #834 [divoxx, Adam Wiggins, Tarmo Tänav]

• Set config.active_record.timestamped_migrations = false to have migrations with numeric prefix instead of


UTC timestamp. #446. [Andrew Stone, Nik Wakelin]

114
Capitulo 10: CHANGELOG

• change_column_default preserves the not-null constraint. #617 [Tarmo Tänav]

• Fixed that create database statements would always include "DEFAULT NULL" (Nick Sieger) [#334]

• Add :tokenizer option to validates_length_of to specify how to split up the attribute string. #507. [David
Lowenfels] Example :

# Ensure essay contains at least 100 words. validates_length_of :essay, :minimum => 100, :too_short => "Your essay
must be at least %d words."), :tokenizer => lambda {|str| str.scan(/\w+/) }

• Allow conditions on multiple tables to be specified using hash. [Pratik Naik]. Example:

User.all :joins => :items, :conditions => { :age => 10, :items => { :color => 'black' } } Item.first :conditions => { :items
=> { :color => 'red' } }

• Always treat integer :limit as byte length. #420 [Tarmo Tänav]

• Partial updates don't update lock_version if nothing changed. #426 [Daniel Morrison]

• Fix column collision with named_scope and :joins. #46 [Duncan Beevers, Mark Catley]

• db:migrate:down and :up update schema_migrations. #369 [Michael Raidel, RaceCondition]

• PostgreSQL: support :conditions => [':foo::integer', { :foo => 1 }] without treating the ::integer typecast as a
bind variable. [Tarmo Tänav]

• MySQL: rename_column preserves column defaults. #466 [Diego Algorta]

• Add :from option to calculations. #397 [Ben Munat]

115
Ruby on Rails 2.2 - O que há de novo?

• Add :validate option to associations to enable/disable the automatic validation of associated models. Resolves
#301. [Jan De Poorter]

• PostgreSQL: use 'INSERT ... RETURNING id' for 8.2 and later. [Jeremy Kemper]

• Added SQL escaping for :limit and :offset in MySQL [Jonathan Wiess]

ACTIVERESOURCE

2.2.0 [RC1] (October 24th, 2008)

• Add ActiveResource::Base#to_xml and ActiveResource::Base#to_json. #1011 [Rasik Pandey, Cody Fauser]

• Add ActiveResource::Base.find(:last). [#754 state:resolved] (Adrian Mugnolo)

• Fixed problems with the logger used if the logging string included %'s [#840 state:resolved] (Jamis Buck)

• Fixed Base#exists? to check status code as integer [#299 state:resolved] (Wes Oldenbeuving)

ACTIVESUPPORT

2.2.0 [RC1] (October 24th, 2008)

• TimeWithZone#freeze: preload instance variables so that we can actually freeze [Geoff Buesing]

• Fix Brasilia timezone #1180 [Marcus Derencius, Kane]

116
Capitulo 10: CHANGELOG

• Time#advance recognizes fractional days and weeks. Deprecate Durations of fractional months and years
#970 [Tom Lea]

• Add ActiveSupport::Rescuable module abstracting ActionController::Base rescue_from features. [Norbert


Crombach, Pratik]

• Switch from String#chars to String#mb_chars for the unicode proxy. [Manfred Stienstra]

This helps with 1.8.7 compatibility and also improves performance for some operations by reducing indirection.

• TimeWithZone #wday, #yday and #to_date avoid trip through #method_missing [Geoff Buesing]

• Added Time, Date, DateTime and TimeWithZone #past?, #future? and #today? #720 [Clemens Kofler, Geoff
Buesing]

• Fixed Sri Jayawardenepura time zone to map to Asia/Colombo [Jamis Buck]

• Added Inflector#parameterize for easy slug generation ("Donald E. Knuth".parameterize => "donald-e-
knuth") #713 [Matt Darby]

• Changed cache benchmarking to be reported in milliseconds [DHH]

• Fix Ruby's Time marshaling bug in pre-1.9 versions of Ruby: utc instances are now correctly unmarshaled
with a utc zone instead of the system local zone [#900 state:resolved] [Luca Guidi, Geoff Buesing]

• Add Array#in_groups which splits or iterates over the array in specified number of groups. #579. [Adrian
Mugnolo] Example:

117
Ruby on Rails 2.2 - O que há de novo?

a = (1..10).toa a.ingroups(3) #=> [[1, 2, 3, 4], [5, 6, 7, nil], [8, 9, 10, nil]] a.in_groups(3, false) #=> [[1, 2, 3, 4], [5, 6, 7],
[8, 9, 10]]

• Fix TimeWithZone unmarshaling: coerce unmarshaled Time instances to utc, because Ruby's marshaling of
Time instances doesn't respect the zone [Geoff Buesing]

• Added Memoizable mixin for caching simple lazy loaded attributes [Josh Peek]

• Move the test related core_ext stuff out of core_ext so it's only loaded by the test helpers. [Michael
Koziarski]

• Add Inflection rules for String#humanize. #535 [dcmanges]

ActiveSupport::Inflector.inflections do |inflect|

inflect.human(/_cnt$/i, '_count')

end

'jargon_cnt'.humanize # => 'Jargon count'

• TimeWithZone: when crossing DST boundary, treat Durations of days, months or years as variable-length,
and all other values as absolute length. A time + 24.hours will advance exactly 24 hours, but a time + 1.day
will advance 23-25 hours, depending on the day. Ensure consistent behavior across all advancing methods
[Geoff Buesing]

• Added TimeZone #=~, to support matching zones by regex in time_zone_select. #195 [Ernie Miller]

• Added Array#second through Array#tenth as aliases for Array#[1] through Array#[9] [DHH]

118
Capitulo 10: CHANGELOG

• Added test/do declaration style testing to ActiveSupport::TestCase [DHH via Jay Fields]

• Added Object#present? which is equivalent to !Object#blank? [DHH]

• Added Enumberable#many? to encapsulate collection.size > 1 [DHH/Damian Janowski]

• Add more standard Hash methods to ActiveSupport::OrderedHash [Steve Purcell]

• Namespace Inflector, Dependencies, OrderedOptions, and TimeZone under ActiveSupport [Josh Peek]

• Added StringInquirer for doing things like StringInquirer.new("production").production? # => true and
StringInquirer.new("production").development? # => false [DHH]

• Fixed Date#end_of_quarter to not blow up on May 31st #289 state:resolved

RAILTIES

2.2.0 [RC1] (October 24th, 2008)

• Fixed that sqlite would report "db/development.sqlite3 already exists" whether true or not on db:create
#614 [Antonio Cangiano]

• Added config.threadsafe! to toggle allow concurrency settings and disable the dependency loader [Josh Peek]

• Turn cache_classes on by default [Josh Peek]

• Added configurable eager load paths. Defaults to app/models, app/controllers, and app/helpers [Josh Peek]

119
Ruby on Rails 2.2 - O que há de novo?

• Introduce simple internationalization support. [Ruby i18n team]

• Make script/plugin install -r option work with git based plugins. #257. [Tim Pope Jakub Kuźma]. Example:

script/plugin install git://github.com/mislav/will_paginate.git -r agnostic # Installs 'agnostic' branch script/plugin install


git://github.com/dchelimsky/rspec.git -r 'tag 1.1.4'

• Added Rails.initialized? flag [Josh Peek]

• Make rake test:uncommitted work with Git. [Tim Pope]

• Added Thin support to script/server. #488 [Bob Klosinski]

• Fix script/about in production mode. #370 [Cheah Chu Yeow, Xavier Noria, David Krmpotic]

• Add the gem load paths before the framework is loaded, so certain gems like RedCloth and BlueCloth can be
frozen.

• Fix discrepancies with loading rails/init.rb from gems.

• Plugins check for the gem init path (rails/init.rb) before the standard plugin init path (init.rb) [Jacek Becela]

• Changed all generated tests to use the test/do declaration style [DHH]

• Wrapped Rails.env in StringInquirer so you can do Rails.env.development? [DHH]

• Fixed that RailsInfoController wasn't considering all requests local in development mode (Edgard Castro)
[#310 state:resolved]

120

Você também pode gostar