A próxima versão do Rails contará com um novo método chamado touch em todos os objetos do ActiveRecord, o método basicamente serve para atualizar os campos updated_at e updated_on do registro atual com a data e hora corrente. Veja abaixo a implementação do método:
current_time = current_time_from_proper_timezone
if attribute
write_attribute(attribute, current_time)
else
write_attribute('updated_at', current_time) if respond_to?(:updated_at)
write_attribute('updated_on', current_time) if respond_to?(:updated_on)
end
save!
end
Como você pode ver na implementação acima ele aceita um parâmetro attribute. Utilizaremos este parâmetro para informar um campo diferente dos mencionados acima quando for necessário.
product.touch # atualiza o campo updated_at
product.touch(:designed_at) # atualiza o campo designed_at
Esta atualização vale tanto para o Rails 3 como para uma futura provável versão do Rails 2.3.
Graças a este novo método também ganhamos um novo atributo para associações belongs_to, fazendo com que ao criar, atualizar ou apagar um registro filho o método touch do registro pai seja executado. Por exemplo, veja o código abaixo:
end
A partir do exemplo, quando cadastrarmos ou atualizarmos um animalzinho de estimação (pet), os campos updated_at/on do dono (owner) correspondente serão automaticamente atualizado com a data atual.
O lançamento do Ruby on Rails 2.3 está muito próximo e eu ainda não comentei sobre algumas novidades por aqui. A idéia desta série não é apenas mostrar as novas funcionalidades, mas explicar e dar exemplos de como elas funcionam na prática, mas alguns dos novos recursos são muito simples e não exigem muitos detalhes. Vejamos alguns relacionados ao Active Record.
Os callbacks do Active Record receberam uma nova implementação que permite o uso combinado das opções :if e :unless no mesmo callback. Você também pode usar um array para definir múltiplas condições:
before_save :update_credit_rating,
:if => :active,
:unless => [:admin, :cash_only]
O método find também ganhou uma nova opção :having para filtrar registros agrupados:
Developer.find(:all,
:group => "salary",
:having => "sum(salary) > 10000",
:select => "salary")
Outra funcionalidade importante que temos de volta é o uso de um hash :conditions em relacionamentos has_many. Isto já funcionava no Rails 2.1, parou de funcionar no Rails 2.2 e está de volta no Rails 2.3.
has_many :orders, :conditions => {:status => 'confirmed'}
Todos os exemplos dados aqui funcionarão somente no Ruby on Rails 2.3 ou superior. Você pode encontrar mais detalhes sobre esta e outras novidades acompanhando a série Rails 2.3.
Outro recurso que também foi muito solicitado e estará disponível com a versão 2.3 do Rails é uma melhoria nas nested transactions (transações aninhadas).
Atualmente no Rails já podemos aninhar transações, ou em um português mais simples podemos colocar uma transação dentro de outra. Veja um exemplo:
User.transaction do
User.create(:username => 'Kotori')
User.transaction do
User.create(:username => 'Nemu')
raise ActiveRecord::Rollback
end
end
User.find(:all) # => empty
Resumindo o exemplo acima: Criei uma transação e inclui um novo usuário no banco, então criei uma segunda transação dentro da primeira e inclui um segundo usuário, e no fim disparei uma exceção que faz com que a primeira transação e todas as suas filhas sejam desfeitas como você pode ver no resultado do método find.
A novidade é a opção :requires_new. Quando usada em uma sub-transação, caso alguma coisa dê errado somente as alterações feitas dai em diante é que serão desfeitas. Veja um novo exemplo, agora com este recurso habilitado:
User.transaction do
User.create(:username => 'Kotori')
User.transaction(:requires_new => true) do
User.create(:username => 'Nemu')
raise ActiveRecord::Rollback
end
end
User.find(:all) # => Returns only Kotori
Como você pode ver no retorno do método find, ao realizar o rollback somente a inclusão do segundo usuário foi desfeita, já que estou usando a opção :requires_new habilitada na segunda transação.
Nem todo banco de dados tem suporte real a nested transactions. Na verdade, até o momento somente o MS-SQL tem esta funcionalidade. Mas não se preocupe se você estiver usando outro banco de dados, o Active Record consegue emular nested transactions usando savepoints.
ATENÇÃO: Caso você esteja usando o MySQL, então não faça operações DDL em nested transactions que estiverem emulando savepoints. Ou seja, não tente executar algo como ‘CREATE TABLE’ nestes blocos. Isto acontece porque o MySQL automaticamente libera todos os savepoints após executar uma operação DDL. Então quando a transação terminar e tentar liberar os savepoints criados, ocorrerá um erro no banco já que todos os savepoints foram liberados antes da hora. Para entender melhor, veja o exemplo abaixo:
# INICIO
Model.connection.transaction do
# CRIA O SAVEPOINT active_record_1
Model.connection.transaction(:requires_new => true) do
# active_record_1 é automaticamente liberado
Model.connection.create_table(...)
end # LIBERA o savepoint active_record_1
# ^^^^ BOOM! database error!
end
Apesar deste pequeno detalhe, em todos os outros casos este recurso tem funcionado perfeitamente.
Todos os exemplos dados aqui funcionarão somente no Ruby on Rails 2.3 ou superior. Você pode encontrar mais detalhes sobre esta e outras novidades acompanhando a série Rails 2.3.
Durante muito tempo a funcionalidade mais requisitada ao core team do Rails era a simplificação do gerenciamento de múltiplos modelos em apenas um formulário. Eu mesmo cheguei a comentar sobre uma nova opção chamada :accessible que facilitaria atribuições em massa em objetos ActiveRecord (aqui e aqui).
Infelizmente este recurso foi incluído ao Rails cedo demais, já que ele só dava suporte a nested models (é como chamamos os modelos que estão “acoplados” a um outro modelo, como quando usamos belongs_to ou has_many) durante a criação dos objetos e por isto ele foi removido afim de ser aprimorado.
No Rails 2.3 esta funcionalidade volta a existir, mas de uma maneira diferente. A primeira coisa que devemos fazer é informar ao modelo que ele se beneficiará deste recurso incluindo uma chamada ao método accept_nested_attributes_for, como no exemplo:
end
Como visto acima estou “ligando” a atribuição em massa para o modelo Task via o modelo Project. Isto também vale para qualquer tipo de relacionamento, como belongs_to, has_one, has_many e has_and_belongs_to_many.
Uma vez feito isto, agora eu posso criar, editar e apagar tarefas (tasks) através do objeto Project:
# Adicionando uma nova tarefa ao projeto
@project.task_attributes = {'new_1' => {:name => 'Task 1' } }
@project.task #=> [ <#Task: name: 'Task 1'> ]
@project.task.clear
# Adicionando duas tarefas ao projeto
@project.task_attributes =
{'new_1' => {:name => 'Task 1' }, 'new_2' => {:name => 'Task 2' } }
@project.save
@project.task #=> [ <#Task: name: 'Task 1'>, <#Task: name: 'Task 2'> ]
# Alterando a primeira tarefa (assumindo o id == 1)
@project.task_attributes = {'1' => {:name => 'My Task' } }
@project.save
# Alterando a segunda tarefa (id == 2) e incluindo uma nova
@project.task_attributes = { '2' => {:name => 'My Second Task' },
'new_1' => {:name => 'Task 3' } }
@project.save
# Apaga o último registro (id == 3)
@project.task_attributes = {'3' => {'_delete' => '1' } }
@project.save
Talvez neste momento você esteja se questionando sobre estes formatos estranhos, como ao apagar um registro. Sim, estes hashs são meio confusos mesmo, mas eles não foram criados para serem usados desta maneira. O uso prático deste novo recurso está na criação de formulários:
<!-- PRESTE ATENÇÃO AQUI -->
Ao definir project_form.fields_for :tasks estamos dizendo que aquele trecho do formulário deve usar o recurso de atribuição em massa para criar, editar ou apagar uma tarefa (task) já existente.
Caso uma das validações da classe Task não passe (imagine que esta tenha um validates_presence_of :name e que eu deixei o campo name em branco), a mensagem correspondente a esta validação será copiada para a classe pai, no caso para a classe Project e estará acessível através do método error_messages_for dela.
Este novo sistema também conta com o recurso de transações. Isto significa que ao realizar uma série de operações de uma só vez, se uma der errado, nenhuma delas será efetivada. Lembre-se apenas que como toda transação no Rails, embora no banco de dados nada aconteça, na instancia do seu objeto ele ainda continuará com as alterações marcadas.
O mais importante em tudo isto é que o código em seus controllers continuarão exatamente da mesma forma como já é hoje. Nenhuma alteração é necessária. Seguindo os exemplos acima, veja como ficaria meu controller:
end
Nada mudou correto?
Eloy Duran, o programador responsável por este novo recurso, tem um projeto no GitHub mostrando mais detalhes sobre o seu funcionamento. Recomendo que você dê uma olhada neste formulário em especial, onde ele mostra como incluir múltiplas tarefas no mesmo projeto.
Todos os exemplos dados aqui funcionarão somente no Ruby on Rails 2.3 ou superior. Você pode encontrar mais detalhes sobre esta e outras novidades acompanhando a série Rails 2.3.
No Rails 2.3, ao acessar nossas rotas nomeadas não teremos mais métodos como formatted_post_path ou formatted_new_user_path. Estes métodos dinâmicos foram removidos com o objetivo de melhorar o consumo de memória no Rails.
Mas isto não significa que não teremos mais a funcionalidade de rotas formatadas. Elas continuam existindo, mas devem ser acessadas de outra maneira. Vejamos alguns exemplos comparando a forma atual e a nova forma:
# atual
formatted_post_path(post, :xml)
# novo
post_path(post, :format => :xml)
# atual
formatted_new_user_path(:json)
# novo
new_user_path(:format => :json)
É uma mudança muito pequena, mas que internamente tem um impacto significativo no consumo de memória dos processos Rails, principalmente se você possui muitas rotas em seu projeto.
Caso você esteja em um processo de migração deve atentar a esta alteração.
O Rails 2.3 já está em sua versão RC1 e em poucos dias devemos ter sua versão final lançada, mas esta série continuará expondo algumas das novidades que encontraremos nesta versão.
Todos os exemplos dados aqui funcionarão somente no Ruby on Rails 2.3/3.0 ou superior. Você pode encontrar mais detalhes sobre esta e outras novidades acompanhando a série Edge Rails.
O método render_component já havia sido marcado como deprecated em versões anteriores do Rails, mas na versão 2.3 ele será totalmente removido do framework.
Este método tem a finalidade de fazer com que uma outra action renderize a action em execução. É meio complicado de explicar, então vamos aos famosos exemplos:
end
end
No exemplo acima estou executando a action delegate_action, mas estou fazendo uso do método render_component para que a resposta da minha action seja criada pela action hello_world que está em um outro controller.
Um outro uso mais prático deste recurso, é usá-lo de dentro de uma view:
Neste caso o render_component agiria como se eu estivesse chamando um partial, quando na verdade estou executando uma action em um controller.
Por que um método tão legal está sendo removido do Rails? A resposta é muito simples: ele é muito lento e tem a mania de deixar as coisas um pouco confusas. Partials e filtros podem ser usados para a mesma finalidade e oferecem uma performance muito melhor, além de mais clareza ao código.
Se ainda assim, você desejar usar render_component ou se já está usando e deseja migrar seu projeto, foi criado um plugin que devolve esta funcionalidade ao Rails.
Todos os exemplos dados aqui funcionarão somente no Ruby on Rails 2.3/3.0 ou superior. Você pode encontrar mais detalhes sobre esta e outras novidades acompanhando a série Edge Rails.
A cada nova versão do Rails o método render tem se tornado mais esperto, e no Rails 2.3 isto não será diferente.
Até hoje se nenhuma opção fosse informada ao método ele usaria a opção :file como padrão. Isto mudou já que agora a opção padrão, para quando nenhuma outra for informada, será renderizar uma partial. Além disso, por convenção, o último parâmetro informado ao método sempre será o hash de variáveis locais (local_assigns).
Isto muda um pouco a forma de usar partials. Vejamos alguns exemplos comparando a sintaxe antiga com a nova sintaxe. Primeiro um exemplo simples:
# Antes
render :partial => "account"
# Rails 2.3 em diante
render "account"
Agora um exemplo informando uma variável local:
# Antes
render :partial => "account", :locals => {:account => @buyer }
# Rails 2.3 em diante
render "account", :account => @buyer
No exemplo abaixo a variável @account é uma instancia da classe Account, desta forma o Rails usa o RecordIdentifier para fazer a mágica:
# Antes
render :partial => "accounts/account", :locals => {:account => @account }
# Rails 2.3 em diante
render(@account)
Um último exemplo, onde a variável @posts é um array com instâncias da classe Post. Mais mágica:
# Antes
render :partial => "posts/post", :collection => @posts
# Rails 2.3 em diante
render(@posts)
Caso você esteja migrando seu projeto para o Rails 2.3, tome o cuidado de verificar se não está usando o método render com a opção padrão em algum lugar do seu projeto. Se estiver, será necessário alterar para especificar a opção :file, assim:
render :file => 'greeting'
Como você pode ver, tivemos uma boa diminuição na quantidade de código necessário ao usar partials.
Todos os exemplos dados aqui funcionarão somente no Ruby on Rails 2.3/3.0 ou superior. Você pode encontrar mais detalhes sobre esta e outras novidades acompanhando a série Edge Rails.
Finders dinâmicos não são mais novidades no Ruby on Rails há muito tempo, qualquer um que já tenha trabalhado em um simples projeto Rails, já fez algo assim:
User.find_by_first_name_and_age('Pedro', 20)
Sabemos que o método acima não existe, mas graças aos finders dinâmicos podemos usá-lo para recuperar todos os usuários que tenham como primeiro nome ‘Pedro’ e 20 anos de idade.
Outro recurso, um pouco mais novo, que também tem se mostrado muito útil são os named scopes. Com este recurso podemos definir um escopo como base para uma pesquisa. Por exemplo:
end
Article.published.all :limit => 5
No exemplo acima estou usando o escopo published que ajustará minha pesquisa para que apenas artigos publicados sejam devolvidos, mas acrescentando o método all também posso limitar a quantidade de artigos retornados em cinco. Este tipo de flexibilidade não é possível em finders dinâmicos.
Para resolver esta deficiência foi adicionado um novo recurso ao Rails chamado dynamic scopes (escopos dinâmicos). Imagine esta funcionalidade como uma mistura dos dois conceitos que acabamos de ver.
Agora você poderá criar escopos de forma dinâmica, da mesma forma como já fazíamos através do find_by. Desta forma:
User.scoped_by_first_name_and_age 'Pedro', 20
Mas com a vantagem de poder usar toda a flexibilidade de um escopo:
User.scoped_by_first_name_and_age('Pedro', 20).all(:limit => 5)
Da mesma forma como fazíamos em escopos comuns, também podemos preceder um escopo com outro escopo:
User.scoped_by_first_name('Pedro').scoped_by_age(20)
Enfim, mais um grande ganho à nossa produtividade.
Todos os exemplos dados aqui funcionarão somente no Ruby on Rails 2.3/3.0 ou superior. Você pode encontrar mais detalhes sobre esta e outras novidades acompanhando a série Edge Rails.
O método delegate é util para expor métodos de terceiros em uma classe como se fossem dela. Para aqueles que não conhecem este método, vamos pegar um exemplo:
end
end
Foo.new.hello # => "hello"
Como você pode ver no exemplo acima estou executando o método hello como se ele pertencesse a classe Foo, embora ele realmente pertença a classe Greeter.
Mas o que aconteceria se a associação não existisse e o objeto greeter fosse nulo? Teríamos um erro do tipo NoMethodError ao executar o método hello já que por debaixo dos panos estaríamos executando nil.hello.
Para contornar este tipo de problema foi adicionado ao método delegate a opção :allow_nil que impede o retorno de uma exceção nesses casos.
attr_accessor :bar
@bar = bar
end
delegate :zoo, :to => :bar, :allow_nil => true
end
Foo.new.zoo # returns nil
No exemplo acima mesmo que o objeto bar seja nulo, o método zoo não disparará uma exceção, ele retornará nil.
Todos os exemplos dados aqui funcionarão somente no Ruby on Rails 2.3/3.0 ou superior. Você pode encontrar mais detalhes sobre esta e outras novidades acompanhando a série Edge Rails.
Uma nova opção foi adicionada aos date helpers do Rails. Métodos como o date_select, time_select, datetime_select, select_minute e outros receberam a opção :prompt.
Com esta opção é possível definir um valor padrão como o primeiro elemento da lista. Veja um exemplo:
O código acima devolverá os campos com as seguintes mensagens:

Exemplo com os valores padrão
Também é possível personalizar a mensagem de cada campo:
Com o código acima teremos algo assim:

Exemplo com valores personalizados
Esta é uma opção muito bem-vinda, já que para conseguir o mesmo efeito antes era necessário um truquezinho.
Todos os exemplos dados aqui funcionarão somente no Ruby on Rails 2.3/3.0 ou superior. Você pode encontrar mais detalhes sobre esta e outras novidades acompanhando a série Edge Rails.