Utilizando o named_scope no ActiveRecord do Ruby on Rails 2.1
14/07/08
O Ruby on Rails 2.1 trouxe uma série de novidades em quase todos os módulos do framework. Uma das novidades que mais gostei foi o named_scope, que permite criar buscas personalizadas, sem perder a elegância. Veja como usar todo o poder desta funcionalidade neste artigo.
Exemplos de uso
Imagine que você tenha dois modelos: Band e Genre.
class CreateBands < ActiveRecord::Migration
def self.up
create_table :bands do |t|
t.string :name
t.references :genre
t.timestamps
end
end
def self.down
drop_table :bands
end
end
class CreateGenres < ActiveRecord::Migration
def self.up
create_table :genres do |t|
t.string :name
t.timestamps
end
end
def self.down
drop_table :genres
end
end
Antes do Ruby on Rails 2.1, se você quisesse criar consultas personalizadas poderia criar métodos ou estender as associações. Por exemplo, poderíamos implementar uma busca pelos artistas recentes ou de um gênero.
class Band < ActiveRecord::Base
belongs_to :genre
def self.recent
all :order => "created_at desc"
end
def self.by_genre(genre)
all :conditions => {:genre_id => genre}
end
end
Mas como você faria se quisesse todos os artistas recentes de um gênero?
E se você quisesse adicionar mais uma condição, como a primeira letra do nome do artista?
É quando a coisa se complica!
Se você usar o named_scope, pode escrever o exemplo anterior desta maneira:
class Band < ActiveRecord::Base
belongs_to :genre
named_scope :recent, :order => "created_at desc"
named_scope :by_genre, lambda {|genre| {:conditions => ["genre_id = ?", genre] }}
end
Ambas abordagens funcionam da mesma maneira quando acessadas sozinhas, como Band.recent ou Band.by_genre(params[:genre_id]).
A difença principal do named_scope é que você pode fazer chamadas encadeadas, propagando as opções de consulta. Note que não é preciso seguir nenhuma ordem específica nas chamadas.
Band.recent.by_genre(params[:genre_id])
Band.by_genre(params[:genre_id]).recent
Todas as buscas adicionadas pelo named_scope se comportam como objetos do ActiveRecord. Você pode executar contagens, fazer outras buscas ou agir ativamente nos resultados retornados.
Band.by_genre.first(:conditions => ["name = ?", params[:name]])
Band.recent.reload
Band.recent.count
Band.by_genre(params[:genre_id]).fans.average(:fans_count)
Se você ainda não usa o named_scope, comece a usá-la agora mesmo!
Ele é, sem dúvida, uma das melhores funcionalidades do ActiveRecord.
Não sei como pude viver sem isto até agora!
- Permalink
- Trackback
- Comentários (1)
- Ao som de: Kay Hanley – Mean Streak
Conhecendo as opções de cache do Rails 2.1
14/07/08
O Ruby on Rails 2.1, dentre outras novidades, trouxe suporte nativo a cache, com diversas opções de armazenamento.
O destaque vai para o suporte ao Memcache, de longe uma das opções mais utilizadas.
Todas essas novidades foram adicionadas ao módulo
ActiveSupport::Cache, que você confere neste artigo.
Configurando as opções de cache
Para definir qual o tipo de cache que você quer utilizar, altere a nova configuração cache_store, adicionada aos arquivos de ambiente de sua aplicação no diretório config/environments/*.rb. Você pode escolher entre :memory_store, :file_store, :mem_cache_store e drb.
config.cache_store = :memory_store
config.cache_store = :file_store, '/path/to/cache'
config.cache_store = :mem_cache_store
config.cache_store = :drb_store, "druby://localhost:2250"
config.cache_store = :mem_cache_store, '127.0.0.1:11211', '127.0.0.1:11212', {:namespace => 'myapp'}
A opção padrão de cache é :memory_store, a menos que o diretório tmp/cache exista; neste caso, a opção utilizada será :file_store.
Vale lembrar que se você utilizar uma opção cujo ambiente não está funcionando corretamente, sua aplicação não deixará de funcionar. Este comportamento é perfeito para trabalho em equipe, onde um desenvolvedor pode configurar seu ambiente para utilizar o Memcache enquanto os outros não precisam se importar com isso naquele momento.
Como funciona
Todas as funções de cache estão disponíveis no objeto Rails.cache.
Para gravar qualquer coisa no cache você deve utilizar o método write.
Rails.cache.write('some_identifier', 'some_value')
Você pode passar qualquer valor para ser gravado no cache, incluindo objetos de ActiveRecord.
@user = User.first
Rails.cache.write(@user.cache_key, @user)
O ActiveRecord adiciona um método que gera uma chave única para cada objeto, chamado cache_key.
Este método gera uma chave como "users/1-20080713185825", que você possa acessar e expirar o objeto sempre que precisar.
Por padrão, a chave gerada irá utilizar o nome do modelo, o id do objeto e a data de atualização, disponível através do atributo
updated_at.
Para ler um objeto do cache, você pode utilizar o método read.
Ele recebe um único argumento que identifica o objeto. Caso o objeto não seja encontrado, o valor nil
será retornado.
Rails.cache.read('some_identifier')
Você pode verificar se um item existe no cache com o método exist?.
Rails.cache.exist?('some_identifier')
Você pode estar se perguntando se você precisa utiliza o método exist? juntamente com read e write para acessar um objeto caso ele exista e gravar um novo item caso ele não seja encontrado. Na verdade, você pode utilizar o método fetch que tenta acessar um item no cache e, caso ele não exista, executa o bloco que é passado e faz a gravação em cache automaticamente.
@users = Rails.cache.fetch('users/all') { User.all }
O método fetch pode receber um hash de opções. No momento, a única opção disponível é :expires_in, que permite alterar o tempo de expiração do cache somente para aquele objeto.
@users = Rails.cache.fetch('users/all', :expires_in => 30.minutes) { User.all }
Para o caso de você estar acessando um único objeto, você pode sobrescrever o método cache_key de modo que ele não gere a chave com o a data de atualização.
class User < ActiveRecord::Base
def cache_key
"users/#{id}"
end
end
Assim, você pode acessar os objetos de maneira mais simples. Veja um exemplo de como isso funcionaria em uma action do controller users.
class UsersControllers < ApplicationController
def show
@user = Rails.cache.fetch("users/#{params[:id]}") { User.find(params[:id]) }
end
end
E se você segue a idéia de encapsular toda a lógica nos modelos, existe sempre a opção de cuidar do cache desta maneira.
class User < ActiveRecord::Base
def self.find_recent_users
Rails.cache.fetch('users/recent') { all :order => 'created_at desc', :limit => 10 }
end
end
Você também pode remover qualquer item do cache com o método delete, que retorna true or false.
Rails.cache.delete('users/1')
Finalizando
É isso! Acredito que este artigo cubra as principais funcionalidades de cache que foram adicionadas à versão 2.1. Dúvidas, sugestões ou correções, envie um comentário!
- Permalink
- Trackback
- Comentários (4)
- Ao som de: Kay Hanley – Mean Streak
Mudança no RSpec 1.1.4 remove inclusão automática de módulos
25/06/08
No RSpec 1.1.4, a inclusão de módulos deixou de ser automática. Você só irá perceber esta mudança se está testando algum módulo, como é o caso dos helpers.
Antes, você só precisava fazer algo como isto:
describe ApplicationHelper do
it "should render flash[:notice]" do
flash[:notice] = "Some notice"
flash_messages.should have_tag('p.notice', 'Some notice')
end
end
Nesta nova versão, se você tentar testar o método flash_messages, irá receber uma mensagem de aviso:
Modules will no longer be automatically included in RSpec version 1.1.4. Called from ./spec/helpers/application_helper_spec.rb:6
A solução é fazer a inclusão manual dos módulos, como este exemplo
describe ApplicationHelper do
include ApplicationHelper
it "should render flash[:notice]" do
flash[:notice] = "Some notice"
flash_messages.should have_tag('p.notice', 'Some notice')
end
end
ou utilizar o objeto helper, que possui todos os métodos do módulo especificado em describe
describe ApplicationHelper do
it "should render flash[:notice]" do
flash[:notice] = "Some notice"
helper.flash_messages.should have_tag('p.notice', 'Some notice')
end
end
Em um primeiro momento, eu não tinha gostado nem um pouco desta alteração, mas depois de
ler os motivos, acho que ficou muito melhor. Você teria dúvidas de que está testando um helper se utilizar helper.flash_messages? E se você tivesse um módulo com um método describe?
As respostas já justificam a alteração por si sós.
- Permalink
- Trackback
- Comentários (0)
- Ao som de: Propagandhi – A Public Dis-service Announcement From Shell
