Utilizando o named_scope no ActiveRecord do Ruby on Rails 2.1


Leia em 1 minuto

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!